From ba8b6172426dbeb7758f0ba9cf475342c6857a3a Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Wed, 31 Jul 2024 10:13:45 -0400 Subject: [PATCH 1/7] Bump to version 0.7.1 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index f87e931..f39991f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cafebabe" -version = "0.7.0" +version = "0.7.1" authors = ["Kartikaya Gupta"] edition = "2018" license = "0BSD" From 7740d12c2a7c7f1c7744c23be499ff3c852dfa35 Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Sat, 3 Aug 2024 21:39:26 -0400 Subject: [PATCH 2/7] Disambiguate error message --- src/constant_pool.rs | 2 +- src/constant_pool/tests.rs | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/constant_pool.rs b/src/constant_pool.rs index 1bce71d..87c47fb 100644 --- a/src/constant_pool.rs +++ b/src/constant_pool.rs @@ -311,7 +311,7 @@ impl<'a> ConstantPoolEntry<'a> { if is_binary_name(x) || is_array_descriptor(x) { Ok(()) } else { - fail!("Invalid binary name") + fail!("Invalid classinfo name") } } diff --git a/src/constant_pool/tests.rs b/src/constant_pool/tests.rs index c9d519d..01303d3 100644 --- a/src/constant_pool/tests.rs +++ b/src/constant_pool/tests.rs @@ -55,7 +55,10 @@ fn test_validate_class_info() { assert_validate_passes!(ClassInfo(wrap(Utf8(Cow::from("some/package/Class"))))); assert_validate_passes!(ClassInfo(wrap(Utf8(Cow::from("[Lsome/package/Class;"))))); - assert_validate_fails!(ClassInfo(wrap(Utf8(Cow::from("")))), "Invalid binary name"); + assert_validate_fails!( + ClassInfo(wrap(Utf8(Cow::from("")))), + "Invalid classinfo name" + ); assert_validate_fails!( ClassInfo(wrap(Utf8Bytes(&[]))), "Attempting to get utf-8 data from non-utf8 constant pool entry!" From 81e04470fef818d770450a3835380033e3d61371 Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Sun, 11 Aug 2024 14:02:50 -0400 Subject: [PATCH 3/7] Add example with loop --- examples/loop.rs | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 examples/loop.rs diff --git a/examples/loop.rs b/examples/loop.rs new file mode 100644 index 0000000..a789a75 --- /dev/null +++ b/examples/loop.rs @@ -0,0 +1,31 @@ +use std::env; +use std::fs::File; +use std::io::Read; + +fn main() { + let mut class_data = Vec::new(); + for arg in env::args().skip(1) { + let mut file = File::open(&arg).unwrap(); + let mut bytes = Vec::new(); + file.read_to_end(&mut bytes).unwrap(); + class_data.push(bytes); + } + + let results = { + let mut parsed = Vec::with_capacity(class_data.len()); + for data in &class_data { + parsed.push(cafebabe::parse_class_with_options( + &data, + cafebabe::ParseOptions::default().parse_bytecode(true), + )); + } + parsed + }; + + for result in results { + match result { + Ok(class) => println!("Successfully parsed {:?}\n{:#?}", class.this_class, class), + Err(e) => println!("Error: {}", e), + } + } +} From fa8f542e111c2b8db18ccd07908876c53f45d27d Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Sun, 11 Aug 2024 15:42:37 -0400 Subject: [PATCH 4/7] Introduce CafeRc This is a layer of abstraction around the usage of Rc. This layer will allow swapping in Arc instead of Rc conditionally based on feature flags. --- src/attributes.rs | 43 ++++++++++++------------ src/bytecode.rs | 7 ++-- src/constant_pool.rs | 69 +++++++++++++++++++------------------- src/constant_pool/tests.rs | 4 +-- src/lib.rs | 12 ++++--- 5 files changed, 67 insertions(+), 68 deletions(-) diff --git a/src/attributes.rs b/src/attributes.rs index 468d43b..a8d1e5a 100644 --- a/src/attributes.rs +++ b/src/attributes.rs @@ -1,6 +1,5 @@ use std::borrow::Cow; use std::ops::Deref; -use std::rc::Rc; use crate::bytecode::ByteCode; use crate::constant_pool::{ @@ -14,7 +13,7 @@ use crate::constant_pool::{ }; use crate::descriptor::FieldType; use crate::names::{is_return_descriptor, is_unqualified_name}; -use crate::{read_u1, read_u2, read_u4, AccessFlags, ParseError, ParseOptions}; +use crate::{read_u1, read_u2, read_u4, AccessFlags, CafeRc, ParseError, ParseOptions}; #[derive(Debug)] pub struct ExceptionTableEntry<'a> { @@ -368,7 +367,7 @@ fn ensure_length(length: usize, expected: usize) -> Result<(), ParseError> { fn read_code_data<'a>( bytes: &'a [u8], ix: &mut usize, - pool: &[Rc>], + pool: &[CafeRc>], opts: &ParseOptions, ) -> Result, ParseError> { let max_stack = read_u2(bytes, ix)?; @@ -417,7 +416,7 @@ fn read_code_data<'a>( fn read_stackmaptable_verification<'a>( bytes: &'a [u8], ix: &mut usize, - pool: &[Rc>], + pool: &[CafeRc>], ) -> Result, ParseError> { let verification_type = match read_u1(bytes, ix)? { 0 => VerificationType::Top, @@ -444,7 +443,7 @@ fn read_stackmaptable_verification<'a>( fn read_stackmaptable_data<'a>( bytes: &'a [u8], ix: &mut usize, - pool: &[Rc>], + pool: &[CafeRc>], ) -> Result>, ParseError> { let count = read_u2(bytes, ix)?; let mut stackmapframes = Vec::with_capacity(count.into()); @@ -537,7 +536,7 @@ fn read_stackmaptable_data<'a>( fn read_exceptions_data<'a>( bytes: &'a [u8], ix: &mut usize, - pool: &[Rc>], + pool: &[CafeRc>], ) -> Result>, ParseError> { let count = read_u2(bytes, ix)?; let mut exceptions = Vec::with_capacity(count.into()); @@ -552,7 +551,7 @@ fn read_exceptions_data<'a>( fn read_innerclasses_data<'a>( bytes: &'a [u8], ix: &mut usize, - pool: &[Rc>], + pool: &[CafeRc>], ) -> Result>, ParseError> { let count = read_u2(bytes, ix)?; let mut innerclasses = Vec::with_capacity(count.into()); @@ -591,7 +590,7 @@ fn read_linenumber_data(bytes: &[u8], ix: &mut usize) -> Result( bytes: &'a [u8], ix: &mut usize, - pool: &[Rc>], + pool: &[CafeRc>], ) -> Result>, ParseError> { let count = read_u2(bytes, ix)?; let mut localvariables = Vec::with_capacity(count.into()); @@ -620,7 +619,7 @@ fn read_localvariable_data<'a>( fn read_localvariabletype_data<'a>( bytes: &'a [u8], ix: &mut usize, - pool: &[Rc>], + pool: &[CafeRc>], ) -> Result>, ParseError> { let count = read_u2(bytes, ix)?; let mut localvariabletypes = Vec::with_capacity(count.into()); @@ -648,7 +647,7 @@ fn read_localvariabletype_data<'a>( fn read_annotation_element_value<'a>( bytes: &'a [u8], ix: &mut usize, - pool: &[Rc>], + pool: &[CafeRc>], ) -> Result, ParseError> { let value = match read_u1(bytes, ix)? as char { 'B' => AnnotationElementValue::ByteConstant(read_cp_integer(bytes, ix, pool)?), @@ -697,7 +696,7 @@ fn read_annotation_element_value<'a>( fn read_annotation<'a>( bytes: &'a [u8], ix: &mut usize, - pool: &[Rc>], + pool: &[CafeRc>], ) -> Result, ParseError> { let type_descriptor = read_cp_utf8(bytes, ix, pool) .and_then(|descriptor| FieldType::parse(&descriptor)) @@ -719,7 +718,7 @@ fn read_annotation<'a>( fn read_annotation_data<'a>( bytes: &'a [u8], ix: &mut usize, - pool: &[Rc>], + pool: &[CafeRc>], ) -> Result>, ParseError> { let count = read_u2(bytes, ix)?; let mut annotations = Vec::with_capacity(count.into()); @@ -733,7 +732,7 @@ fn read_annotation_data<'a>( fn read_parameter_annotation_data<'a>( bytes: &'a [u8], ix: &mut usize, - pool: &[Rc>], + pool: &[CafeRc>], ) -> Result>, ParseError> { let count = read_u1(bytes, ix)?; let mut parameters = Vec::with_capacity(count.into()); @@ -754,7 +753,7 @@ fn read_parameter_annotation_data<'a>( fn read_type_annotation_data<'a>( bytes: &'a [u8], ix: &mut usize, - pool: &[Rc>], + pool: &[CafeRc>], ) -> Result>, ParseError> { let count = read_u2(bytes, ix)?; let mut annotations = Vec::with_capacity(count.into()); @@ -840,7 +839,7 @@ fn read_type_annotation_data<'a>( fn read_bootstrapmethods_data<'a>( bytes: &'a [u8], ix: &mut usize, - pool: &[Rc>], + pool: &[CafeRc>], ) -> Result>, ParseError> { let count = read_u2(bytes, ix)?; let mut bootstrapmethods = Vec::with_capacity(count.into()); @@ -862,7 +861,7 @@ fn read_bootstrapmethods_data<'a>( fn read_methodparameters_data<'a>( bytes: &'a [u8], ix: &mut usize, - pool: &[Rc>], + pool: &[CafeRc>], ) -> Result>, ParseError> { let count = read_u1(bytes, ix)?; let mut methodparameters = Vec::with_capacity(count.into()); @@ -882,7 +881,7 @@ fn read_methodparameters_data<'a>( fn read_module_data<'a>( bytes: &'a [u8], ix: &mut usize, - pool: &[Rc>], + pool: &[CafeRc>], ) -> Result, ParseError> { let name = read_cp_moduleinfo(bytes, ix, pool).map_err(|e| err!(e, "name"))?; let access_flags = ModuleAccessFlags::from_bits(read_u2(bytes, ix)?) @@ -983,7 +982,7 @@ fn read_module_data<'a>( fn read_modulepackages_data<'a>( bytes: &'a [u8], ix: &mut usize, - pool: &[Rc>], + pool: &[CafeRc>], ) -> Result>, ParseError> { let count = read_u2(bytes, ix)?; let mut packages = Vec::with_capacity(count.into()); @@ -997,7 +996,7 @@ fn read_modulepackages_data<'a>( fn read_nestmembers_data<'a>( bytes: &'a [u8], ix: &mut usize, - pool: &[Rc>], + pool: &[CafeRc>], ) -> Result>, ParseError> { let count = read_u2(bytes, ix)?; let mut members = Vec::with_capacity(count.into()); @@ -1010,7 +1009,7 @@ fn read_nestmembers_data<'a>( fn read_permitted_subclasses_data<'a>( bytes: &'a [u8], ix: &mut usize, - pool: &[Rc>], + pool: &[CafeRc>], ) -> Result>, ParseError> { let count = read_u2(bytes, ix)?; let mut permitted_subclasses = Vec::with_capacity(count.into()); @@ -1025,7 +1024,7 @@ fn read_permitted_subclasses_data<'a>( fn read_record_data<'a>( bytes: &'a [u8], ix: &mut usize, - pool: &[Rc>], + pool: &[CafeRc>], opts: &ParseOptions, ) -> Result>, ParseError> { let count = read_u2(bytes, ix)?; @@ -1052,7 +1051,7 @@ fn read_record_data<'a>( pub(crate) fn read_attributes<'a>( bytes: &'a [u8], ix: &mut usize, - pool: &[Rc>], + pool: &[CafeRc>], opts: &ParseOptions, ) -> Result>, ParseError> { let count = read_u2(bytes, ix)?; diff --git a/src/bytecode.rs b/src/bytecode.rs index f4355ad..b9d67e1 100644 --- a/src/bytecode.rs +++ b/src/bytecode.rs @@ -1,6 +1,5 @@ use std::borrow::Cow; use std::convert::TryFrom; -use std::rc::Rc; use crate::constant_pool::{ get_cp_loadable, read_cp_classinfo, read_cp_invokedynamic, read_cp_memberref, @@ -8,7 +7,7 @@ use crate::constant_pool::{ use crate::constant_pool::{ ConstantPoolEntry, ConstantPoolEntryTypes, InvokeDynamic, Loadable, MemberRef, }; -use crate::{read_u1, read_u2, read_u4, ParseError}; +use crate::{read_u1, read_u2, read_u4, CafeRc, ParseError}; pub type JumpOffset = i32; @@ -216,7 +215,7 @@ pub struct ByteCode<'a> { impl<'a> ByteCode<'a> { pub(crate) fn from( code: &'a [u8], - pool: &[Rc>], + pool: &[CafeRc>], ) -> Result { let bytecode = Self { opcodes: read_opcodes(code, pool)?, @@ -311,7 +310,7 @@ impl<'a> ByteCode<'a> { fn read_opcodes<'a>( code: &'a [u8], - pool: &[Rc>], + pool: &[CafeRc>], ) -> Result)>, ParseError> { let mut opcodes = Vec::new(); let mut ix = 0; diff --git a/src/constant_pool.rs b/src/constant_pool.rs index 87c47fb..d6eecb1 100644 --- a/src/constant_pool.rs +++ b/src/constant_pool.rs @@ -1,25 +1,24 @@ use std::borrow::Cow; use std::cell::RefCell; use std::ops::Deref; -use std::rc::Rc; use crate::names::{ is_array_descriptor, is_binary_name, is_field_descriptor, is_method_descriptor, is_module_name, is_unqualified_method_name, is_unqualified_name, }; -use crate::{read_u1, read_u2, read_u4, read_u8, ParseError}; +use crate::{read_u1, read_u2, read_u4, read_u8, CafeRc, ParseError}; #[derive(Debug)] pub(crate) enum ConstantPoolRef<'a> { Unresolved(u16), - Resolved(Rc>), + Resolved(CafeRc>), } impl<'a> ConstantPoolRef<'a> { fn resolve( &mut self, my_index: usize, - pool: &[Rc>], + pool: &[CafeRc>], ) -> Result<(), ParseError> { match self { ConstantPoolRef::Unresolved(ix) => { @@ -41,7 +40,7 @@ impl<'a> ConstantPoolRef<'a> { } } - fn get(&self) -> &Rc> { + fn get(&self) -> &CafeRc> { match self { ConstantPoolRef::Unresolved(_) => panic!("Called get on a unresolved ConstantPoolRef"), ConstantPoolRef::Resolved(target) => target, @@ -53,7 +52,7 @@ trait RefCellDeref<'a> { fn resolve( &self, cp_index: usize, - pool: &[Rc>], + pool: &[CafeRc>], ) -> Result<(), ParseError>; fn ensure_type(&self, allowed: ConstantPoolEntryTypes) -> Result<(), ParseError>; } @@ -62,7 +61,7 @@ impl<'a> RefCellDeref<'a> for RefCell> { fn resolve( &self, cp_index: usize, - pool: &[Rc>], + pool: &[CafeRc>], ) -> Result<(), ParseError> { self.borrow_mut().resolve(cp_index, pool) } @@ -144,7 +143,7 @@ impl<'a> ConstantPoolEntry<'a> { fn resolve( &self, my_index: usize, - pool: &[Rc>], + pool: &[CafeRc>], ) -> Result<(), ParseError> { match self { // Entry types that do not reference other entries: @@ -602,7 +601,7 @@ fn read_constant_package<'a>( Ok(ConstantPoolEntry::PackageInfo(name_ref)) } -fn resolve_constant_pool(constant_pool: &[Rc]) -> Result<(), ParseError> { +fn resolve_constant_pool(constant_pool: &[CafeRc]) -> Result<(), ParseError> { for (i, cp_entry) in constant_pool.iter().enumerate() { cp_entry.resolve(i, constant_pool)?; } @@ -610,7 +609,7 @@ fn resolve_constant_pool(constant_pool: &[Rc]) -> Result<(), } fn validate_constant_pool( - constant_pool: &[Rc], + constant_pool: &[CafeRc], major_version: u16, ) -> Result<(), ParseError> { for (i, cp_entry) in constant_pool.iter().enumerate() { @@ -625,14 +624,14 @@ pub(crate) fn read_constant_pool<'a>( bytes: &'a [u8], ix: &mut usize, major_version: u16, -) -> Result>>, ParseError> { +) -> Result>>, ParseError> { let count = read_u2(bytes, ix)?; let mut constant_pool = Vec::with_capacity(count.into()); - constant_pool.push(Rc::new(ConstantPoolEntry::Zero)); + constant_pool.push(CafeRc::new(ConstantPoolEntry::Zero)); let mut cp_ix = 1; while cp_ix < count { let constant_type = read_u1(bytes, ix)?; - constant_pool.push(Rc::new(match constant_type { + constant_pool.push(CafeRc::new(match constant_type { 1 => read_constant_utf8(bytes, ix)?, 3 => read_constant_integer(bytes, ix)?, 4 => read_constant_float(bytes, ix)?, @@ -662,7 +661,7 @@ pub(crate) fn read_constant_pool<'a>( // long and double types take up two entries in the constant pool, // so eat up another index. cp_ix += 1; - constant_pool.push(Rc::new(ConstantPoolEntry::Unused)); + constant_pool.push(CafeRc::new(ConstantPoolEntry::Unused)); } } resolve_constant_pool(&constant_pool)?; @@ -673,8 +672,8 @@ pub(crate) fn read_constant_pool<'a>( fn read_cp_ref_any<'a>( bytes: &'a [u8], ix: &mut usize, - pool: &[Rc>], -) -> Result>, ParseError> { + pool: &[CafeRc>], +) -> Result>, ParseError> { let cp_index = read_u2(bytes, ix)? as usize; if cp_index >= pool.len() { fail!( @@ -688,7 +687,7 @@ fn read_cp_ref_any<'a>( pub(crate) fn read_cp_utf8<'a>( bytes: &'a [u8], ix: &mut usize, - pool: &[Rc>], + pool: &[CafeRc>], ) -> Result, ParseError> { let cp_ref = read_cp_ref_any(bytes, ix, pool)?; match cp_ref.deref() { @@ -700,7 +699,7 @@ pub(crate) fn read_cp_utf8<'a>( pub(crate) fn read_cp_utf8_opt<'a>( bytes: &'a [u8], ix: &mut usize, - pool: &[Rc>], + pool: &[CafeRc>], ) -> Result>, ParseError> { let cp_ref = read_cp_ref_any(bytes, ix, pool)?; match cp_ref.deref() { @@ -713,7 +712,7 @@ pub(crate) fn read_cp_utf8_opt<'a>( pub(crate) fn read_cp_classinfo<'a>( bytes: &'a [u8], ix: &mut usize, - pool: &[Rc>], + pool: &[CafeRc>], ) -> Result, ParseError> { let cp_ref = read_cp_ref_any(bytes, ix, pool)?; match cp_ref.deref() { @@ -725,7 +724,7 @@ pub(crate) fn read_cp_classinfo<'a>( pub(crate) fn read_cp_classinfo_opt<'a>( bytes: &'a [u8], ix: &mut usize, - pool: &[Rc>], + pool: &[CafeRc>], ) -> Result>, ParseError> { let cp_ref = read_cp_ref_any(bytes, ix, pool)?; match cp_ref.deref() { @@ -738,7 +737,7 @@ pub(crate) fn read_cp_classinfo_opt<'a>( pub(crate) fn read_cp_moduleinfo<'a>( bytes: &'a [u8], ix: &mut usize, - pool: &[Rc>], + pool: &[CafeRc>], ) -> Result, ParseError> { let cp_ref = read_cp_ref_any(bytes, ix, pool)?; match cp_ref.deref() { @@ -750,7 +749,7 @@ pub(crate) fn read_cp_moduleinfo<'a>( pub(crate) fn read_cp_packageinfo<'a>( bytes: &'a [u8], ix: &mut usize, - pool: &[Rc>], + pool: &[CafeRc>], ) -> Result, ParseError> { let cp_ref = read_cp_ref_any(bytes, ix, pool)?; match cp_ref.deref() { @@ -768,7 +767,7 @@ pub struct NameAndType<'a> { pub(crate) fn read_cp_nameandtype_opt<'a>( bytes: &'a [u8], ix: &mut usize, - pool: &[Rc>], + pool: &[CafeRc>], ) -> Result>, ParseError> { let cp_ref = read_cp_ref_any(bytes, ix, pool)?; match cp_ref.deref() { @@ -794,7 +793,7 @@ pub enum LiteralConstant<'a> { pub(crate) fn read_cp_literalconstant<'a>( bytes: &'a [u8], ix: &mut usize, - pool: &[Rc>], + pool: &[CafeRc>], ) -> Result, ParseError> { let cp_ref = read_cp_ref_any(bytes, ix, pool)?; match cp_ref.deref() { @@ -810,7 +809,7 @@ pub(crate) fn read_cp_literalconstant<'a>( pub(crate) fn read_cp_integer<'a>( bytes: &'a [u8], ix: &mut usize, - pool: &[Rc>], + pool: &[CafeRc>], ) -> Result { let cp_ref = read_cp_ref_any(bytes, ix, pool)?; match cp_ref.deref() { @@ -822,7 +821,7 @@ pub(crate) fn read_cp_integer<'a>( pub(crate) fn read_cp_float<'a>( bytes: &'a [u8], ix: &mut usize, - pool: &[Rc>], + pool: &[CafeRc>], ) -> Result { let cp_ref = read_cp_ref_any(bytes, ix, pool)?; match cp_ref.deref() { @@ -834,7 +833,7 @@ pub(crate) fn read_cp_float<'a>( pub(crate) fn read_cp_long<'a>( bytes: &'a [u8], ix: &mut usize, - pool: &[Rc>], + pool: &[CafeRc>], ) -> Result { let cp_ref = read_cp_ref_any(bytes, ix, pool)?; match cp_ref.deref() { @@ -846,7 +845,7 @@ pub(crate) fn read_cp_long<'a>( pub(crate) fn read_cp_double<'a>( bytes: &'a [u8], ix: &mut usize, - pool: &[Rc>], + pool: &[CafeRc>], ) -> Result { let cp_ref = read_cp_ref_any(bytes, ix, pool)?; match cp_ref.deref() { @@ -864,7 +863,7 @@ pub struct MemberRef<'a> { pub(crate) fn read_cp_memberref<'a>( bytes: &'a [u8], ix: &mut usize, - pool: &[Rc>], + pool: &[CafeRc>], allowed: ConstantPoolEntryTypes, ) -> Result, ParseError> { let cp_ref = read_cp_ref_any(bytes, ix, pool)?; @@ -891,7 +890,7 @@ pub struct InvokeDynamic<'a> { pub(crate) fn read_cp_invokedynamic<'a>( bytes: &'a [u8], ix: &mut usize, - pool: &[Rc>], + pool: &[CafeRc>], ) -> Result, ParseError> { let cp_ref = read_cp_ref_any(bytes, ix, pool)?; match cp_ref.deref() { @@ -920,7 +919,7 @@ pub enum Loadable<'a> { pub(crate) fn get_cp_loadable<'a>( cp_index: usize, - pool: &[Rc>], + pool: &[CafeRc>], ) -> Result, ParseError> { if cp_index >= pool.len() { fail!( @@ -999,7 +998,7 @@ fn make_method_handle<'a>( pub(crate) fn read_cp_methodhandle<'a>( bytes: &'a [u8], ix: &mut usize, - pool: &[Rc>], + pool: &[CafeRc>], ) -> Result, ParseError> { let cp_ref = read_cp_ref_any(bytes, ix, pool)?; match cp_ref.deref() { @@ -1019,7 +1018,7 @@ pub enum BootstrapArgument<'a> { pub(crate) fn read_cp_bootstrap_argument<'a>( bytes: &'a [u8], ix: &mut usize, - pool: &[Rc>], + pool: &[CafeRc>], ) -> Result, ParseError> { let cp_ref = read_cp_ref_any(bytes, ix, pool)?; match cp_ref.deref() { @@ -1068,12 +1067,12 @@ pub enum ConstantPoolItem<'a> { } pub struct ConstantPoolIter<'a> { - constant_pool: &'a [Rc>], + constant_pool: &'a [CafeRc>], index: usize, } impl<'a> ConstantPoolIter<'a> { - pub(crate) fn new(constant_pool: &'a [Rc>]) -> Self { + pub(crate) fn new(constant_pool: &'a [CafeRc>]) -> Self { ConstantPoolIter { constant_pool, index: 0, diff --git a/src/constant_pool/tests.rs b/src/constant_pool/tests.rs index 01303d3..53ec0e7 100644 --- a/src/constant_pool/tests.rs +++ b/src/constant_pool/tests.rs @@ -33,9 +33,9 @@ macro_rules! assert_validate_fails { } // Helper for creating the smart pointer types (RefCell, ConstantPoolRef, -// Rc) required to nest ConstantPoolEntry instances. +// CafeRc) required to nest ConstantPoolEntry instances. fn wrap(entry: ConstantPoolEntry) -> RefCell { - RefCell::new(ConstantPoolRef::Resolved(Rc::new(entry))) + RefCell::new(ConstantPoolRef::Resolved(CafeRc::new(entry))) } #[test] diff --git a/src/lib.rs b/src/lib.rs index 05094bd..03a8e6c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -29,6 +29,8 @@ use crate::descriptor::{FieldType, MethodDescriptor, ReturnDescriptor}; pub use crate::error::ParseError; use crate::names::{is_unqualified_method_name, is_unqualified_name}; +pub(crate) type CafeRc = Rc; + pub(crate) fn read_u1(bytes: &[u8], ix: &mut usize) -> Result { if bytes.len() < *ix + 1 { fail!("Unexpected end of stream reading u1 at index {}", *ix); @@ -81,7 +83,7 @@ pub(crate) fn read_u8(bytes: &[u8], ix: &mut usize) -> Result { fn read_interfaces<'a>( bytes: &'a [u8], ix: &mut usize, - pool: &[Rc>], + pool: &[CafeRc>], ) -> Result>, ParseError> { let count = read_u2(bytes, ix)?; let mut interfaces = Vec::with_capacity(count.into()); @@ -145,7 +147,7 @@ pub struct FieldInfo<'a> { fn read_fields<'a>( bytes: &'a [u8], ix: &mut usize, - pool: &[Rc>], + pool: &[CafeRc>], opts: &ParseOptions, ) -> Result>, ParseError> { let count = read_u2(bytes, ix)?; @@ -209,7 +211,7 @@ pub struct MethodInfo<'a> { fn read_methods<'a>( bytes: &'a [u8], ix: &mut usize, - pool: &[Rc>], + pool: &[CafeRc>], opts: &ParseOptions, in_interface: bool, major_version: u16, @@ -274,7 +276,7 @@ bitflags! { } fn validate_bootstrap_methods<'a>( - pool: &[Rc>], + pool: &[CafeRc>], attributes: &[AttributeInfo<'a>], ) -> Result<(), ParseError> { for cp_entry in pool { @@ -306,7 +308,7 @@ fn validate_bootstrap_methods<'a>( pub struct ClassFile<'a> { pub major_version: u16, pub minor_version: u16, - constant_pool: Vec>>, + constant_pool: Vec>>, pub access_flags: ClassAccessFlags, pub this_class: Cow<'a, str>, pub super_class: Option>, From 2c6c2cac4adc2ad3ea919e3e8868d5e886b4108f Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Sun, 11 Aug 2024 15:47:36 -0400 Subject: [PATCH 5/7] Introduce CafeCell An abstraction over RefCell to allow swapping in something threadsafe --- src/constant_pool.rs | 36 +++++++++++++++++++----------------- src/constant_pool/tests.rs | 6 +++--- 2 files changed, 22 insertions(+), 20 deletions(-) diff --git a/src/constant_pool.rs b/src/constant_pool.rs index d6eecb1..1a89e6b 100644 --- a/src/constant_pool.rs +++ b/src/constant_pool.rs @@ -8,6 +8,8 @@ use crate::names::{ }; use crate::{read_u1, read_u2, read_u4, read_u8, CafeRc, ParseError}; +type CafeCell = RefCell; + #[derive(Debug)] pub(crate) enum ConstantPoolRef<'a> { Unresolved(u16), @@ -48,7 +50,7 @@ impl<'a> ConstantPoolRef<'a> { } } -trait RefCellDeref<'a> { +trait CafeCellDeref<'a> { fn resolve( &self, cp_index: usize, @@ -57,7 +59,7 @@ trait RefCellDeref<'a> { fn ensure_type(&self, allowed: ConstantPoolEntryTypes) -> Result<(), ParseError>; } -impl<'a> RefCellDeref<'a> for RefCell> { +impl<'a> CafeCellDeref<'a> for CafeCell> { fn resolve( &self, cp_index: usize, @@ -124,18 +126,18 @@ pub(crate) enum ConstantPoolEntry<'a> { Float(f32), Long(i64), Double(f64), - ClassInfo(RefCell>), - String(RefCell>), - FieldRef(RefCell>, RefCell>), - MethodRef(RefCell>, RefCell>), - InterfaceMethodRef(RefCell>, RefCell>), - NameAndType(RefCell>, RefCell>), - MethodHandle(ReferenceKind, RefCell>), - MethodType(RefCell>), - Dynamic(BootstrapMethodRef, RefCell>), - InvokeDynamic(BootstrapMethodRef, RefCell>), - ModuleInfo(RefCell>), - PackageInfo(RefCell>), + ClassInfo(CafeCell>), + String(CafeCell>), + FieldRef(CafeCell>, CafeCell>), + MethodRef(CafeCell>, CafeCell>), + InterfaceMethodRef(CafeCell>, CafeCell>), + NameAndType(CafeCell>, CafeCell>), + MethodHandle(ReferenceKind, CafeCell>), + MethodType(CafeCell>), + Dynamic(BootstrapMethodRef, CafeCell>), + InvokeDynamic(BootstrapMethodRef, CafeCell>), + ModuleInfo(CafeCell>), + PackageInfo(CafeCell>), Unused, } @@ -403,8 +405,8 @@ impl<'a> ConstantPoolEntry<'a> { fn read_unresolved_cp_ref<'a>( bytes: &'a [u8], ix: &mut usize, -) -> Result>, ParseError> { - Ok(RefCell::new(ConstantPoolRef::Unresolved(read_u2( +) -> Result>, ParseError> { + Ok(CafeCell::new(ConstantPoolRef::Unresolved(read_u2( bytes, ix, )?))) } @@ -967,7 +969,7 @@ pub struct MethodHandle<'a> { fn make_method_handle<'a>( x: &ReferenceKind, - y: &RefCell>, + y: &CafeCell>, ) -> Result, ParseError> { let (class_name, member_kind, member_ref) = match y.borrow().get().deref() { ConstantPoolEntry::FieldRef(c, m) => ( diff --git a/src/constant_pool/tests.rs b/src/constant_pool/tests.rs index 53ec0e7..4a9fc1e 100644 --- a/src/constant_pool/tests.rs +++ b/src/constant_pool/tests.rs @@ -32,10 +32,10 @@ macro_rules! assert_validate_fails { }; } -// Helper for creating the smart pointer types (RefCell, ConstantPoolRef, +// Helper for creating the smart pointer types (CafeCell, ConstantPoolRef, // CafeRc) required to nest ConstantPoolEntry instances. -fn wrap(entry: ConstantPoolEntry) -> RefCell { - RefCell::new(ConstantPoolRef::Resolved(CafeRc::new(entry))) +fn wrap(entry: ConstantPoolEntry) -> CafeCell { + CafeCell::new(ConstantPoolRef::Resolved(CafeRc::new(entry))) } #[test] From a94ea41717fd23038c72a7c960ec92a6457c6f30 Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Sun, 11 Aug 2024 15:49:48 -0400 Subject: [PATCH 6/7] Introduce peel macro This abstracts the repeated .borrow().get() call that is used in a bunch of places and will need to be swapped for something else in threadsafe mode. --- src/constant_pool.rs | 130 ++++++++++++++++++++----------------------- 1 file changed, 61 insertions(+), 69 deletions(-) diff --git a/src/constant_pool.rs b/src/constant_pool.rs index 1a89e6b..28e7f69 100644 --- a/src/constant_pool.rs +++ b/src/constant_pool.rs @@ -50,6 +50,12 @@ impl<'a> ConstantPoolRef<'a> { } } +macro_rules! peel { + ($x:expr) => { + $x.borrow().get() + }; +} + trait CafeCellDeref<'a> { fn resolve( &self, @@ -69,7 +75,7 @@ impl<'a> CafeCellDeref<'a> for CafeCell> { } fn ensure_type(&self, allowed: ConstantPoolEntryTypes) -> Result<(), ParseError> { - self.borrow().get().ensure_type(allowed) + peel!(self).ensure_type(allowed) } } @@ -211,27 +217,27 @@ impl<'a> ConstantPoolEntry<'a> { match self { ConstantPoolEntry::ClassInfo(x) => { x.ensure_type(ConstantPoolEntryTypes::UTF8)?; - x.borrow().get().validate_classinfo_name() + peel!(x).validate_classinfo_name() } ConstantPoolEntry::String(x) => x.ensure_type(ConstantPoolEntryTypes::UTF8), ConstantPoolEntry::FieldRef(x, y) => { x.ensure_type(ConstantPoolEntryTypes::CLASS_INFO)?; y.ensure_type(ConstantPoolEntryTypes::NAME_AND_TYPE)?; - y.borrow().get().validate_field_descriptor() + peel!(y).validate_field_descriptor() } ConstantPoolEntry::MethodRef(x, y) => { x.ensure_type(ConstantPoolEntryTypes::CLASS_INFO)?; y.ensure_type(ConstantPoolEntryTypes::NAME_AND_TYPE)?; - y.borrow().get().validate_method_descriptor() + peel!(y).validate_method_descriptor() } ConstantPoolEntry::InterfaceMethodRef(x, y) => { x.ensure_type(ConstantPoolEntryTypes::CLASS_INFO)?; y.ensure_type(ConstantPoolEntryTypes::NAME_AND_TYPE)?; - y.borrow().get().validate_method_descriptor() + peel!(y).validate_method_descriptor() } ConstantPoolEntry::NameAndType(x, y) => { x.ensure_type(ConstantPoolEntryTypes::UTF8)?; - x.borrow().get().validate_unqualified_name()?; + peel!(x).validate_unqualified_name()?; y.ensure_type(ConstantPoolEntryTypes::UTF8) // y is validated as part of FieldRef/MethodRef/InterfaceMethodRef/Dynamic/InvokeDynamic pool item validation } @@ -254,23 +260,23 @@ impl<'a> ConstantPoolEntry<'a> { }), ConstantPoolEntry::MethodType(x) => { x.ensure_type(ConstantPoolEntryTypes::UTF8)?; - x.borrow().get().validate_method_descriptor() + peel!(x).validate_method_descriptor() } ConstantPoolEntry::Dynamic(_, y) => { y.ensure_type(ConstantPoolEntryTypes::NAME_AND_TYPE)?; - y.borrow().get().validate_field_descriptor() + peel!(y).validate_field_descriptor() } ConstantPoolEntry::InvokeDynamic(_, y) => { y.ensure_type(ConstantPoolEntryTypes::NAME_AND_TYPE)?; - y.borrow().get().validate_method_descriptor() + peel!(y).validate_method_descriptor() } ConstantPoolEntry::ModuleInfo(x) => { x.ensure_type(ConstantPoolEntryTypes::UTF8)?; - x.borrow().get().validate_module_name() + peel!(x).validate_module_name() } ConstantPoolEntry::PackageInfo(x) => { x.ensure_type(ConstantPoolEntryTypes::UTF8)?; - x.borrow().get().validate_binary_name() + peel!(x).validate_binary_name() } // Entry types that do not reference other entries: @@ -344,7 +350,7 @@ impl<'a> ConstantPoolEntry<'a> { fn validate_field_descriptor(&self) -> Result<(), ParseError> { match self { ConstantPoolEntry::NameAndType(_, y) => { - if is_field_descriptor(y.borrow().get().str()?) { + if is_field_descriptor(peel!(y).str()?) { Ok(()) } else { fail!("Invalid field descriptor") @@ -356,7 +362,7 @@ impl<'a> ConstantPoolEntry<'a> { fn validate_method_descriptor(&self) -> Result<(), ParseError> { match self { - ConstantPoolEntry::NameAndType(_, y) => y.borrow().get().validate_method_descriptor(), + ConstantPoolEntry::NameAndType(_, y) => peel!(y).validate_method_descriptor(), _ => { if is_method_descriptor(self.str()?) { Ok(()) @@ -384,7 +390,7 @@ impl<'a> ConstantPoolEntry<'a> { fn classinfo(&self) -> Cow<'a, str> { match self { - ConstantPoolEntry::ClassInfo(x) => x.borrow().get().utf8(), + ConstantPoolEntry::ClassInfo(x) => peel!(x).utf8(), _ => panic!("Attempting to get classinfo data from non-classinfo constant pool entry!"), } } @@ -392,8 +398,8 @@ impl<'a> ConstantPoolEntry<'a> { fn name_and_type(&self) -> NameAndType<'a> { match self { ConstantPoolEntry::NameAndType(x, y) => NameAndType { - name: x.borrow().get().utf8(), - descriptor: y.borrow().get().utf8(), + name: peel!(x).utf8(), + descriptor: peel!(y).utf8(), }, _ => panic!( "Attempting to get name and type data from non-name-and-type constant pool entry!" @@ -718,7 +724,7 @@ pub(crate) fn read_cp_classinfo<'a>( ) -> Result, ParseError> { let cp_ref = read_cp_ref_any(bytes, ix, pool)?; match cp_ref.deref() { - ConstantPoolEntry::ClassInfo(x) => Ok(x.borrow().get().utf8()), + ConstantPoolEntry::ClassInfo(x) => Ok(peel!(x).utf8()), _ => fail!("Unexpected constant pool reference type"), } } @@ -731,7 +737,7 @@ pub(crate) fn read_cp_classinfo_opt<'a>( let cp_ref = read_cp_ref_any(bytes, ix, pool)?; match cp_ref.deref() { ConstantPoolEntry::Zero => Ok(None), - ConstantPoolEntry::ClassInfo(x) => Ok(Some(x.borrow().get().utf8())), + ConstantPoolEntry::ClassInfo(x) => Ok(Some(peel!(x).utf8())), _ => fail!("Unexpected constant pool reference type"), } } @@ -743,7 +749,7 @@ pub(crate) fn read_cp_moduleinfo<'a>( ) -> Result, ParseError> { let cp_ref = read_cp_ref_any(bytes, ix, pool)?; match cp_ref.deref() { - ConstantPoolEntry::ModuleInfo(x) => Ok(x.borrow().get().utf8()), + ConstantPoolEntry::ModuleInfo(x) => Ok(peel!(x).utf8()), _ => fail!("Unexpected constant pool reference type"), } } @@ -755,7 +761,7 @@ pub(crate) fn read_cp_packageinfo<'a>( ) -> Result, ParseError> { let cp_ref = read_cp_ref_any(bytes, ix, pool)?; match cp_ref.deref() { - ConstantPoolEntry::PackageInfo(x) => Ok(x.borrow().get().utf8()), + ConstantPoolEntry::PackageInfo(x) => Ok(peel!(x).utf8()), _ => fail!("Unexpected constant pool reference type"), } } @@ -775,8 +781,8 @@ pub(crate) fn read_cp_nameandtype_opt<'a>( match cp_ref.deref() { ConstantPoolEntry::Zero => Ok(None), ConstantPoolEntry::NameAndType(x, y) => Ok(Some(NameAndType { - name: x.borrow().get().utf8(), - descriptor: y.borrow().get().utf8(), + name: peel!(x).utf8(), + descriptor: peel!(y).utf8(), })), _ => fail!("Unexpected constant pool reference type"), } @@ -803,7 +809,7 @@ pub(crate) fn read_cp_literalconstant<'a>( ConstantPoolEntry::Float(v) => Ok(LiteralConstant::Float(*v)), ConstantPoolEntry::Long(v) => Ok(LiteralConstant::Long(*v)), ConstantPoolEntry::Double(v) => Ok(LiteralConstant::Double(*v)), - ConstantPoolEntry::String(v) => Ok(v.borrow().get().string_literal()), + ConstantPoolEntry::String(v) => Ok(peel!(v).string_literal()), _ => fail!("Unexpected constant pool reference type"), } } @@ -876,8 +882,8 @@ pub(crate) fn read_cp_memberref<'a>( ConstantPoolEntry::FieldRef(c, m) | ConstantPoolEntry::MethodRef(c, m) | ConstantPoolEntry::InterfaceMethodRef(c, m) => Ok(MemberRef { - class_name: c.borrow().get().classinfo(), - name_and_type: m.borrow().get().name_and_type(), + class_name: peel!(c).classinfo(), + name_and_type: peel!(m).name_and_type(), }), _ => fail!("Unexpected constant pool reference type"), } @@ -898,7 +904,7 @@ pub(crate) fn read_cp_invokedynamic<'a>( match cp_ref.deref() { ConstantPoolEntry::InvokeDynamic(x, y) => Ok(InvokeDynamic { attr_index: *x, - name_and_type: y.borrow().get().name_and_type(), + name_and_type: peel!(y).name_and_type(), }), _ => fail!("Unexpected constant pool reference type"), } @@ -936,17 +942,15 @@ pub(crate) fn get_cp_loadable<'a>( ConstantPoolEntry::Float(v) => Ok(Loadable::LiteralConstant(LiteralConstant::Float(*v))), ConstantPoolEntry::Long(v) => Ok(Loadable::LiteralConstant(LiteralConstant::Long(*v))), ConstantPoolEntry::Double(v) => Ok(Loadable::LiteralConstant(LiteralConstant::Double(*v))), - ConstantPoolEntry::String(v) => { - Ok(Loadable::LiteralConstant(v.borrow().get().string_literal())) - } - ConstantPoolEntry::ClassInfo(x) => Ok(Loadable::ClassInfo(x.borrow().get().utf8())), + ConstantPoolEntry::String(v) => Ok(Loadable::LiteralConstant(peel!(v).string_literal())), + ConstantPoolEntry::ClassInfo(x) => Ok(Loadable::ClassInfo(peel!(x).utf8())), ConstantPoolEntry::MethodHandle(x, y) => { Ok(Loadable::MethodHandle(make_method_handle(x, y)?)) } - ConstantPoolEntry::MethodType(x) => Ok(Loadable::MethodType(x.borrow().get().utf8())), + ConstantPoolEntry::MethodType(x) => Ok(Loadable::MethodType(peel!(x).utf8())), ConstantPoolEntry::Dynamic(x, y) => Ok(Loadable::Dynamic(Dynamic { attr_index: *x, - name_and_type: y.borrow().get().name_and_type(), + name_and_type: peel!(y).name_and_type(), })), _ => fail!("Unexpected non-loadable constant pool reference found"), } @@ -971,21 +975,21 @@ fn make_method_handle<'a>( x: &ReferenceKind, y: &CafeCell>, ) -> Result, ParseError> { - let (class_name, member_kind, member_ref) = match y.borrow().get().deref() { + let (class_name, member_kind, member_ref) = match peel!(y).deref() { ConstantPoolEntry::FieldRef(c, m) => ( - c.borrow().get().classinfo(), + peel!(c).classinfo(), MemberKind::Field, - m.borrow().get().name_and_type(), + peel!(m).name_and_type(), ), ConstantPoolEntry::MethodRef(c, m) => ( - c.borrow().get().classinfo(), + peel!(c).classinfo(), MemberKind::Method, - m.borrow().get().name_and_type(), + peel!(m).name_and_type(), ), ConstantPoolEntry::InterfaceMethodRef(c, m) => ( - c.borrow().get().classinfo(), + peel!(c).classinfo(), MemberKind::InterfaceMethod, - m.borrow().get().name_and_type(), + peel!(m).name_and_type(), ), _ => fail!("Unexpected constant pool reference type"), }; @@ -1037,17 +1041,13 @@ pub(crate) fn read_cp_bootstrap_argument<'a>( LiteralConstant::Double(*v), )), ConstantPoolEntry::String(v) => Ok(BootstrapArgument::LiteralConstant( - v.borrow().get().string_literal(), + peel!(v).string_literal(), )), - ConstantPoolEntry::ClassInfo(x) => { - Ok(BootstrapArgument::ClassInfo(x.borrow().get().utf8())) - } + ConstantPoolEntry::ClassInfo(x) => Ok(BootstrapArgument::ClassInfo(peel!(x).utf8())), ConstantPoolEntry::MethodHandle(x, y) => { Ok(BootstrapArgument::MethodHandle(make_method_handle(x, y)?)) } - ConstantPoolEntry::MethodType(x) => { - Ok(BootstrapArgument::MethodType(x.borrow().get().utf8())) - } + ConstantPoolEntry::MethodType(x) => Ok(BootstrapArgument::MethodType(peel!(x).utf8())), _ => fail!("Unexpected constant pool reference type"), } } @@ -1103,54 +1103,46 @@ impl<'a> Iterator for ConstantPoolIter<'a> { ConstantPoolEntry::Double(v) => { ConstantPoolItem::LiteralConstant(LiteralConstant::Double(*v)) } - ConstantPoolEntry::ClassInfo(x) => { - ConstantPoolItem::ClassInfo(x.borrow().get().utf8()) - } + ConstantPoolEntry::ClassInfo(x) => ConstantPoolItem::ClassInfo(peel!(x).utf8()), ConstantPoolEntry::String(x) => { - ConstantPoolItem::LiteralConstant(x.borrow().get().string_literal()) + ConstantPoolItem::LiteralConstant(peel!(x).string_literal()) } ConstantPoolEntry::FieldRef(c, m) => ConstantPoolItem::FieldRef(MemberRef { - class_name: c.borrow().get().classinfo(), - name_and_type: m.borrow().get().name_and_type(), + class_name: peel!(c).classinfo(), + name_and_type: peel!(m).name_and_type(), }), ConstantPoolEntry::MethodRef(c, m) => ConstantPoolItem::MethodRef(MemberRef { - class_name: c.borrow().get().classinfo(), - name_and_type: m.borrow().get().name_and_type(), + class_name: peel!(c).classinfo(), + name_and_type: peel!(m).name_and_type(), }), ConstantPoolEntry::InterfaceMethodRef(c, m) => { ConstantPoolItem::InterfaceMethodRef(MemberRef { - class_name: c.borrow().get().classinfo(), - name_and_type: m.borrow().get().name_and_type(), + class_name: peel!(c).classinfo(), + name_and_type: peel!(m).name_and_type(), }) } ConstantPoolEntry::NameAndType(x, y) => { ConstantPoolItem::NameAndType(NameAndType { - name: x.borrow().get().utf8(), - descriptor: y.borrow().get().utf8(), + name: peel!(x).utf8(), + descriptor: peel!(y).utf8(), }) } ConstantPoolEntry::MethodHandle(x, y) => { ConstantPoolItem::MethodHandle(make_method_handle(x, y).unwrap()) } - ConstantPoolEntry::MethodType(x) => { - ConstantPoolItem::MethodType(x.borrow().get().utf8()) - } + ConstantPoolEntry::MethodType(x) => ConstantPoolItem::MethodType(peel!(x).utf8()), ConstantPoolEntry::Dynamic(x, y) => ConstantPoolItem::Dynamic(Dynamic { attr_index: *x, - name_and_type: y.borrow().get().name_and_type(), + name_and_type: peel!(y).name_and_type(), }), ConstantPoolEntry::InvokeDynamic(x, y) => { ConstantPoolItem::InvokeDynamic(InvokeDynamic { attr_index: *x, - name_and_type: y.borrow().get().name_and_type(), + name_and_type: peel!(y).name_and_type(), }) } - ConstantPoolEntry::ModuleInfo(x) => { - ConstantPoolItem::ModuleInfo(x.borrow().get().utf8()) - } - ConstantPoolEntry::PackageInfo(x) => { - ConstantPoolItem::PackageInfo(x.borrow().get().utf8()) - } + ConstantPoolEntry::ModuleInfo(x) => ConstantPoolItem::ModuleInfo(peel!(x).utf8()), + ConstantPoolEntry::PackageInfo(x) => ConstantPoolItem::PackageInfo(peel!(x).utf8()), ConstantPoolEntry::Unused => continue, }; return Some(item); From 428dc024caf18ec91abb24198b8a6ad821cc23cf Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Sun, 11 Aug 2024 15:51:23 -0400 Subject: [PATCH 7/7] Introduce threadsafe feature This introduces a feature to swap thread-unsafe structures to threadsafe structures (but which are less performant). The loop example is also updated to use rayon and exercise the threadsafe feature. --- Cargo.toml | 12 ++++++++++++ examples/{loop.rs => threadsafe.rs} | 16 ++++++++-------- src/constant_pool.rs | 21 ++++++++++++++++++++- src/lib.rs | 7 +++++++ 4 files changed, 47 insertions(+), 9 deletions(-) rename examples/{loop.rs => threadsafe.rs} (73%) diff --git a/Cargo.toml b/Cargo.toml index f39991f..08def92 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,18 @@ exclude = [".gitignore", ".github/**", "examples/**", "tests/**"] [badges] maintenance = { status = "passively-maintained" } +[features] +default = [] +threadsafe = [] + [dependencies] bitflags = "1.0" cesu8 = "1.1.0" + +[dev-dependencies] +rayon = "1.10.0" + +[[example]] +name = "threadsafe" +path = "examples/threadsafe.rs" +required-features = ["threadsafe"] diff --git a/examples/loop.rs b/examples/threadsafe.rs similarity index 73% rename from examples/loop.rs rename to examples/threadsafe.rs index a789a75..5e7e133 100644 --- a/examples/loop.rs +++ b/examples/threadsafe.rs @@ -1,3 +1,4 @@ +use rayon::prelude::*; use std::env; use std::fs::File; use std::io::Read; @@ -11,16 +12,15 @@ fn main() { class_data.push(bytes); } - let results = { - let mut parsed = Vec::with_capacity(class_data.len()); - for data in &class_data { - parsed.push(cafebabe::parse_class_with_options( + let results: Vec> = class_data + .par_iter() + .map(|data| { + cafebabe::parse_class_with_options( &data, cafebabe::ParseOptions::default().parse_bytecode(true), - )); - } - parsed - }; + ) + }) + .collect(); for result in results { match result { diff --git a/src/constant_pool.rs b/src/constant_pool.rs index 28e7f69..808afba 100644 --- a/src/constant_pool.rs +++ b/src/constant_pool.rs @@ -1,6 +1,11 @@ use std::borrow::Cow; +#[cfg(not(feature = "threadsafe"))] use std::cell::RefCell; use std::ops::Deref; +#[cfg(feature = "threadsafe")] +use std::ops::DerefMut; +#[cfg(feature = "threadsafe")] +use std::sync::Mutex; use crate::names::{ is_array_descriptor, is_binary_name, is_field_descriptor, is_method_descriptor, is_module_name, @@ -8,7 +13,10 @@ use crate::names::{ }; use crate::{read_u1, read_u2, read_u4, read_u8, CafeRc, ParseError}; +#[cfg(not(feature = "threadsafe"))] type CafeCell = RefCell; +#[cfg(feature = "threadsafe")] +type CafeCell = Mutex; #[derive(Debug)] pub(crate) enum ConstantPoolRef<'a> { @@ -50,12 +58,20 @@ impl<'a> ConstantPoolRef<'a> { } } +#[cfg(not(feature = "threadsafe"))] macro_rules! peel { ($x:expr) => { $x.borrow().get() }; } +#[cfg(feature = "threadsafe")] +macro_rules! peel { + ($x:expr) => { + $x.lock().unwrap().deref().get() + }; +} + trait CafeCellDeref<'a> { fn resolve( &self, @@ -71,7 +87,10 @@ impl<'a> CafeCellDeref<'a> for CafeCell> { cp_index: usize, pool: &[CafeRc>], ) -> Result<(), ParseError> { - self.borrow_mut().resolve(cp_index, pool) + #[cfg(not(feature = "threadsafe"))] + return self.borrow_mut().resolve(cp_index, pool); + #[cfg(feature = "threadsafe")] + return self.lock().unwrap().deref_mut().resolve(cp_index, pool); } fn ensure_type(&self, allowed: ConstantPoolEntryTypes) -> Result<(), ParseError> { diff --git a/src/lib.rs b/src/lib.rs index 03a8e6c..234c6df 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,7 +18,11 @@ pub mod names; use std::borrow::Cow; use std::collections::HashSet; use std::ops::Deref; + +#[cfg(not(feature = "threadsafe"))] use std::rc::Rc; +#[cfg(feature = "threadsafe")] +use std::sync::Arc; use crate::attributes::{read_attributes, AttributeData, AttributeInfo}; use crate::constant_pool::{ @@ -29,7 +33,10 @@ use crate::descriptor::{FieldType, MethodDescriptor, ReturnDescriptor}; pub use crate::error::ParseError; use crate::names::{is_unqualified_method_name, is_unqualified_name}; +#[cfg(not(feature = "threadsafe"))] pub(crate) type CafeRc = Rc; +#[cfg(feature = "threadsafe")] +pub(crate) type CafeRc = Arc; pub(crate) fn read_u1(bytes: &[u8], ix: &mut usize) -> Result { if bytes.len() < *ix + 1 {