cfsp 1.0.1

A JVM Bytecode Manipulation Framework inspired by ASM.
Documentation
use std::io::Read;

use byteorder::{BigEndian, ReadBytesExt};

use crate::node::attribute::{
    Attribute, Object, StackMapFrameEntry, StackMapTable, VerificationType,
};
use crate::parse::error::{ParseError, ParseResult};

#[inline]
pub(super) fn stack_map_table<R: Read>(input: &mut R) -> ParseResult<Option<Attribute>> {
    let number_of_entries = input.read_u16::<BigEndian>()?;
    let mut entries = Vec::with_capacity(number_of_entries as usize);

    for _ in 0..number_of_entries {
        entries.push(stack_map_frame_entry(input)?);
    }

    Ok(Some(Attribute::StackMapTable(StackMapTable {
        number_of_entries,
        entries,
    })))
}

fn stack_map_frame_entry<R: Read>(input: &mut R) -> ParseResult<StackMapFrameEntry> {
    let frame_type = input.read_u8()?;

    match frame_type {
        0..=63 => Ok(StackMapFrameEntry::Same { frame_type }),
        64..=127 => {
            let stack = verification_type(input)?;

            Ok(StackMapFrameEntry::SameLocal1StackItem { frame_type, stack })
        }
        247 => {
            let offset_delta = input.read_u16::<BigEndian>()?;
            let stack = verification_type(input)?;

            Ok(StackMapFrameEntry::SameLocal1StackItemExtended {
                frame_type,
                offset_delta,
                stack,
            })
        }
        248..=250 => {
            let offset_delta = input.read_u16::<BigEndian>()?;

            Ok(StackMapFrameEntry::Chop {
                frame_type,
                offset_delta,
            })
        }
        251 => {
            let offset_delta = input.read_u16::<BigEndian>()?;

            Ok(StackMapFrameEntry::SameExtended {
                frame_type,
                offset_delta,
            })
        }
        252..=254 => {
            let offset_delta = input.read_u16::<BigEndian>()?;
            let mut locals = Vec::with_capacity(frame_type as usize - 251);

            for _ in 0..frame_type - 251 {
                locals.push(verification_type(input)?);
            }

            Ok(StackMapFrameEntry::Append {
                frame_type,
                offset_delta,
                locals,
            })
        }
        255 => {
            let offset_delta = input.read_u16::<BigEndian>()?;
            let number_of_locals = input.read_u16::<BigEndian>()?;
            let mut locals = Vec::with_capacity(number_of_locals as usize);

            for _ in 0..number_of_locals {
                locals.push(verification_type(input)?);
            }

            let number_of_stack_items = input.read_u16::<BigEndian>()?;
            let mut stack = Vec::with_capacity(number_of_locals as usize);

            for _ in 0..number_of_stack_items {
                stack.push(verification_type(input)?);
            }

            Ok(StackMapFrameEntry::Full {
                frame_type,
                offset_delta,
                number_of_locals,
                locals,
                number_of_stack_items,
                stack,
            })
        }
        _ => Err(ParseError::MatchOutOfBoundUsize(
            "stack map frame entry",
            vec![
                "0..=63",
                "64..=127",
                "247",
                "248..=250",
                "251",
                "252..=254",
                "255",
            ],
            frame_type as usize,
        )),
    }
}

fn verification_type<R: Read>(input: &mut R) -> ParseResult<VerificationType> {
    let tag = input.read_u8()?;

    match tag {
        0 => Ok(VerificationType::Top),
        1 => Ok(VerificationType::Integer),
        2 => Ok(VerificationType::Float),
        3 => Ok(VerificationType::Double),
        4 => Ok(VerificationType::Long),
        5 => Ok(VerificationType::Null),
        6 => Ok(VerificationType::UninitializedThis),
        7 => {
            let cpool_index = input.read_u16::<BigEndian>()?;

            Ok(VerificationType::Object(Object { cpool_index }))
        }
        8 => {
            let offset = input.read_u16::<BigEndian>()?;

            Ok(VerificationType::Uninitialized { offset })
        }
        _ => Err(ParseError::MatchOutOfBoundUsize(
            "verification type",
            vec!["0..=8"],
            tag as usize,
        )),
    }
}