use std::cell::RefCell;
use std::ops::Deref;
use paste::paste;
use serde::{Deserialize, Serialize};
use crate::node::attribute::{Attribute, AttributeInfo, BootstrapMethod, BootstrapMethods};
use crate::node::error::{NodeResError, NodeResResult};
use crate::parse::ParseError;
macro_rules! const_getter {
($(#[$attr:meta])* => $variant_name: ident, $variant: ty) => {
paste! {
$(#[$attr])*
pub fn [< get_ $variant_name >] (&self, index: u16) -> Option<&$variant> {
if let Some(Some(Constant::$variant(constant))) = self.get(index as usize - 1)
{
Some(constant)
} else {
None
}
}
}
};
}
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub struct ConstantPool {
len: u16,
entries: Vec<Option<Constant>>,
}
impl ConstantPool {
pub(crate) fn with_capacity(capacity: u16) -> Self {
Self {
len: 1,
entries: Vec::with_capacity(capacity as usize),
}
}
pub fn len(&self) -> u16 {
self.len
}
pub(crate) fn add(&mut self, constant: Constant) {
let occupies_2_slots = constant.occupies_2_slots();
self.entries.push(Some(constant));
if occupies_2_slots {
self.len += 2;
self.entries.push(None);
} else {
self.len += 1;
}
}
pub fn get_constant(&self, index: u16) -> Option<&Constant> {
self.get(index as usize - 1).map(Option::as_ref).flatten()
}
const_getter!(
=> utf8, Utf8
);
const_getter!(
=> integer, Integer
);
const_getter!(
=> float, Float
);
const_getter!(
=> long, Long
);
const_getter!(
=> double, Double
);
const_getter!(
=> class, Class
);
const_getter!(
=> string, String
);
const_getter!(
=> field_ref, FieldRef
);
const_getter!(
=> method_ref, MethodRef
);
const_getter!(
=> interface_method_ref, InterfaceMethodRef
);
const_getter!(
=> name_and_type, NameAndType
);
const_getter!(
=> method_handle, MethodHandle
);
const_getter!(
=> method_type, MethodType
);
const_getter!(
=> dynamic, Dynamic
);
const_getter!(
=> invoke_dynamic, InvokeDynamic
);
const_getter!(
=> module, Module
);
const_getter!(
=> package, Package
);
}
impl Deref for ConstantPool {
type Target = Vec<Option<Constant>>;
fn deref(&self) -> &Self::Target {
&self.entries
}
}
#[repr(u8)]
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Serialize, Deserialize)]
pub enum ConstantTag {
Utf8 = 1,
Integer = 3,
Float = 4,
Long = 5,
Double = 6,
Class = 7,
String = 8,
FieldRef = 9,
MethodRef = 10,
InterfaceMethodRef = 11,
NameAndType = 12,
MethodHandle = 15,
MethodType = 16,
Dynamic = 17,
InvokeDynamic = 18,
Module = 19,
Package = 20,
}
impl TryFrom<u8> for ConstantTag {
type Error = ParseError;
fn try_from(value: u8) -> Result<Self, Self::Error> {
use ConstantTag::*;
let tag = match value {
1 => Utf8,
3 => Integer,
4 => Float,
5 => Long,
6 => Double,
7 => Class,
8 => String,
9 => FieldRef,
10 => MethodRef,
11 => InterfaceMethodRef,
12 => NameAndType,
15 => MethodHandle,
16 => MethodType,
17 => Dynamic,
18 => InvokeDynamic,
19 => Module,
20 => Package,
_ => {
return Err(ParseError::MatchOutOfBoundUsize(
"constant tag",
vec!["1", "3..=12", "15..=20"],
value as usize,
))
}
};
Ok(tag)
}
}
impl Into<&'static str> for ConstantTag {
fn into(self) -> &'static str {
match self {
ConstantTag::Utf8 => "Utf8",
ConstantTag::Integer => "Integer",
ConstantTag::Float => "Float",
ConstantTag::Long => "Long",
ConstantTag::Double => "Double",
ConstantTag::Class => "Class",
ConstantTag::String => "String",
ConstantTag::FieldRef => "FieldRef",
ConstantTag::MethodRef => "MethodRef",
ConstantTag::InterfaceMethodRef => "InterfaceMethodRef",
ConstantTag::NameAndType => "NameAndType",
ConstantTag::MethodHandle => "MethodHandle",
ConstantTag::MethodType => "MethodType",
ConstantTag::Dynamic => "Dynamic",
ConstantTag::InvokeDynamic => "InvokeDynamic",
ConstantTag::Module => "Module",
ConstantTag::Package => "Package",
}
}
}
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub enum Constant {
Utf8(Utf8),
Integer(Integer),
Float(Float),
Long(Long),
Double(Double),
Class(Class),
String(String),
FieldRef(FieldRef),
MethodRef(MethodRef),
InterfaceMethodRef(InterfaceMethodRef),
NameAndType(NameAndType),
MethodHandle(MethodHandle),
MethodType(MethodType),
Dynamic(Dynamic),
InvokeDynamic(InvokeDynamic),
Module(Module),
Package(Package),
}
impl Constant {
pub const fn tag(&self) -> ConstantTag {
match self {
Constant::Utf8(..) => ConstantTag::Utf8,
Constant::Integer(..) => ConstantTag::Integer,
Constant::Float(..) => ConstantTag::Float,
Constant::Long(..) => ConstantTag::Long,
Constant::Double(..) => ConstantTag::Double,
Constant::Class(..) => ConstantTag::Class,
Constant::String(..) => ConstantTag::String,
Constant::FieldRef(..) => ConstantTag::FieldRef,
Constant::MethodRef(..) => ConstantTag::MethodRef,
Constant::InterfaceMethodRef(..) => ConstantTag::InterfaceMethodRef,
Constant::NameAndType(..) => ConstantTag::NameAndType,
Constant::MethodHandle(..) => ConstantTag::MethodHandle,
Constant::MethodType(..) => ConstantTag::MethodType,
Constant::Dynamic(..) => ConstantTag::Dynamic,
Constant::InvokeDynamic(..) => ConstantTag::InvokeDynamic,
Constant::Module(..) => ConstantTag::Module,
Constant::Package(..) => ConstantTag::Package,
}
}
pub const fn occupies_2_slots(&self) -> bool {
match self {
Constant::Long(_) | Constant::Double(_) => true,
_ => false,
}
}
pub fn name(&self) -> &'static str {
self.tag().into()
}
}
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub struct Utf8 {
pub length: u16,
pub bytes: Vec<u8>,
#[serde(skip)]
pub string: RefCell<Option<std::string::String>>,
}
impl Utf8 {
pub fn string(&self) -> NodeResResult<std::string::String> {
let string_ref = self.string.borrow_mut();
if let Some(string) = string_ref.as_ref() {
Ok(string.clone())
} else {
cesu8::from_java_cesu8(&self.bytes[..])
.map_err(|_| NodeResError::StringParseFail(self.bytes.clone().into_boxed_slice()))
.map(|string| string.to_string())
}
}
}
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub struct Integer {
pub bytes: [u8; 4],
}
impl Integer {
pub fn as_i32(&self) -> i32 {
i32::from_be_bytes(self.bytes)
}
}
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub struct Float {
pub bytes: [u8; 4],
}
impl Float {
pub fn as_f32(&self) -> f32 {
f32::from_be_bytes(self.bytes)
}
}
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub struct Long {
pub high_bytes: [u8; 4],
pub low_bytes: [u8; 4],
}
impl Long {
pub fn as_i64(&self) -> i64 {
let mut bytes = [0u8; 8];
bytes[..4].copy_from_slice(&self.high_bytes);
bytes[4..].copy_from_slice(&self.low_bytes);
i64::from_be_bytes(bytes)
}
}
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub struct Double {
pub high_bytes: [u8; 4],
pub low_bytes: [u8; 4],
}
impl Double {
pub fn as_f64(&self) -> f64 {
let mut bytes = [0u8; 8];
bytes[..4].copy_from_slice(&self.high_bytes);
bytes[4..].copy_from_slice(&self.low_bytes);
f64::from_be_bytes(bytes)
}
}
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub struct Class {
pub name_index: u16,
}
impl Class {
pub fn name<'constant, 'constant_pool: 'constant>(
&'constant self,
constant_pool: &'constant_pool ConstantPool,
) -> Option<&'constant_pool Utf8> {
constant_pool.get_utf8(self.name_index)
}
}
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub struct String {
pub string_index: u16,
}
impl String {
pub fn string<'constant, 'constant_pool: 'constant>(
&'constant self,
constant_pool: &'constant_pool ConstantPool,
) -> Option<&'constant_pool Utf8> {
constant_pool.get_utf8(self.string_index)
}
}
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub struct FieldRef {
pub class_index: u16,
pub name_and_type_index: u16,
}
impl FieldRef {
pub fn class<'constant, 'constant_pool: 'constant>(
&'constant self,
constant_pool: &'constant_pool ConstantPool,
) -> Option<&'constant_pool Class> {
constant_pool.get_class(self.class_index)
}
pub fn name_and_type<'constant, 'constant_pool: 'constant>(
&'constant self,
constant_pool: &'constant_pool ConstantPool,
) -> Option<&'constant_pool NameAndType> {
constant_pool.get_name_and_type(self.name_and_type_index)
}
}
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub struct MethodRef {
pub class_index: u16,
pub name_and_type_index: u16,
}
impl MethodRef {
pub fn class<'constant, 'constant_pool: 'constant>(
&'constant self,
constant_pool: &'constant_pool ConstantPool,
) -> Option<&'constant_pool Class> {
constant_pool.get_class(self.class_index)
}
pub fn name_and_type<'constant, 'constant_pool: 'constant>(
&'constant self,
constant_pool: &'constant_pool ConstantPool,
) -> Option<&'constant_pool NameAndType> {
constant_pool.get_name_and_type(self.name_and_type_index)
}
}
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub struct InterfaceMethodRef {
pub class_index: u16,
pub name_and_type_index: u16,
}
impl InterfaceMethodRef {
pub fn class<'constant, 'constant_pool: 'constant>(
&'constant self,
constant_pool: &'constant_pool ConstantPool,
) -> Option<&'constant_pool Class> {
constant_pool.get_class(self.class_index)
}
pub fn name_and_type<'constant, 'constant_pool: 'constant>(
&'constant self,
constant_pool: &'constant_pool ConstantPool,
) -> Option<&'constant_pool NameAndType> {
constant_pool.get_name_and_type(self.name_and_type_index)
}
}
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub struct NameAndType {
pub name_index: u16,
pub type_index: u16,
}
impl NameAndType {
pub fn name<'constant, 'constant_pool: 'constant>(
&'constant self,
constant_pool: &'constant_pool ConstantPool,
) -> Option<&'constant_pool Utf8> {
constant_pool.get_utf8(self.name_index)
}
pub fn typ<'constant, 'constant_pool: 'constant>(
&'constant self,
constant_pool: &'constant_pool ConstantPool,
) -> Option<&'constant_pool Utf8> {
constant_pool.get_utf8(self.type_index)
}
}
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub struct MethodHandle {
pub reference_kind: u8,
pub reference_index: u16,
}
impl MethodHandle {
pub fn reference_kind(&self) -> NodeResResult<RefKind> {
RefKind::try_from(self.reference_kind).map_err(|_| {
NodeResError::MatchOutOfBound(
"reference kind",
vec!["1..=9"],
self.reference_kind as usize,
)
})
}
pub fn reference_constant<'constant, 'constant_pool: 'constant>(
&'constant self,
constant_pool: &'constant_pool ConstantPool,
) -> NodeResResult<Option<&'constant_pool Constant>> {
if let Some(constant) = constant_pool.get_constant(self.reference_index) {
match self.reference_kind()? {
RefKind::GetField | RefKind::GetStatic | RefKind::PutField | RefKind::PutStatic => {
if let Constant::FieldRef(_) = constant {
Ok(Some(constant))
} else {
Err(NodeResError::MismatchReferenceConstant(
"FieldRef",
self.reference_index,
constant.name(),
))
}
}
RefKind::InvokeVirtual | RefKind::NewInvokeSpecial => {
if let Constant::MethodRef(_) = constant {
Ok(Some(constant))
} else {
Err(NodeResError::MismatchReferenceConstant(
"MethodRef",
self.reference_index,
constant.name(),
))
}
}
RefKind::InvokeStatic | RefKind::InvokeSpecial => {
if matches!(
constant,
Constant::MethodRef(_) | Constant::InterfaceMethodRef(_)
) {
Ok(Some(constant))
} else {
Err(NodeResError::MismatchReferenceConstant(
"MethodRef or constant InterfaceMethodRef",
self.reference_index,
constant.name(),
))
}
}
RefKind::InvokeInterface => {
if let Constant::InterfaceMethodRef(_) = constant {
Ok(Some(constant))
} else {
Err(NodeResError::MismatchReferenceConstant(
"InterfaceMethodRef",
self.reference_index,
constant.name(),
))
}
}
}
} else {
Ok(None)
}
}
}
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub struct MethodType {
pub descriptor_index: u16,
}
impl MethodType {
pub fn descriptor<'constant, 'constant_pool: 'constant>(
&'constant self,
constant_pool: &'constant_pool ConstantPool,
) -> Option<&'constant_pool Utf8> {
constant_pool.get_utf8(self.descriptor_index)
}
}
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub struct Dynamic {
pub bootstrap_method_attr_index: u16,
pub name_and_type_index: u16,
}
impl Dynamic {
pub fn bootstrap_method<'bootstrap_method, 'attributes: 'bootstrap_method>(
&self,
attribute_infos: &'attributes Vec<AttributeInfo>,
) -> Option<&'bootstrap_method BootstrapMethod> {
let bootstrap_methods = if let Some(AttributeInfo {
attribute:
Some(Attribute::BootstrapMethods(BootstrapMethods {
bootstrap_methods, ..
})),
..
}) = attribute_infos.iter().find(|attribute_info| {
matches!(
attribute_info.attribute,
Some(Attribute::BootstrapMethods { .. })
)
}) {
bootstrap_methods
} else {
return None;
};
bootstrap_methods.get(self.bootstrap_method_attr_index as usize)
}
pub fn name_and_type<'constant, 'constant_pool: 'constant>(
&'constant self,
constant_pool: &'constant_pool ConstantPool,
) -> Option<&'constant_pool NameAndType> {
constant_pool.get_name_and_type(self.name_and_type_index)
}
}
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub struct InvokeDynamic {
pub bootstrap_method_attr_index: u16,
pub name_and_type_index: u16,
}
impl InvokeDynamic {
pub fn bootstrap_method<'bootstrap_method, 'attributes: 'bootstrap_method>(
&self,
attribute_infos: &'attributes Vec<AttributeInfo>,
) -> Option<&'bootstrap_method BootstrapMethod> {
let bootstrap_methods = if let Some(AttributeInfo {
attribute:
Some(Attribute::BootstrapMethods(BootstrapMethods {
bootstrap_methods, ..
})),
..
}) = attribute_infos.iter().find(|attribute_info| {
matches!(
attribute_info.attribute,
Some(Attribute::BootstrapMethods { .. })
)
}) {
bootstrap_methods
} else {
return None;
};
bootstrap_methods.get(self.bootstrap_method_attr_index as usize)
}
pub fn name_and_type<'constant, 'constant_pool: 'constant>(
&'constant self,
constant_pool: &'constant_pool ConstantPool,
) -> Option<&'constant_pool NameAndType> {
constant_pool.get_name_and_type(self.name_and_type_index)
}
}
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub struct Module {
pub name_index: u16,
}
impl Module {
pub fn name<'constant, 'constant_pool: 'constant>(
&'constant self,
constant_pool: &'constant_pool ConstantPool,
) -> Option<&'constant_pool Utf8> {
constant_pool.get_utf8(self.name_index)
}
}
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub struct Package {
pub name_index: u16,
}
impl Package {
pub fn name<'constant, 'constant_pool: 'constant>(
&'constant self,
constant_pool: &'constant_pool ConstantPool,
) -> Option<&'constant_pool Utf8> {
constant_pool.get_utf8(self.name_index)
}
}
#[repr(u8)]
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Serialize, Deserialize)]
pub enum RefKind {
GetField = 1,
GetStatic = 2,
PutField = 3,
PutStatic = 4,
InvokeVirtual = 5,
InvokeStatic = 6,
InvokeSpecial = 7,
NewInvokeSpecial = 8,
InvokeInterface = 9,
}
impl TryFrom<u8> for RefKind {
type Error = ParseError;
fn try_from(value: u8) -> Result<Self, Self::Error> {
use RefKind::*;
let ref_kind = match value {
1 => GetField,
2 => GetStatic,
3 => PutField,
4 => PutStatic,
5 => InvokeVirtual,
6 => InvokeStatic,
7 => InvokeSpecial,
8 => NewInvokeSpecial,
9 => InvokeInterface,
_ => {
return Err(ParseError::MatchOutOfBoundUsize(
"reference kind",
vec!["1..=9"],
value as usize,
))
}
};
Ok(ref_kind)
}
}