Skip to main content

java_serialization/
parser.rs

1use nom::{
2    bytes::complete::{tag, take},
3    combinator::map,
4    multi::count,
5    number::complete::{
6        be_f32, be_f64, be_i16, be_i32, be_i64, be_i8, be_u16, be_u32, be_u64, be_u8,
7    },
8    IResult,
9};
10
11use crate::constants::*;
12use crate::handle::HandleTable;
13use crate::types::*;
14
15/// Preprocess JDK 8u20 serialization data by inserting a missing TC_ENDBLOCKDATA.
16///
17/// JDK 8u20 exploit payloads omit a TC_ENDBLOCKDATA byte after a TC_REFERENCE
18/// to handle 0x7e0009, causing annotation parsing to fail. This function inserts
19/// the missing byte, mirroring zkar's `FromJDK8u20Bytes` preprocessing.
20///
21/// Note: `parse_serialization_stream` automatically applies this preprocessing
22/// on retry, so explicit use is only needed for debugging or raw byte manipulation.
23pub fn preprocess_jdk8u20(data: &[u8]) -> Vec<u8> {
24    let pattern: &[u8] = &[0x00, 0x7e, 0x00, 0x09];
25    if let Some(pos) = data.windows(4).position(|w| w == pattern) {
26        let mut result = Vec::with_capacity(data.len() + 1);
27        result.extend_from_slice(&data[..pos + 4]);
28        result.push(TC_ENDBLOCKDATA);
29        result.extend_from_slice(&data[pos + 4..]);
30        result
31    } else {
32        data.to_vec()
33    }
34}
35
36/// Parse a Java serialization stream.
37///
38/// Automatically retries with JDK8u20 preprocessing if the initial parse fails.
39/// Returns the remaining unparsed bytes and the parsed `SerializationStream`.
40/// Fails if the magic number (0xACED) or version (5) don't match, or the data
41/// is malformed.
42pub fn parse_serialization_stream(input: &[u8]) -> IResult<&[u8], SerializationStream> {
43    let result = parse_serialization_stream_inner(input);
44    if result.is_ok() {
45        return result;
46    }
47    // Retry with JDK8u20 preprocessing
48    let preprocessed = preprocess_jdk8u20(input);
49    if preprocessed.len() == input.len() {
50        // No modification was made, no point retrying
51        return result;
52    }
53    match parse_serialization_stream_inner(&preprocessed) {
54        Ok((_, stream)) => Ok((&input[input.len()..], stream)),
55        Err(_) => result,
56    }
57}
58
59fn parse_serialization_stream_inner(input: &[u8]) -> IResult<&[u8], SerializationStream> {
60    // Magic: 0xACED
61    let (input, _) = tag(&[0xAC, 0xED])(input)?;
62    // Version: 5
63    let (input, version) = be_u16(input)?;
64
65    let mut handle_table = HandleTable::new();
66    let mut remaining = input;
67    let mut contents = Vec::new();
68
69    while !remaining.is_empty() {
70        let (rem, content) = parse_content(remaining, &mut handle_table)?;
71        contents.push(content);
72        remaining = rem;
73    }
74
75    Ok((remaining, SerializationStream { version, contents }))
76}
77
78fn parse_content<'a>(
79    input: &'a [u8],
80    handles: &mut HandleTable,
81) -> IResult<&'a [u8], ContentElement> {
82    let tc = peek_tc(input)?;
83    match tc {
84        TC_BLOCKDATA | TC_BLOCKDATALONG => {
85            let (input, bd) = parse_block_data(input)?;
86            Ok((input, ContentElement::BlockData(bd)))
87        }
88        _ => {
89            let (input, obj) = parse_object(input, handles)?;
90            Ok((input, ContentElement::Object(obj)))
91        }
92    }
93}
94
95/// Peek at the next type code without consuming it.
96fn peek_tc(input: &[u8]) -> Result<u8, nom::Err<nom::error::Error<&[u8]>>> {
97    if input.is_empty() {
98        return Err(nom::Err::Error(nom::error::Error::new(
99            input,
100            nom::error::ErrorKind::Eof,
101        )));
102    }
103    Ok(input[0])
104}
105
106fn parse_object<'a>(input: &'a [u8], handles: &mut HandleTable) -> IResult<&'a [u8], StreamObject> {
107    let (input, tc) = be_u8(input)?;
108    match tc {
109        TC_NULL => Ok((input, StreamObject::NullReference)),
110        TC_REFERENCE => {
111            let (input, handle) = be_u32(input)?;
112            Ok((input, StreamObject::PrevObject { handle }))
113        }
114        TC_CLASSDESC => {
115            let (input, desc) = parse_normal_class_desc(input, handles)?;
116            Ok((
117                input,
118                StreamObject::NewClassDesc(ClassDesc::Normal(Box::new(desc))),
119            ))
120        }
121        TC_PROXYCLASSDESC => {
122            let (input, desc) = parse_proxy_class_desc(input, handles)?;
123            Ok((input, StreamObject::NewClassDesc(ClassDesc::Proxy(desc))))
124        }
125        TC_OBJECT => {
126            let (input, obj) = parse_new_object(input, handles)?;
127            Ok((input, StreamObject::NewObject(obj)))
128        }
129        TC_STRING => {
130            let (input, s) = parse_tc_string(input, handles, false)?;
131            Ok((input, StreamObject::NewString(s)))
132        }
133        TC_LONGSTRING => {
134            let (input, s) = parse_tc_string(input, handles, true)?;
135            Ok((input, StreamObject::NewString(s)))
136        }
137        TC_ARRAY => {
138            let (input, arr) = parse_new_array(input, handles)?;
139            Ok((input, StreamObject::NewArray(arr)))
140        }
141        TC_CLASS => {
142            let (input, cls) = parse_new_class(input, handles)?;
143            Ok((input, StreamObject::NewClass(cls)))
144        }
145        TC_ENUM => {
146            let (input, en) = parse_new_enum(input, handles)?;
147            Ok((input, StreamObject::NewEnum(en)))
148        }
149        TC_RESET => {
150            handles.reset();
151            Ok((input, StreamObject::Reset))
152        }
153        TC_BLOCKDATA | TC_BLOCKDATALONG => {
154            // Block data should not appear as a standalone object.
155            // It is only valid inside classAnnotation or objectAnnotation.
156            Err(nom::Err::Error(nom::error::Error::new(
157                input,
158                nom::error::ErrorKind::Tag,
159            )))
160        }
161        TC_EXCEPTION => {
162            // TC_EXCEPTION reset (Throwable)object reset
163            // Simplified: skip exception parsing for now
164            Ok((input, StreamObject::Exception))
165        }
166        TC_ENDBLOCKDATA => Err(nom::Err::Error(nom::error::Error::new(
167            input,
168            nom::error::ErrorKind::Tag,
169        ))),
170        _ => Err(nom::Err::Error(nom::error::Error::new(
171            input,
172            nom::error::ErrorKind::Tag,
173        ))),
174    }
175}
176
177fn parse_class_desc_ref<'a>(
178    input: &'a [u8],
179    handles: &mut HandleTable,
180) -> IResult<&'a [u8], ClassDescRef> {
181    let (input, tc) = be_u8(input)?;
182    match tc {
183        TC_NULL => Ok((input, ClassDescRef::Null)),
184        TC_REFERENCE => {
185            let (input, handle) = be_u32(input)?;
186            Ok((input, ClassDescRef::Reference { handle }))
187        }
188        TC_CLASSDESC => {
189            let (input, desc) = parse_normal_class_desc(input, handles)?;
190            Ok((
191                input,
192                ClassDescRef::Inline(Box::new(ClassDesc::Normal(Box::new(desc)))),
193            ))
194        }
195        TC_PROXYCLASSDESC => {
196            let (input, desc) = parse_proxy_class_desc(input, handles)?;
197            Ok((
198                input,
199                ClassDescRef::Inline(Box::new(ClassDesc::Proxy(desc))),
200            ))
201        }
202        _ => Err(nom::Err::Error(nom::error::Error::new(
203            input,
204            nom::error::ErrorKind::Tag,
205        ))),
206    }
207}
208
209fn parse_normal_class_desc<'a>(
210    input: &'a [u8],
211    handles: &mut HandleTable,
212) -> IResult<&'a [u8], NormalClassDesc> {
213    // newHandle - JDK assigns handle BEFORE reading fields
214    let handle = handles.assign_handle();
215    // className (utf)
216    let (input, class_name) = parse_utf(input)?;
217    // serialVersionUID (long)
218    let (input, serial_version_uid) = be_i64(input)?;
219    // classDescFlags (byte)
220    let (input, flags) = be_u8(input)?;
221    // fields
222    let (input, fields) = parse_fields_desc(input, handles)?;
223    // classAnnotation
224    let (input, class_annotation) = parse_class_annotation(input, handles)?;
225    // superClassDesc
226    let (input, super_class_desc) = parse_class_desc_ref(input, handles)?;
227
228    let desc = NormalClassDesc {
229        class_name,
230        serial_version_uid,
231        handle,
232        flags,
233        fields,
234        class_annotation,
235        super_class_desc: Box::new(super_class_desc),
236    };
237
238    // Update the handle table with the actual class descriptor object
239    handles.update(
240        handle,
241        StreamObject::NewClassDesc(ClassDesc::Normal(Box::new(desc.clone()))),
242    );
243
244    Ok((input, desc))
245}
246
247fn parse_proxy_class_desc<'a>(
248    input: &'a [u8],
249    handles: &mut HandleTable,
250) -> IResult<&'a [u8], ProxyClassDesc> {
251    // newHandle - JDK assigns handle BEFORE reading interface names
252    let handle = handles.assign_handle();
253    // (int)<count> proxyInterfaceName[count]
254    let (input, interface_count) = be_u32(input)?;
255    let (input, interface_names) = count(parse_utf, interface_count as usize)(input)?;
256    // classAnnotation
257    let (input, class_annotation) = parse_class_annotation(input, handles)?;
258    // superClassDesc
259    let (input, super_class_desc) = parse_class_desc_ref(input, handles)?;
260
261    let desc = ProxyClassDesc {
262        handle,
263        interface_names,
264        class_annotation,
265        super_class_desc: Box::new(super_class_desc),
266    };
267
268    // Update the handle table with the actual class descriptor object
269    handles.update(
270        handle,
271        StreamObject::NewClassDesc(ClassDesc::Proxy(desc.clone())),
272    );
273
274    Ok((input, desc))
275}
276
277fn parse_fields_desc<'a>(
278    input: &'a [u8],
279    handles: &mut HandleTable,
280) -> IResult<&'a [u8], Vec<FieldDesc>> {
281    let (input, field_count) = be_u16(input)?;
282    let mut remaining = input;
283    let mut fields = Vec::with_capacity(field_count as usize);
284    for _ in 0..field_count {
285        let (rem, field) = parse_field_desc(remaining, handles)?;
286        fields.push(field);
287        remaining = rem;
288    }
289    Ok((remaining, fields))
290}
291
292fn parse_field_desc<'a>(
293    input: &'a [u8],
294    handles: &mut HandleTable,
295) -> IResult<&'a [u8], FieldDesc> {
296    let (input, type_code) = be_u8(input)?;
297    let (input, field_name) = parse_utf(input)?;
298    match type_code {
299        b'B' | b'C' | b'D' | b'F' | b'I' | b'J' | b'S' | b'Z' => Ok((
300            input,
301            FieldDesc::Primitive(PrimitiveFieldDesc {
302                type_code,
303                field_name,
304            }),
305        )),
306        b'L' | b'[' => {
307            // className1: (String)object - typically TC_STRING followed by (utf)
308            let (input, class_name) = parse_string_object(input, handles)?;
309            Ok((
310                input,
311                FieldDesc::Object(ObjectFieldDesc {
312                    type_code,
313                    field_name,
314                    class_name,
315                }),
316            ))
317        }
318        _ => Err(nom::Err::Error(nom::error::Error::new(
319            input,
320            nom::error::ErrorKind::Tag,
321        ))),
322    }
323}
324
325/// Parse a (String)object - could be TC_STRING, TC_LONGSTRING, TC_REFERENCE, or TC_NULL.
326/// Returns the string value.
327fn parse_string_object<'a>(
328    input: &'a [u8],
329    handles: &mut HandleTable,
330) -> IResult<&'a [u8], String> {
331    let (input, tc) = be_u8(input)?;
332    match tc {
333        TC_STRING => {
334            let (input, value) = parse_utf(input)?;
335            let _handle = handles.assign(StreamObject::NewString(StreamString {
336                value: value.clone(),
337                handle: 0,
338                is_long: false,
339            }));
340            Ok((input, value))
341        }
342        TC_LONGSTRING => {
343            let (input, value) = parse_long_utf(input)?;
344            let _handle = handles.assign(StreamObject::NewString(StreamString {
345                value: value.clone(),
346                handle: 0,
347                is_long: true,
348            }));
349            Ok((input, value))
350        }
351        TC_REFERENCE => {
352            let (input, handle) = be_u32(input)?;
353            // Try to resolve from handle table
354            let value = handles
355                .get(handle)
356                .and_then(|obj| {
357                    if let StreamObject::NewString(s) = obj {
358                        Some(s.value.clone())
359                    } else {
360                        None
361                    }
362                })
363                .unwrap_or_else(|| format!("<ref:{:#08x}>", handle));
364            Ok((input, value))
365        }
366        TC_NULL => Ok((input, String::new())),
367        _ => Err(nom::Err::Error(nom::error::Error::new(
368            input,
369            nom::error::ErrorKind::Tag,
370        ))),
371    }
372}
373
374fn parse_class_annotation<'a>(
375    input: &'a [u8],
376    handles: &mut HandleTable,
377) -> IResult<&'a [u8], Vec<AnnotationElement>> {
378    let mut remaining = input;
379    let mut elements = Vec::new();
380    loop {
381        let tc = peek_tc(remaining)?;
382        if tc == TC_ENDBLOCKDATA {
383            let (rem, _) = be_u8(remaining)?; // consume TC_ENDBLOCKDATA
384            return Ok((rem, elements));
385        }
386        if tc == TC_BLOCKDATA || tc == TC_BLOCKDATALONG {
387            let (rem, bd) = parse_block_data(remaining)?;
388            elements.push(AnnotationElement::BlockData(bd));
389            remaining = rem;
390        } else {
391            let (rem, obj) = parse_object(remaining, handles)?;
392            elements.push(AnnotationElement::Object(obj));
393            remaining = rem;
394        }
395    }
396}
397
398fn parse_new_object<'a>(
399    input: &'a [u8],
400    handles: &mut HandleTable,
401) -> IResult<&'a [u8], NewObject> {
402    // classDesc
403    let (input, class_desc) = parse_class_desc_ref(input, handles)?;
404    // newHandle
405    let handle = handles.assign_handle();
406    // classdata[] - data for each class in hierarchy
407    let (input, class_data) = parse_class_data_hierarchy(input, &class_desc, handles)?;
408
409    let obj = NewObject {
410        class_desc: class_desc.clone(),
411        handle,
412        class_data,
413    };
414    handles.update(handle, StreamObject::NewObject(obj.clone()));
415    Ok((input, obj))
416}
417
418fn parse_class_data_hierarchy<'a>(
419    input: &'a [u8],
420    class_desc_ref: &ClassDescRef,
421    handles: &mut HandleTable,
422) -> IResult<&'a [u8], Vec<ClassData>> {
423    // Walk the class hierarchy - collect from this class to highest superclass
424    let hierarchy = collect_class_hierarchy(class_desc_ref, handles);
425    let mut remaining = input;
426    let mut class_data = Vec::new();
427
428    for desc in &hierarchy {
429        let (rem, data) = parse_class_data(remaining, desc, handles)?;
430        class_data.push(data);
431        remaining = rem;
432    }
433
434    Ok((remaining, class_data))
435}
436
437/// Collect class descriptors from super to this class (excluding null/non-serializable).
438/// Resolves handle references via the handle table.
439fn collect_class_hierarchy(
440    class_desc_ref: &ClassDescRef,
441    handles: &HandleTable,
442) -> Vec<NormalClassDesc> {
443    let mut hierarchy = Vec::new();
444    let mut current = Some(class_desc_ref);
445
446    while let Some(desc_ref) = current {
447        match desc_ref {
448            ClassDescRef::Null => break,
449            ClassDescRef::Reference { handle } => {
450                // Resolve reference from handle table
451                match handles.get(*handle) {
452                    Some(StreamObject::NewClassDesc(ClassDesc::Normal(desc))) => {
453                        let super_ref = &*desc.super_class_desc;
454                        hierarchy.push((**desc).clone());
455                        current = Some(super_ref);
456                    }
457                    Some(StreamObject::NewClassDesc(ClassDesc::Proxy(_))) => break,
458                    _ => break,
459                }
460            }
461            ClassDescRef::Inline(class_desc) => match class_desc.as_ref() {
462                ClassDesc::Normal(desc) => {
463                    let super_ref = &*desc.super_class_desc;
464                    hierarchy.push((**desc).clone());
465                    current = Some(super_ref);
466                }
467                ClassDesc::Proxy(_) => {
468                    break;
469                }
470            },
471        }
472    }
473
474    // Reverse to get superclass-first order (per Java serialization spec)
475    hierarchy.reverse();
476    hierarchy
477}
478
479fn parse_class_data<'a>(
480    input: &'a [u8],
481    desc: &NormalClassDesc,
482    handles: &mut HandleTable,
483) -> IResult<&'a [u8], ClassData> {
484    if desc.is_externalizable() {
485        if desc.has_block_data() {
486            // SC_EXTERNALIZABLE + SC_BLOCK_DATA: objectAnnotation format
487            let (input, annotation) = parse_object_annotation(input, handles)?;
488            Ok((input, ClassData::ExternalBlockData(annotation)))
489        } else {
490            // SC_EXTERNALIZABLE without SC_BLOCK_DATA (old JDK 1.1 format):
491            // The external content length is NOT delimited by TC_ENDBLOCKDATA.
492            // Without class metadata, we cannot determine the content length.
493            // This format is extremely rare in practice. We attempt to parse
494            // as objectAnnotation as a best-effort fallback, which works for
495            // classes that happen to write TC_ENDBLOCKDATA-terminated data.
496            let (input, annotation) = parse_object_annotation(input, handles)?;
497            Ok((input, ClassData::ExternalBlockData(annotation)))
498        }
499    } else if desc.is_serializable() {
500        if desc.has_write_method() {
501            // SC_SERIALIZABLE + SC_WRITE_METHOD: writeObject() may or may not
502            // have called defaultWriteObject(). We try parsing field values
503            // first, then annotation; if that fails, fall back to treating
504            // the entire region as an objectAnnotation.
505            let snap = handles.snapshot();
506            if let Ok((input, field_values)) = parse_field_values(input, &desc.fields, handles) {
507                let field_set = FieldValueSet {
508                    values: field_values,
509                };
510                if let Ok((input, annotation)) = parse_object_annotation(input, handles) {
511                    return Ok((
512                        input,
513                        ClassData::WriteMethodWithFields(field_set, annotation),
514                    ));
515                }
516            }
517            // Rollback handle table and try pure annotation mode
518            handles.rollback(snap);
519            let (input, annotation) = parse_object_annotation(input, handles)?;
520            Ok((input, ClassData::WriteMethod(annotation)))
521        } else {
522            let (input, field_values) = parse_field_values(input, &desc.fields, handles)?;
523            let field_set = FieldValueSet {
524                values: field_values,
525            };
526            Ok((input, ClassData::NoWriteMethod(field_set)))
527        }
528    } else {
529        // Non-serializable class, no data
530        Ok((
531            input,
532            ClassData::NoWriteMethod(FieldValueSet { values: vec![] }),
533        ))
534    }
535}
536
537fn parse_field_values<'a>(
538    input: &'a [u8],
539    fields: &[FieldDesc],
540    handles: &mut HandleTable,
541) -> IResult<&'a [u8], Vec<FieldValue>> {
542    let mut remaining = input;
543    let mut values = Vec::with_capacity(fields.len());
544
545    for field in fields {
546        let (rem, value) = parse_field_value(remaining, field, handles)?;
547        values.push(value);
548        remaining = rem;
549    }
550
551    Ok((remaining, values))
552}
553
554fn parse_field_value<'a>(
555    input: &'a [u8],
556    field: &FieldDesc,
557    handles: &mut HandleTable,
558) -> IResult<&'a [u8], FieldValue> {
559    match field {
560        FieldDesc::Primitive(pf) => match pf.type_code {
561            b'B' => map(be_i8, FieldValue::Byte)(input),
562            b'C' => map(be_u16, FieldValue::Char)(input),
563            b'D' => map(be_f64, FieldValue::Double)(input),
564            b'F' => map(be_f32, FieldValue::Float)(input),
565            b'I' => map(be_i32, FieldValue::Int)(input),
566            b'J' => map(be_i64, FieldValue::Long)(input),
567            b'S' => map(be_i16, FieldValue::Short)(input),
568            b'Z' => map(be_u8, |v| FieldValue::Boolean(v != 0))(input),
569            _ => Err(nom::Err::Error(nom::error::Error::new(
570                input,
571                nom::error::ErrorKind::Tag,
572            ))),
573        },
574        FieldDesc::Object(_) => {
575            let (input, obj) = parse_object(input, handles)?;
576            match obj {
577                StreamObject::NullReference => Ok((input, FieldValue::Object(None))),
578                obj => Ok((input, FieldValue::Object(Some(Box::new(obj))))),
579            }
580        }
581    }
582}
583
584fn parse_object_annotation<'a>(
585    input: &'a [u8],
586    handles: &mut HandleTable,
587) -> IResult<&'a [u8], ObjectAnnotation> {
588    let mut remaining = input;
589    let mut contents = Vec::new();
590    loop {
591        let tc = peek_tc(remaining)?;
592        if tc == TC_ENDBLOCKDATA {
593            let (rem, _) = be_u8(remaining)?; // consume TC_ENDBLOCKDATA
594            return Ok((rem, ObjectAnnotation { contents }));
595        }
596        if tc == TC_BLOCKDATA || tc == TC_BLOCKDATALONG {
597            let (rem, bd) = parse_block_data(remaining)?;
598            contents.push(AnnotationElement::BlockData(bd));
599            remaining = rem;
600        } else {
601            let (rem, obj) = parse_object(remaining, handles)?;
602            contents.push(AnnotationElement::Object(obj));
603            remaining = rem;
604        }
605    }
606}
607
608fn parse_new_class<'a>(input: &'a [u8], handles: &mut HandleTable) -> IResult<&'a [u8], NewClass> {
609    let (input, class_desc) = parse_class_desc_ref(input, handles)?;
610    let handle = handles.assign_handle();
611    let cls = NewClass {
612        class_desc: class_desc.clone(),
613        handle,
614    };
615    handles.update(handle, StreamObject::NewClass(cls.clone()));
616    Ok((input, cls))
617}
618
619fn parse_new_array<'a>(input: &'a [u8], handles: &mut HandleTable) -> IResult<&'a [u8], NewArray> {
620    // classDesc
621    let (input, class_desc) = parse_class_desc_ref(input, handles)?;
622    // (int)<size>
623    let (input, size) = be_u32(input)?;
624    // newHandle - JDK assigns handle AFTER reading size, BEFORE reading values
625    let handle = handles.assign_handle();
626    // values[size]
627    let (input, values) = parse_array_values(input, &class_desc, size as usize, handles)?;
628
629    let arr = NewArray {
630        class_desc,
631        handle,
632        size,
633        values,
634    };
635    handles.update(handle, StreamObject::NewArray(arr.clone()));
636    Ok((input, arr))
637}
638
639fn parse_array_values<'a>(
640    input: &'a [u8],
641    class_desc: &ClassDescRef,
642    size: usize,
643    handles: &mut HandleTable,
644) -> IResult<&'a [u8], ArrayValues> {
645    // Determine array component type from class name
646    // Need to resolve references through the handle table
647    let class_name = match class_desc {
648        ClassDescRef::Inline(cd) => match cd.as_ref() {
649            ClassDesc::Normal(desc) => desc.class_name.as_str(),
650            ClassDesc::Proxy(_) => "",
651        },
652        ClassDescRef::Reference { handle } => {
653            // Resolve reference from handle table to get class name
654            handles
655                .get(*handle)
656                .and_then(|obj| {
657                    if let StreamObject::NewClassDesc(ClassDesc::Normal(desc)) = obj {
658                        Some(desc.class_name.as_str())
659                    } else {
660                        None
661                    }
662                })
663                .unwrap_or("")
664        }
665        ClassDescRef::Null => "",
666    };
667
668    // Array class names are like "[B", "[[Ljava/lang/Object;", etc.
669    match get_array_component_type(class_name) {
670        ArrayComponentType::Byte => {
671            let (input, vals) = count(be_i8, size)(input)?;
672            Ok((input, ArrayValues::Byte(vals)))
673        }
674        ArrayComponentType::Char => {
675            let (input, vals) = count(be_u16, size)(input)?;
676            Ok((input, ArrayValues::Char(vals)))
677        }
678        ArrayComponentType::Double => {
679            let (input, vals) = count(be_f64, size)(input)?;
680            Ok((input, ArrayValues::Double(vals)))
681        }
682        ArrayComponentType::Float => {
683            let (input, vals) = count(be_f32, size)(input)?;
684            Ok((input, ArrayValues::Float(vals)))
685        }
686        ArrayComponentType::Int => {
687            let (input, vals) = count(be_i32, size)(input)?;
688            Ok((input, ArrayValues::Int(vals)))
689        }
690        ArrayComponentType::Long => {
691            let (input, vals) = count(be_i64, size)(input)?;
692            Ok((input, ArrayValues::Long(vals)))
693        }
694        ArrayComponentType::Short => {
695            let (input, vals) = count(be_i16, size)(input)?;
696            Ok((input, ArrayValues::Short(vals)))
697        }
698        ArrayComponentType::Boolean => {
699            let (input, vals) = count(be_u8, size)(input)?;
700            Ok((input, ArrayValues::Boolean(vals)))
701        }
702        ArrayComponentType::Object => {
703            let mut remaining = input;
704            let mut vals = Vec::with_capacity(size);
705            for _ in 0..size {
706                let (rem, obj) = parse_object(remaining, handles)?;
707                match obj {
708                    StreamObject::NullReference => vals.push(None),
709                    obj => vals.push(Some(obj)),
710                }
711                remaining = rem;
712            }
713            Ok((remaining, ArrayValues::Object(vals)))
714        }
715    }
716}
717
718enum ArrayComponentType {
719    Byte,
720    Char,
721    Double,
722    Float,
723    Int,
724    Long,
725    Short,
726    Boolean,
727    Object,
728}
729
730fn get_array_component_type(class_name: &str) -> ArrayComponentType {
731    // Strip leading '[' to get component type
732    let mut chars = class_name.chars().peekable();
733    let mut depth = 0;
734    while chars.peek() == Some(&'[') {
735        chars.next();
736        depth += 1;
737    }
738
739    if depth == 0 {
740        return ArrayComponentType::Object;
741    }
742
743    // If there are multiple array dimensions (depth > 1), the elements are objects (arrays)
744    if depth > 1 {
745        return ArrayComponentType::Object;
746    }
747
748    // depth == 1: single-dimension array, check the component type
749    match chars.next() {
750        Some('B') => ArrayComponentType::Byte,
751        Some('C') => ArrayComponentType::Char,
752        Some('D') => ArrayComponentType::Double,
753        Some('F') => ArrayComponentType::Float,
754        Some('I') => ArrayComponentType::Int,
755        Some('J') => ArrayComponentType::Long,
756        Some('S') => ArrayComponentType::Short,
757        Some('Z') => ArrayComponentType::Boolean,
758        Some('L') | None => ArrayComponentType::Object,
759        _ => ArrayComponentType::Object,
760    }
761}
762
763fn parse_new_enum<'a>(input: &'a [u8], handles: &mut HandleTable) -> IResult<&'a [u8], NewEnum> {
764    // classDesc
765    let (input, class_desc) = parse_class_desc_ref(input, handles)?;
766    // newHandle
767    let handle = handles.assign_handle();
768    // enumConstantName: (String)object
769    let (input, constant_name) = parse_enum_constant_name(input, handles)?;
770
771    let en = NewEnum {
772        class_desc,
773        handle,
774        constant_name,
775    };
776    handles.update(handle, StreamObject::NewEnum(en.clone()));
777    Ok((input, en))
778}
779
780fn parse_enum_constant_name<'a>(
781    input: &'a [u8],
782    handles: &mut HandleTable,
783) -> IResult<&'a [u8], StreamString> {
784    let (input, tc) = be_u8(input)?;
785    match tc {
786        TC_STRING => parse_tc_string(input, handles, false),
787        TC_LONGSTRING => parse_tc_string(input, handles, true),
788        TC_REFERENCE => {
789            let (input, handle) = be_u32(input)?;
790            // Return a placeholder - the actual string should be resolved from the handle table
791            Ok((
792                input,
793                StreamString {
794                    value: format!("<ref:{:#08x}>", handle),
795                    handle: 0, // This string doesn't get a new handle
796                    is_long: false,
797                },
798            ))
799        }
800        _ => Err(nom::Err::Error(nom::error::Error::new(
801            input,
802            nom::error::ErrorKind::Tag,
803        ))),
804    }
805}
806
807fn parse_tc_string<'a>(
808    input: &'a [u8],
809    handles: &mut HandleTable,
810    is_long: bool,
811) -> IResult<&'a [u8], StreamString> {
812    let (input, value) = if is_long {
813        parse_long_utf(input)?
814    } else {
815        parse_utf(input)?
816    };
817    let stream_string = StreamString {
818        value: value.clone(),
819        handle: 0,
820        is_long,
821    };
822    let handle = handles.assign(StreamObject::NewString(stream_string.clone()));
823    let result = StreamString {
824        value,
825        handle,
826        is_long,
827    };
828    Ok((input, result))
829}
830
831/// Parse a (utf) - 2-byte length followed by modified UTF-8 data.
832fn parse_utf(input: &[u8]) -> IResult<&[u8], String> {
833    let (input, length) = be_u16(input)?;
834    let (input, bytes) = take(length as usize)(input)?;
835    let value = cesu8::from_java_cesu8(bytes)
836        .map(|s| s.into_owned())
837        .unwrap_or_else(|_| String::from_utf8_lossy(bytes).into_owned());
838    Ok((input, value))
839}
840
841/// Parse a (long-utf) - 8-byte length followed by modified UTF-8 data.
842fn parse_long_utf(input: &[u8]) -> IResult<&[u8], String> {
843    let (input, length) = be_u64(input)?;
844    let (input, bytes) = take(length as usize)(input)?;
845    let value = cesu8::from_java_cesu8(bytes)
846        .map(|s| s.into_owned())
847        .unwrap_or_else(|_| String::from_utf8_lossy(bytes).into_owned());
848    Ok((input, value))
849}
850
851fn parse_block_data(input: &[u8]) -> IResult<&[u8], BlockData> {
852    let (input, tc) = be_u8(input)?;
853    parse_block_data_from_tc(input, tc)
854}
855
856fn parse_block_data_from_tc(input: &[u8], tc: u8) -> IResult<&[u8], BlockData> {
857    match tc {
858        TC_BLOCKDATA => {
859            let (input, size) = be_u8(input)?;
860            let (input, data) = take(size as usize)(input)?;
861            Ok((
862                input,
863                BlockData::Short {
864                    data: data.to_vec(),
865                },
866            ))
867        }
868        TC_BLOCKDATALONG => {
869            let (input, size) = be_u32(input)?;
870            let (input, data) = take(size as usize)(input)?;
871            Ok((
872                input,
873                BlockData::Long {
874                    data: data.to_vec(),
875                },
876            ))
877        }
878        _ => Err(nom::Err::Error(nom::error::Error::new(
879            input,
880            nom::error::ErrorKind::Tag,
881        ))),
882    }
883}