Skip to main content

jvm_assembler/formats/class/writer/
attributes.rs

1use super::ClassWriter;
2use crate::program::*;
3use gaia_binary::BinaryWriter;
4use gaia_types::Result;
5use std::io::Write;
6
7/// Raw exception handler entry
8struct RawExceptionHandler {
9    start_pc: u16,
10    end_pc: u16,
11    handler_pc: u16,
12    catch_type_index: u16,
13}
14
15impl<W: Write> ClassWriter<W> {
16    /// Collects constants from an attribute and adds them to the constant pool
17    pub fn collect_attribute_constants(&mut self, attribute: &JvmAttribute) {
18        match attribute {
19            JvmAttribute::SourceFile { filename } => {
20                self.add_utf8("SourceFile".to_string());
21                self.add_utf8(filename.clone());
22            }
23            JvmAttribute::ConstantValue { value } => {
24                self.add_utf8("ConstantValue".to_string());
25                match value {
26                    JvmConstantPoolEntry::Integer { .. } => {}
27                    JvmConstantPoolEntry::Float { .. } => {}
28                    JvmConstantPoolEntry::Long { .. } => {}
29                    JvmConstantPoolEntry::Double { .. } => {}
30                    JvmConstantPoolEntry::String { value } => {
31                        self.add_string(value.clone());
32                    }
33                    _ => {}
34                }
35            }
36            JvmAttribute::Exceptions { exceptions } => {
37                self.add_utf8("Exceptions".to_string());
38                for exc in exceptions {
39                    self.add_class(exc.clone());
40                }
41            }
42            JvmAttribute::Signature { signature } => {
43                self.add_utf8("Signature".to_string());
44                self.add_utf8(signature.clone());
45            }
46            JvmAttribute::StackMapTable { frames } => {
47                self.add_utf8("StackMapTable".to_string());
48                for frame in frames {
49                    match frame {
50                        JvmStackMapFrame::SameLocals1StackItem { stack, .. }
51                        | JvmStackMapFrame::SameLocals1StackItemExtended { stack, .. } => {
52                            self.collect_verification_type_constants(stack);
53                        }
54                        JvmStackMapFrame::Append { locals, .. } => {
55                            for vt in locals {
56                                self.collect_verification_type_constants(vt);
57                            }
58                        }
59                        JvmStackMapFrame::Full { locals, stack, .. } => {
60                            for vt in locals {
61                                self.collect_verification_type_constants(vt);
62                            }
63                            for vt in stack {
64                                self.collect_verification_type_constants(vt);
65                            }
66                        }
67                        _ => {}
68                    }
69                }
70            }
71            JvmAttribute::InnerClasses { classes } => {
72                self.add_utf8("InnerClasses".to_string());
73                for inner in classes {
74                    self.add_class(inner.inner_class.clone());
75                    if let Some(outer) = &inner.outer_class {
76                        self.add_class(outer.clone());
77                    }
78                    if let Some(name) = &inner.inner_name {
79                        self.add_utf8(name.clone());
80                    }
81                }
82            }
83            JvmAttribute::EnclosingMethod { class_name, method_name, method_descriptor } => {
84                self.add_utf8("EnclosingMethod".to_string());
85                self.add_class(class_name.clone());
86                if let (Some(name), Some(desc)) = (method_name, method_descriptor) {
87                    self.add_name_and_type(name.clone(), desc.clone());
88                }
89            }
90            JvmAttribute::Code { attributes, exception_table, .. } => {
91                self.add_utf8("Code".to_string());
92                for attr in attributes {
93                    self.collect_attribute_constants(attr);
94                }
95                for handler in exception_table {
96                    if let Some(catch_type) = &handler.catch_type {
97                        self.add_class(catch_type.clone());
98                    }
99                }
100            }
101            JvmAttribute::LineNumberTable { .. } => {
102                self.add_utf8("LineNumberTable".to_string());
103            }
104            JvmAttribute::LocalVariableTable { entries } => {
105                self.add_utf8("LocalVariableTable".to_string());
106                for entry in entries {
107                    self.add_utf8(entry.name.clone());
108                    self.add_utf8(entry.descriptor.clone());
109                }
110            }
111            JvmAttribute::BootstrapMethods { methods: _ } => {
112                self.add_utf8("BootstrapMethods".to_string());
113                // Bootstrap method references and arguments are already in the constant pool
114            }
115            JvmAttribute::RuntimeVisibleAnnotations { annotations } => {
116                self.add_utf8("RuntimeVisibleAnnotations".to_string());
117                self.collect_annotation_constants(annotations);
118            }
119            JvmAttribute::RuntimeInvisibleAnnotations { annotations } => {
120                self.add_utf8("RuntimeInvisibleAnnotations".to_string());
121                self.collect_annotation_constants(annotations);
122            }
123            JvmAttribute::MethodParameters { parameters } => {
124                self.add_utf8("MethodParameters".to_string());
125                for param in parameters {
126                    if let Some(name) = &param.name {
127                        self.add_utf8(name.clone());
128                    }
129                }
130            }
131            JvmAttribute::Unknown { name, .. } => {
132                self.add_utf8(name.clone());
133            }
134        }
135    }
136
137    /// Collects constants from annotations
138    pub fn collect_annotation_constants(&mut self, annotations: &[JvmAnnotation]) {
139        for annotation in annotations {
140            self.add_class(annotation.type_name.clone());
141            for element in &annotation.elements {
142                self.add_utf8(element.name.clone());
143                self.collect_annotation_value_constants(&element.value);
144            }
145        }
146    }
147
148    /// Collects constants from annotation values
149    pub fn collect_annotation_value_constants(&mut self, value: &JvmAnnotationValue) {
150        match value {
151            JvmAnnotationValue::String(s) => {
152                self.add_string(s.clone());
153            }
154            JvmAnnotationValue::Enum { class_name, enum_name } => {
155                self.add_class(class_name.clone());
156                self.add_utf8(enum_name.clone());
157            }
158            JvmAnnotationValue::Class(class_name) => {
159                self.add_string(class_name.clone());
160            }
161            JvmAnnotationValue::Annotation(annotation) => {
162                self.add_class(annotation.type_name.clone());
163                for element in &annotation.elements {
164                    self.add_utf8(element.name.clone());
165                    self.collect_annotation_value_constants(&element.value);
166                }
167            }
168            JvmAnnotationValue::Array(values) => {
169                for v in values {
170                    self.collect_annotation_value_constants(v);
171                }
172            }
173            _ => {}
174        }
175    }
176
177    /// Collects constants from a verification type and adds them to the constant pool
178    pub fn collect_verification_type_constants(&mut self, vt: &JvmVerificationType) {
179        if let JvmVerificationType::Object { class_name } = vt {
180            self.add_class(class_name.clone());
181        }
182    }
183
184    /// Writes an attribute to the class file
185    pub fn write_attribute(&mut self, attribute: &JvmAttribute) -> Result<()> {
186        match attribute {
187            JvmAttribute::SourceFile { filename } => {
188                let name_idx = self.add_utf8("SourceFile".to_string());
189                let file_idx = self.add_utf8(filename.clone());
190                self.writer.write_u16(name_idx)?;
191                self.writer.write_u32(2)?;
192                self.writer.write_u16(file_idx)?;
193            }
194            JvmAttribute::ConstantValue { value } => {
195                let name_idx = self.add_utf8("ConstantValue".to_string());
196                let val_idx = match value {
197                    JvmConstantPoolEntry::Integer { value } => self.add_int(*value),
198                    JvmConstantPoolEntry::Float { value } => self.add_float(*value),
199                    JvmConstantPoolEntry::Long { value } => self.add_long(*value),
200                    JvmConstantPoolEntry::Double { value } => self.add_double(*value),
201                    JvmConstantPoolEntry::String { value } => self.add_string(value.clone()),
202                    _ => 0,
203                };
204                self.writer.write_u16(name_idx)?;
205                self.writer.write_u32(2)?;
206                self.writer.write_u16(val_idx)?;
207            }
208            JvmAttribute::Exceptions { exceptions } => {
209                let name_idx = self.add_utf8("Exceptions".to_string());
210                let attr_len = 2 + exceptions.len() * 2;
211                self.writer.write_u16(name_idx)?;
212                self.writer.write_u32(attr_len as u32)?;
213                self.writer.write_u16(exceptions.len() as u16)?;
214                for exc in exceptions {
215                    let exc_idx = self.add_class(exc.clone());
216                    self.writer.write_u16(exc_idx)?;
217                }
218            }
219            JvmAttribute::Signature { signature } => {
220                let name_idx = self.add_utf8("Signature".to_string());
221                let sig_idx = self.add_utf8(signature.clone());
222                self.writer.write_u16(name_idx)?;
223                self.writer.write_u32(2)?;
224                self.writer.write_u16(sig_idx)?;
225            }
226            JvmAttribute::StackMapTable { frames } => {
227                let name_idx = self.add_utf8("StackMapTable".to_string());
228                let mut buf = Vec::new();
229                buf.extend_from_slice(&(frames.len() as u16).to_be_bytes());
230                for frame in frames {
231                    self.write_stack_map_frame_to_buf(frame, &mut buf)?;
232                }
233                self.writer.write_u16(name_idx)?;
234                self.writer.write_u32(buf.len() as u32)?;
235                self.writer.write_all(&buf)?;
236            }
237            JvmAttribute::InnerClasses { classes } => {
238                let name_idx = self.add_utf8("InnerClasses".to_string());
239                let attr_len = 2 + classes.len() * 8;
240                self.writer.write_u16(name_idx)?;
241                self.writer.write_u32(attr_len as u32)?;
242                self.writer.write_u16(classes.len() as u16)?;
243                for inner in classes {
244                    let inner_idx = self.add_class(inner.inner_class.clone());
245                    let outer_idx = if let Some(outer) = &inner.outer_class { self.add_class(outer.clone()) } else { 0 };
246                    let name_idx = if let Some(name) = &inner.inner_name { self.add_utf8(name.clone()) } else { 0 };
247                    self.writer.write_u16(inner_idx)?;
248                    self.writer.write_u16(outer_idx)?;
249                    self.writer.write_u16(name_idx)?;
250                    self.writer.write_u16(inner.access_flags.to_flags())?;
251                }
252            }
253            JvmAttribute::EnclosingMethod { class_name, method_name, method_descriptor } => {
254                let name_idx = self.add_utf8("EnclosingMethod".to_string());
255                let class_idx = self.add_class(class_name.clone());
256                let nt_idx = if let (Some(name), Some(desc)) = (method_name, method_descriptor) {
257                    self.add_name_and_type(name.clone(), desc.clone())
258                }
259                else {
260                    0
261                };
262                self.writer.write_u16(name_idx)?;
263                self.writer.write_u32(4)?;
264                self.writer.write_u16(class_idx)?;
265                self.writer.write_u16(nt_idx)?;
266            }
267            JvmAttribute::BootstrapMethods { methods } => {
268                let name_idx = self.add_utf8("BootstrapMethods".to_string());
269                
270                // Calculate attribute length
271                let mut attr_len = 2; // method count
272                for method in methods {
273                    attr_len += 2; // method_ref
274                    attr_len += 2; // argument count
275                    attr_len += method.arguments.len() * 2; // arguments
276                }
277                
278                self.writer.write_u16(name_idx)?;
279                self.writer.write_u32(attr_len as u32)?;
280                self.writer.write_u16(methods.len() as u16)?;
281                
282                for method in methods {
283                    self.writer.write_u16(method.method_ref)?;
284                    self.writer.write_u16(method.arguments.len() as u16)?;
285                    for arg in &method.arguments {
286                        self.writer.write_u16(*arg)?;
287                    }
288                }
289            }
290            JvmAttribute::RuntimeVisibleAnnotations { annotations } => {
291                let name_idx = self.add_utf8("RuntimeVisibleAnnotations".to_string());
292                let mut buf = Vec::new();
293                buf.extend_from_slice(&annotations.len().to_be_bytes());
294                for annotation in annotations {
295                    self.write_annotation_to_buf(annotation, &mut buf)?;
296                }
297                self.writer.write_u16(name_idx)?;
298                self.writer.write_u32(buf.len() as u32)?;
299                self.writer.write_all(&buf)?;
300            }
301            JvmAttribute::RuntimeInvisibleAnnotations { annotations } => {
302                let name_idx = self.add_utf8("RuntimeInvisibleAnnotations".to_string());
303                let mut buf = Vec::new();
304                buf.extend_from_slice(&annotations.len().to_be_bytes());
305                for annotation in annotations {
306                    self.write_annotation_to_buf(annotation, &mut buf)?;
307                }
308                self.writer.write_u16(name_idx)?;
309                self.writer.write_u32(buf.len() as u32)?;
310                self.writer.write_all(&buf)?;
311            }
312            JvmAttribute::MethodParameters { parameters } => {
313                let name_idx = self.add_utf8("MethodParameters".to_string());
314                let mut buf = Vec::new();
315                buf.extend_from_slice(&parameters.len().to_be_bytes());
316                for param in parameters {
317                    let name_idx = if let Some(name) = &param.name {
318                        self.add_utf8(name.clone())
319                    } else {
320                        0
321                    };
322                    buf.extend_from_slice(&name_idx.to_be_bytes());
323                    buf.extend_from_slice(&param.access_flags.to_be_bytes());
324                }
325                self.writer.write_u16(name_idx)?;
326                self.writer.write_u32(buf.len() as u32)?;
327                self.writer.write_all(&buf)?;
328            }
329            JvmAttribute::Unknown { name, data } => {
330                let name_idx = self.add_utf8(name.clone());
331                self.writer.write_u16(name_idx)?;
332                self.writer.write_u32(data.len() as u32)?;
333                self.writer.write_all(data)?;
334            }
335            _ => {
336                // Code, LineNumberTable, LocalVariableTable 等需要特殊处理或暂不支持
337            }
338        }
339        Ok(())
340    }
341
342    /// Writes a verification type to a buffer
343    pub fn write_verification_type_to_buf(&mut self, vt: &JvmVerificationType, buf: &mut Vec<u8>) -> Result<()> {
344        match vt {
345            JvmVerificationType::Top => buf.push(0),
346            JvmVerificationType::Integer => buf.push(1),
347            JvmVerificationType::Float => buf.push(2),
348            JvmVerificationType::Double => buf.push(3),
349            JvmVerificationType::Long => buf.push(4),
350            JvmVerificationType::Null => buf.push(5),
351            JvmVerificationType::UninitializedThis => buf.push(6),
352            JvmVerificationType::Object { class_name } => {
353                buf.push(7);
354                let class_idx = self.add_class(class_name.clone());
355                buf.extend_from_slice(&class_idx.to_be_bytes());
356            }
357            JvmVerificationType::Uninitialized { offset } => {
358                buf.push(8);
359                buf.extend_from_slice(&offset.to_be_bytes());
360            }
361        }
362        Ok(())
363    }
364
365    /// Writes a stack map frame to a buffer
366    pub fn write_stack_map_frame_to_buf(&mut self, frame: &JvmStackMapFrame, buf: &mut Vec<u8>) -> Result<()> {
367        match frame {
368            JvmStackMapFrame::Same { offset_delta } => {
369                if *offset_delta <= 63 {
370                    buf.push(*offset_delta as u8);
371                }
372                else {
373                    buf.push(251);
374                    buf.extend_from_slice(&offset_delta.to_be_bytes());
375                }
376            }
377            JvmStackMapFrame::SameLocals1StackItem { offset_delta, stack } => {
378                if *offset_delta <= 63 {
379                    buf.push((*offset_delta + 64) as u8);
380                    self.write_verification_type_to_buf(stack, buf)?;
381                }
382                else {
383                    buf.push(247);
384                    buf.extend_from_slice(&offset_delta.to_be_bytes());
385                    self.write_verification_type_to_buf(stack, buf)?;
386                }
387            }
388            JvmStackMapFrame::SameLocals1StackItemExtended { offset_delta, stack } => {
389                buf.push(247);
390                buf.extend_from_slice(&offset_delta.to_be_bytes());
391                self.write_verification_type_to_buf(stack, buf)?;
392            }
393            JvmStackMapFrame::Chop { offset_delta, k } => {
394                buf.push(251 - k);
395                buf.extend_from_slice(&offset_delta.to_be_bytes());
396            }
397            JvmStackMapFrame::SameExtended { offset_delta } => {
398                buf.push(251);
399                buf.extend_from_slice(&offset_delta.to_be_bytes());
400            }
401            JvmStackMapFrame::Append { offset_delta, locals } => {
402                buf.push((251 + locals.len()) as u8);
403                buf.extend_from_slice(&offset_delta.to_be_bytes());
404                for vt in locals {
405                    self.write_verification_type_to_buf(vt, buf)?;
406                }
407            }
408            JvmStackMapFrame::Full { offset_delta, locals, stack } => {
409                buf.push(255);
410                buf.extend_from_slice(&offset_delta.to_be_bytes());
411                buf.extend_from_slice(&(locals.len() as u16).to_be_bytes());
412                for vt in locals {
413                    self.write_verification_type_to_buf(vt, buf)?;
414                }
415                buf.extend_from_slice(&(stack.len() as u16).to_be_bytes());
416                for vt in stack {
417                    self.write_verification_type_to_buf(vt, buf)?;
418                }
419            }
420        }
421        Ok(())
422    }
423
424    /// Writes an annotation to a buffer
425    pub fn write_annotation_to_buf(&mut self, annotation: &JvmAnnotation, buf: &mut Vec<u8>) -> Result<()> {
426        let type_idx = self.add_class(annotation.type_name.clone());
427        buf.extend_from_slice(&type_idx.to_be_bytes());
428        buf.extend_from_slice(&annotation.elements.len().to_be_bytes());
429        for element in &annotation.elements {
430            self.write_annotation_element_to_buf(element, buf)?;
431        }
432        Ok(())
433    }
434
435    /// Writes an annotation element to a buffer
436    pub fn write_annotation_element_to_buf(&mut self, element: &JvmAnnotationElement, buf: &mut Vec<u8>) -> Result<()> {
437        let name_idx = self.add_utf8(element.name.clone());
438        buf.extend_from_slice(&name_idx.to_be_bytes());
439        self.write_annotation_value_to_buf(&element.value, buf)?;
440        Ok(())
441    }
442
443    /// Writes an annotation value to a buffer
444    pub fn write_annotation_value_to_buf(&mut self, value: &JvmAnnotationValue, buf: &mut Vec<u8>) -> Result<()> {
445        match value {
446            JvmAnnotationValue::Byte(b) => {
447                buf.push(6); // CONSTANT_Integer
448                let idx = self.add_int(*b as i32);
449                buf.extend_from_slice(&idx.to_be_bytes());
450            }
451            JvmAnnotationValue::Char(c) => {
452                buf.push(6); // CONSTANT_Integer
453                let idx = self.add_int(*c as i32);
454                buf.extend_from_slice(&idx.to_be_bytes());
455            }
456            JvmAnnotationValue::Double(d) => {
457                buf.push(5); // CONSTANT_Double
458                let idx = self.add_double(*d);
459                buf.extend_from_slice(&idx.to_be_bytes());
460            }
461            JvmAnnotationValue::Float(f) => {
462                buf.push(4); // CONSTANT_Float
463                let idx = self.add_float(*f);
464                buf.extend_from_slice(&idx.to_be_bytes());
465            }
466            JvmAnnotationValue::Int(i) => {
467                buf.push(6); // CONSTANT_Integer
468                let idx = self.add_int(*i);
469                buf.extend_from_slice(&idx.to_be_bytes());
470            }
471            JvmAnnotationValue::Long(l) => {
472                buf.push(5); // CONSTANT_Long
473                let idx = self.add_long(*l);
474                buf.extend_from_slice(&idx.to_be_bytes());
475            }
476            JvmAnnotationValue::Short(s) => {
477                buf.push(6); // CONSTANT_Integer
478                let idx = self.add_int(*s as i32);
479                buf.extend_from_slice(&idx.to_be_bytes());
480            }
481            JvmAnnotationValue::Boolean(b) => {
482                buf.push(6); // CONSTANT_Integer
483                let idx = self.add_int(if *b { 1 } else { 0 });
484                buf.extend_from_slice(&idx.to_be_bytes());
485            }
486            JvmAnnotationValue::String(s) => {
487                buf.push(8); // CONSTANT_String
488                let idx = self.add_string(s.clone());
489                buf.extend_from_slice(&idx.to_be_bytes());
490            }
491            JvmAnnotationValue::Enum { class_name, enum_name } => {
492                buf.push(10); // CONSTANT_Enum
493                let class_idx = self.add_class(class_name.clone());
494                let nt_idx = self.add_name_and_type(enum_name.clone(), "".to_string());
495                buf.extend_from_slice(&class_idx.to_be_bytes());
496                buf.extend_from_slice(&nt_idx.to_be_bytes());
497            }
498            JvmAnnotationValue::Class(class_name) => {
499                buf.push(9); // CONSTANT_Class
500                let idx = self.add_string(class_name.clone());
501                buf.extend_from_slice(&idx.to_be_bytes());
502            }
503            JvmAnnotationValue::Annotation(annotation) => {
504                buf.push(0x00); // ANNOTATION
505                self.write_annotation_to_buf(annotation, buf)?;
506            }
507            JvmAnnotationValue::Array(values) => {
508                buf.push(0x01); // ARRAY
509                buf.extend_from_slice(&values.len().to_be_bytes());
510                for v in values {
511                    self.write_annotation_value_to_buf(v, buf)?;
512                }
513            }
514        }
515        Ok(())
516    }
517
518    /// Writes a Code attribute to the class file
519    pub fn write_code_attribute(&mut self, program: &JvmProgram, method: &JvmMethod, code_utf8_idx: u16) -> Result<()> {
520        // Code 属性名称索引
521        self.writer.write_u16(code_utf8_idx)?;
522
523        let (bytecode, label_positions) = self.generate_method_bytecode(method);
524
525        // 解析异常表
526        let mut raw_exception_table = Vec::new();
527        for handler in &method.exception_handlers {
528            let start_pc = *label_positions.get(&handler.start_label).unwrap_or(&0) as u16;
529            let end_pc = *label_positions.get(&handler.end_label).unwrap_or(&0) as u16;
530            let handler_pc = *label_positions.get(&handler.handler_label).unwrap_or(&0) as u16;
531            let catch_type_index =
532                if let Some(catch_type) = &handler.catch_type { self.add_class(catch_type.clone()) } else { 0 };
533            raw_exception_table.push(RawExceptionHandler { start_pc, end_pc, handler_pc, catch_type_index });
534        }
535
536        // 收集并准备子属性
537        let mut code_attributes = Vec::new();
538
539        // 自动生成 StackMapTable (如果版本 >= Java 6 且尚未手动提供)
540        if program.version.major >= 50 {
541            let has_stack_map = method.attributes.iter().any(|a| matches!(a, JvmAttribute::StackMapTable { .. }));
542            if !has_stack_map {
543                let analyzer = crate::analyzer::StackMapAnalyzer::new(program.name.clone(), method, &label_positions);
544                let frames = analyzer.analyze();
545                if !frames.is_empty() {
546                    code_attributes.push(JvmAttribute::StackMapTable { frames });
547                }
548            }
549        }
550
551        // 准备子属性数据
552        let mut sub_attr_buf = Vec::new();
553        // 这里需要临时替换 writer 以写入到缓冲区
554        let mut temp_writer = ClassWriter {
555            writer: BinaryWriter::new(&mut sub_attr_buf),
556            cp_entries: self.cp_entries.clone(),
557            cp_map: self.cp_map.clone(),
558        };
559
560        // 写入自动生成的属性
561        for attr in &code_attributes {
562            temp_writer.write_attribute(attr)?;
563        }
564
565        // 同步常量池
566        self.cp_entries = temp_writer.cp_entries;
567        self.cp_map = temp_writer.cp_map;
568
569        // 计算总长度
570        let exception_table_len = raw_exception_table.len() * 8;
571        let attribute_length = 2 + 2 + 4 + bytecode.len() + 2 + exception_table_len + 2 + sub_attr_buf.len();
572        self.writer.write_u32(attribute_length as u32)?;
573
574        // max_stack 和 max_locals
575        self.writer.write_u16(method.max_stack)?;
576        self.writer.write_u16(method.max_locals)?;
577
578        // 字节码长度和字节码
579        self.writer.write_u32(bytecode.len() as u32)?;
580        self.writer.write_all(&bytecode)?;
581
582        // 异常表
583        self.writer.write_u16(raw_exception_table.len() as u16)?;
584        for handler in raw_exception_table {
585            self.writer.write_u16(handler.start_pc)?;
586            self.writer.write_u16(handler.end_pc)?;
587            self.writer.write_u16(handler.handler_pc)?;
588            self.writer.write_u16(handler.catch_type_index)?;
589        }
590
591        // 子属性数量
592        self.writer.write_u16(code_attributes.len() as u16)?;
593        self.writer.write_all(&sub_attr_buf)?;
594
595        Ok(())
596    }
597}