From 3c53082a34517577a2b76035be3a1cda778d099b Mon Sep 17 00:00:00 2001 From: wuwbobo2021 Date: Mon, 23 Jun 2025 08:55:59 +0800 Subject: [PATCH 1/3] gen: Update to cafebabe 0.9.0 --- java-spaghetti-gen/Cargo.toml | 4 +- .../src/emit_rust/class_proxy.rs | 4 +- java-spaghetti-gen/src/emit_rust/fields.rs | 14 +- .../src/emit_rust/known_docs_url.rs | 4 +- java-spaghetti-gen/src/emit_rust/methods.rs | 6 +- .../src/identifiers/method_mangling_style.rs | 20 +-- java-spaghetti-gen/src/parser_util/class.rs | 4 +- java-spaghetti-gen/src/parser_util/field.rs | 32 +--- java-spaghetti-gen/src/parser_util/id.rs | 143 ++---------------- java-spaghetti-gen/src/parser_util/method.rs | 19 +-- java-spaghetti-gen/src/parser_util/mod.rs | 4 +- 11 files changed, 41 insertions(+), 213 deletions(-) diff --git a/java-spaghetti-gen/Cargo.toml b/java-spaghetti-gen/Cargo.toml index df59fb5..64202e4 100644 --- a/java-spaghetti-gen/Cargo.toml +++ b/java-spaghetti-gen/Cargo.toml @@ -9,13 +9,13 @@ categories = ["development-tools::ffi"] license = "MIT OR Apache-2.0" [dependencies] -cafebabe = "0.8.0" +cafebabe = "0.9.0" clap = { version = "4", features = ["derive"] } bitflags = "2.8.0" serde = "1.0.197" serde_derive = "1.0.197" toml = "0.8.10" -zip = "2.2.2" +zip = "4.1.0" quote = "1.0.40" proc-macro2 = "1.0.95" anyhow = "1.0.98" diff --git a/java-spaghetti-gen/src/emit_rust/class_proxy.rs b/java-spaghetti-gen/src/emit_rust/class_proxy.rs index 78e2fdf..695d2c2 100644 --- a/java-spaghetti-gen/src/emit_rust/class_proxy.rs +++ b/java-spaghetti-gen/src/emit_rust/class_proxy.rs @@ -10,7 +10,7 @@ use super::fields::RustTypeFlavor; use super::methods::Method; use crate::emit_rust::Context; use crate::emit_rust::fields::emit_rust_type; -use crate::parser_util::{Id, emit_field_descriptor}; +use crate::parser_util::Id; impl Class { pub(crate) fn write_proxy(&self, context: &Context, methods: &[Method]) -> anyhow::Result { @@ -208,7 +208,7 @@ fn mangle_native_method(path: &str, name: &str, args: &[FieldDescriptor]) -> Str res.push_str(&mangle_native(name)); res.push_str("__"); for d in args { - res.push_str(&mangle_native(&emit_field_descriptor(d))); + res.push_str(&mangle_native(&d.to_string())); } res diff --git a/java-spaghetti-gen/src/emit_rust/fields.rs b/java-spaghetti-gen/src/emit_rust/fields.rs index 0b69dff..d924b61 100644 --- a/java-spaghetti-gen/src/emit_rust/fields.rs +++ b/java-spaghetti-gen/src/emit_rust/fields.rs @@ -8,7 +8,7 @@ use super::cstring; use super::known_docs_url::KnownDocsUrl; use crate::emit_rust::Context; use crate::identifiers::{FieldMangling, IdentifierManglingError}; -use crate::parser_util::{ClassName, IdBuf, IterableId, JavaClass, JavaField, emit_field_descriptor}; +use crate::parser_util::{Id, JavaClass, JavaField}; pub struct Field<'a> { pub class: &'a JavaClass, @@ -123,7 +123,7 @@ impl<'a> Field<'a> { let value = emit_constant(&value, descriptor); let ty = if descriptor.dimensions == 0 && let FieldType::Object(cls) = &descriptor.field_type - && ClassName::from(cls).is_string_class() + && Id::from(cls).is_string_class() { quote!(&'static str) } else { @@ -154,7 +154,7 @@ impl<'a> Field<'a> { }; let java_name = cstring(self.java.name()); - let descriptor = cstring(&emit_field_descriptor(self.java.descriptor())); + let descriptor = cstring(&self.java.descriptor().to_string()); let get_docs = format!("**get** {docs}"); let set_docs = format!("**set** {docs}"); @@ -287,11 +287,11 @@ pub fn emit_rust_type( FieldType::Float => quote!(f32), FieldType::Double => quote!(f64), FieldType::Object(class_name) => { - let class = IdBuf::from(class_name); + let class = Id::from(class_name); if !context.all_classes.contains_key(class.as_str()) { reject_reasons.push("ERROR: missing class for field/argument type"); } - if let Ok(path) = context.java_to_rust_path(class.as_id(), mod_) { + if let Ok(path) = context.java_to_rust_path(class, mod_) { flavorify(path, flavor) } else { reject_reasons.push("ERROR: Failed to resolve JNI path to Rust path for class type"); @@ -313,13 +313,13 @@ pub fn emit_rust_type( FieldType::Float => quote!(::java_spaghetti::FloatArray), FieldType::Double => quote!(::java_spaghetti::DoubleArray), FieldType::Object(class_name) => { - let class = IdBuf::from(class_name); + let class = Id::from(class_name); if !context.all_classes.contains_key(class.as_str()) { reject_reasons.push("ERROR: missing class for field type"); } - let path = match context.java_to_rust_path(class.as_id(), mod_) { + let path = match context.java_to_rust_path(class, mod_) { Ok(path) => path, Err(_) => { reject_reasons.push("ERROR: Failed to resolve JNI path to Rust path for class type"); diff --git a/java-spaghetti-gen/src/emit_rust/known_docs_url.rs b/java-spaghetti-gen/src/emit_rust/known_docs_url.rs index aa7ae5d..30f61a7 100644 --- a/java-spaghetti-gen/src/emit_rust/known_docs_url.rs +++ b/java-spaghetti-gen/src/emit_rust/known_docs_url.rs @@ -4,7 +4,7 @@ use cafebabe::descriptors::{FieldDescriptor, FieldType}; use super::methods::Method; use crate::emit_rust::Context; -use crate::parser_util::{Id, IdBuf}; +use crate::parser_util::Id; pub(crate) struct KnownDocsUrl { pub(crate) label: String, @@ -149,7 +149,7 @@ impl KnownDocsUrl { FieldType::Float => "float", FieldType::Double => "double", FieldType::Object(ref class_name) => { - let class = IdBuf::from(class_name); + let class = Id::from(class_name); obj_arg = class .as_str() .replace('/', pattern.argument_namespace_separator.as_str()) diff --git a/java-spaghetti-gen/src/emit_rust/methods.rs b/java-spaghetti-gen/src/emit_rust/methods.rs index d7ad34c..2d0cbff 100644 --- a/java-spaghetti-gen/src/emit_rust/methods.rs +++ b/java-spaghetti-gen/src/emit_rust/methods.rs @@ -7,7 +7,7 @@ use super::fields::{RustTypeFlavor, emit_fragment_type, emit_rust_type}; use super::known_docs_url::KnownDocsUrl; use crate::emit_rust::Context; use crate::identifiers::MethodManglingStyle; -use crate::parser_util::{JavaClass, JavaMethod, emit_method_descriptor}; +use crate::parser_util::{JavaClass, JavaMethod}; pub struct Method<'a> { pub class: &'a JavaClass, @@ -48,7 +48,7 @@ impl<'a> Method<'a> { "{}\x1f{}\x1f{}", self.class.path().as_str(), self.java.name(), - emit_method_descriptor(self.java.descriptor()) + self.java.descriptor() ); let ignored = context.config.ignore_class_methods.contains(&java_class_method) @@ -162,7 +162,7 @@ impl<'a> Method<'a> { }; let java_name = cstring(self.java.name()); - let descriptor = cstring(&emit_method_descriptor(self.java.descriptor())); + let descriptor = cstring(&self.java.descriptor().to_string()); let method_name = format_ident!("{method_name}"); let call = if self.java.is_constructor() { diff --git a/java-spaghetti-gen/src/identifiers/method_mangling_style.rs b/java-spaghetti-gen/src/identifiers/method_mangling_style.rs index d6c1611..ce6322c 100644 --- a/java-spaghetti-gen/src/identifiers/method_mangling_style.rs +++ b/java-spaghetti-gen/src/identifiers/method_mangling_style.rs @@ -2,7 +2,7 @@ use cafebabe::descriptors::{FieldType, MethodDescriptor}; use serde_derive::Deserialize; use super::rust_identifier::{IdentifierManglingError, javaify_identifier, rustify_identifier}; -use crate::parser_util::{ClassName, IdPart}; +use crate::parser_util::{Id, IdPart}; #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq, Hash)] #[serde(rename_all = "snake_case")] @@ -75,7 +75,7 @@ pub enum MethodManglingStyle { fn method_mangling_style_mangle_test() { use std::borrow::Cow; - use cafebabe::descriptors::{FieldDescriptor, ReturnDescriptor, UnqualifiedSegment}; + use cafebabe::descriptors::{ClassName, FieldDescriptor, ReturnDescriptor}; let desc_no_arg_ret_v = MethodDescriptor { parameters: Vec::new(), @@ -93,19 +93,7 @@ fn method_mangling_style_mangle_test() { let desc_arg_obj_ret_v = MethodDescriptor { parameters: vec![FieldDescriptor { dimensions: 0, - field_type: FieldType::Object(cafebabe::descriptors::ClassName { - segments: vec![ - UnqualifiedSegment { - name: Cow::Borrowed("java"), - }, - UnqualifiedSegment { - name: Cow::Borrowed("lang"), - }, - UnqualifiedSegment { - name: Cow::Borrowed("Object"), - }, - ], - }), + field_type: FieldType::Object(ClassName::try_from(Cow::Borrowed("java/lang/Object")).unwrap()), }], return_type: ReturnDescriptor::Void, }; @@ -245,7 +233,7 @@ impl MethodManglingStyle { FieldType::Float => buffer.push_str("_float"), FieldType::Double => buffer.push_str("_double"), FieldType::Object(class_name) => { - let class = ClassName::from(class_name); + let class = Id::from(class_name); if long_sig { for component in class.iter() { diff --git a/java-spaghetti-gen/src/parser_util/class.rs b/java-spaghetti-gen/src/parser_util/class.rs index fccd9e6..e203b06 100644 --- a/java-spaghetti-gen/src/parser_util/class.rs +++ b/java-spaghetti-gen/src/parser_util/class.rs @@ -1,9 +1,9 @@ -use std::borrow::Cow; use std::marker::PhantomPinned; use std::pin::Pin; pub use cafebabe::ClassAccessFlags; use cafebabe::attributes::AttributeData; +use cafebabe::descriptors::ClassName; use super::Id; @@ -79,7 +79,7 @@ impl JavaClass { self.get().super_class.as_ref().map(|class| Id(class)) } - pub fn interfaces(&self) -> std::slice::Iter<'_, Cow<'_, str>> { + pub fn interfaces(&self) -> std::slice::Iter<'_, ClassName<'_>> { self.get().interfaces.iter() } diff --git a/java-spaghetti-gen/src/parser_util/field.rs b/java-spaghetti-gen/src/parser_util/field.rs index a4622ce..56cb455 100644 --- a/java-spaghetti-gen/src/parser_util/field.rs +++ b/java-spaghetti-gen/src/parser_util/field.rs @@ -1,11 +1,7 @@ -use std::fmt::Write; - use cafebabe::FieldAccessFlags; use cafebabe::attributes::AttributeData; use cafebabe::constant_pool::LiteralConstant; -use cafebabe::descriptors::{FieldDescriptor, FieldType}; - -use super::ClassName; +use cafebabe::descriptors::FieldDescriptor; #[derive(Clone, Copy, Debug)] pub struct JavaField<'a> { @@ -96,29 +92,3 @@ impl<'a> JavaField<'a> { &self.java.descriptor } } - -pub fn emit_field_descriptor(descriptor: &FieldDescriptor) -> String { - let mut res = String::new(); - for _ in 0..descriptor.dimensions { - res.push('['); - } - if let FieldType::Object(class_name) = &descriptor.field_type { - res.push('L'); - write!(&mut res, "{}", ClassName::from(class_name)).unwrap(); - res.push(';'); - } else { - let ch = match descriptor.field_type { - FieldType::Boolean => 'Z', - FieldType::Byte => 'B', - FieldType::Char => 'C', - FieldType::Short => 'S', - FieldType::Integer => 'I', - FieldType::Long => 'J', - FieldType::Float => 'F', - FieldType::Double => 'D', - _ => unreachable!(), - }; - res.push(ch) - } - res -} diff --git a/java-spaghetti-gen/src/parser_util/id.rs b/java-spaghetti-gen/src/parser_util/id.rs index 433215a..a29030d 100644 --- a/java-spaghetti-gen/src/parser_util/id.rs +++ b/java-spaghetti-gen/src/parser_util/id.rs @@ -1,28 +1,6 @@ // Migrated from . -use std::fmt::Write; - -/// Owned Java class binary name (internal form). -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct IdBuf(String); - -impl IdBuf { - pub fn new(s: String) -> Self { - Self(s) - } - pub fn as_str(&self) -> &str { - self.0.as_str() - } - pub fn as_id(&self) -> Id { - Id(self.0.as_str()) - } - #[allow(dead_code)] - pub fn iter(&self) -> IdIter { - IdIter::new(self.0.as_str()) - } -} - -// XXX: This should really be `#[repr(transparent)] pub struct Id(str);`... +// XXX: This may really be `#[repr(transparent)] pub struct Id(str);`... // Also, patterns apparently can't handle Id::new(...) even when it's a const fn. /// Borrowed Java class binary name (internal form). @@ -36,6 +14,20 @@ impl<'a> Id<'a> { pub fn iter(&self) -> IdIter<'a> { IdIter::new(self.0) } + + pub fn is_string_class(&self) -> bool { + let mut iter = self.into_iter(); + iter.next() == Some(IdPart::Namespace("java")) + && iter.next() == Some(IdPart::Namespace("lang")) + && iter.next() == Some(IdPart::LeafClass("String")) + && iter.next().is_none() + } +} + +impl<'r, 'a: 'r> From<&'r cafebabe::descriptors::ClassName<'a>> for Id<'r> { + fn from(value: &'r cafebabe::descriptors::ClassName<'a>) -> Self { + Self(value.as_ref()) + } } impl<'a> IntoIterator for Id<'a> { @@ -126,108 +118,3 @@ fn id_iter_test() { ] ); } - -/// Newtype for `cafebabe::descriptors::ClassName`. -/// -/// XXX: cannot get the original string from `cafebabe::descriptors::ClassName`; the binary -/// name is split into `UnqualifiedSegment`s, not caring about Java-specific nested classes. -/// See . -#[derive(Clone, Copy, Debug)] -pub struct ClassName<'a> { - inner: &'a cafebabe::descriptors::ClassName<'a>, -} - -impl<'a> From<&'a cafebabe::descriptors::ClassName<'a>> for ClassName<'a> { - fn from(value: &'a cafebabe::descriptors::ClassName<'a>) -> Self { - Self { inner: value } - } -} - -impl<'a> std::ops::Deref for ClassName<'a> { - type Target = cafebabe::descriptors::ClassName<'a>; - fn deref(&self) -> &Self::Target { - self.inner - } -} - -impl std::fmt::Display for ClassName<'_> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let mut segs = self.segments.iter(); - f.write_str(segs.next().unwrap().name.as_ref())?; - for seg in segs { - f.write_char('/')?; - f.write_str(seg.name.as_ref())?; - } - Ok(()) - } -} - -impl<'a> From<&ClassName<'a>> for IdBuf { - fn from(value: &ClassName<'a>) -> Self { - Self::new(value.to_string()) - } -} - -impl<'a> From<&cafebabe::descriptors::ClassName<'a>> for IdBuf { - fn from(value: &cafebabe::descriptors::ClassName<'a>) -> Self { - Self::new(ClassName::from(value).to_string()) - } -} - -impl<'a> ClassName<'a> { - pub fn iter<'s>(&'s self) -> ClassNameIter<'a> { - let segments = &self.inner.segments; - if segments.len() > 1 { - ClassNameIter::RestPath(segments) - } else { - let classes = segments.last().map(|s| s.name.as_ref()).unwrap_or(""); - ClassNameIter::RestClasses(IdIter::new(classes)) - } - } -} - -impl<'a> IntoIterator for ClassName<'a> { - type Item = IdPart<'a>; - type IntoIter = ClassNameIter<'a>; - fn into_iter(self) -> Self::IntoIter { - self.iter() - } -} - -pub enum ClassNameIter<'a> { - RestPath(&'a [cafebabe::descriptors::UnqualifiedSegment<'a>]), - RestClasses(IdIter<'a>), -} - -impl<'a> Iterator for ClassNameIter<'a> { - type Item = IdPart<'a>; - fn next(&mut self) -> Option { - match self { - Self::RestPath(segments) => { - // `segments.len() > 1` must be true at here - let namespace = IdPart::Namespace(&segments[0].name); - *self = if segments.len() - 1 > 1 { - Self::RestPath(&segments[1..]) - } else { - Self::RestClasses(IdIter::new(&segments.last().unwrap().name)) - }; - Some(namespace) - } - Self::RestClasses(id_iter) => id_iter.next(), - } - } -} - -#[allow(unused)] -pub trait IterableId<'a>: IntoIterator> + Copy { - fn is_string_class(self) -> bool { - let mut iter = self.into_iter(); - iter.next() == Some(IdPart::Namespace("java")) - && iter.next() == Some(IdPart::Namespace("lang")) - && iter.next() == Some(IdPart::LeafClass("String")) - && iter.next().is_none() - } -} - -impl<'a> IterableId<'a> for Id<'a> {} -impl<'a> IterableId<'a> for ClassName<'a> {} diff --git a/java-spaghetti-gen/src/parser_util/method.rs b/java-spaghetti-gen/src/parser_util/method.rs index c66b998..7b21abe 100644 --- a/java-spaghetti-gen/src/parser_util/method.rs +++ b/java-spaghetti-gen/src/parser_util/method.rs @@ -1,8 +1,6 @@ use cafebabe::MethodAccessFlags; use cafebabe::attributes::AttributeData; -use cafebabe::descriptors::{MethodDescriptor, ReturnDescriptor}; - -use super::emit_field_descriptor; +use cafebabe::descriptors::MethodDescriptor; pub struct JavaMethod<'a> { java: &'a cafebabe::MethodInfo<'a>, @@ -101,18 +99,3 @@ impl<'a> JavaMethod<'a> { &self.java.descriptor } } - -pub fn emit_method_descriptor(descriptor: &MethodDescriptor) -> String { - let mut res = String::new(); - res.push('('); - for arg in descriptor.parameters.iter() { - res.push_str(&emit_field_descriptor(arg)) - } - res.push(')'); - if let ReturnDescriptor::Return(desc) = &descriptor.return_type { - res.push_str(&emit_field_descriptor(desc)) - } else { - res.push('V') - } - res -} diff --git a/java-spaghetti-gen/src/parser_util/mod.rs b/java-spaghetti-gen/src/parser_util/mod.rs index faefd56..04a63eb 100644 --- a/java-spaghetti-gen/src/parser_util/mod.rs +++ b/java-spaghetti-gen/src/parser_util/mod.rs @@ -4,6 +4,6 @@ mod id; mod method; pub use class::JavaClass; -pub use field::{JavaField, emit_field_descriptor}; +pub use field::JavaField; pub use id::*; -pub use method::{JavaMethod, emit_method_descriptor}; +pub use method::JavaMethod; From 95d7d2e615884628c94ff47d5216675f2ad40b54 Mon Sep 17 00:00:00 2001 From: wuwbobo2021 Date: Mon, 23 Jun 2025 09:56:21 +0800 Subject: [PATCH 2/3] Fix clippy warnings; fix class local reference leaks --- java-spaghetti-gen/src/config/runtime.rs | 2 +- .../src/emit_rust/class_proxy.rs | 5 +-- java-spaghetti-gen/src/emit_rust/classes.rs | 6 ++-- java-spaghetti-gen/src/emit_rust/fields.rs | 6 ++-- java-spaghetti-gen/src/emit_rust/methods.rs | 2 +- java-spaghetti-gen/src/emit_rust/mod.rs | 2 +- java-spaghetti-gen/src/emit_rust/modules.rs | 8 ++--- java-spaghetti-gen/src/run/mod.rs | 2 ++ java-spaghetti-gen/src/run/run.rs | 5 +-- java-spaghetti-gen/src/util/progress.rs | 2 +- java-spaghetti/Cargo.toml | 2 +- java-spaghetti/src/array.rs | 31 ++++++++++++------- java-spaghetti/src/env.rs | 5 +-- java-spaghetti/src/jni_type.rs | 2 ++ java-spaghetti/src/lib.rs | 1 + java-spaghetti/src/refs/arg.rs | 6 ++-- java-spaghetti/src/refs/local.rs | 2 +- java-spaghetti/src/refs/ref_.rs | 14 +++++---- java-spaghetti/src/string_chars.rs | 6 ++-- java-spaghetti/src/vm.rs | 4 +-- 20 files changed, 63 insertions(+), 50 deletions(-) diff --git a/java-spaghetti-gen/src/config/runtime.rs b/java-spaghetti-gen/src/config/runtime.rs index a4bcb11..a056580 100644 --- a/java-spaghetti-gen/src/config/runtime.rs +++ b/java-spaghetti-gen/src/config/runtime.rs @@ -167,7 +167,7 @@ fn expand_vars(string: String) -> String { if let Ok(replacement) = std::env::var(segment) { buf.push_str(&replacement[..]); } else { - println!("cargo:rerun-if-env-changed={}", segment); + println!("cargo:rerun-if-env-changed={segment}"); buf.push('%'); buf.push_str(segment); buf.push('%'); diff --git a/java-spaghetti-gen/src/emit_rust/class_proxy.rs b/java-spaghetti-gen/src/emit_rust/class_proxy.rs index 695d2c2..b33d33e 100644 --- a/java-spaghetti-gen/src/emit_rust/class_proxy.rs +++ b/java-spaghetti-gen/src/emit_rust/class_proxy.rs @@ -13,6 +13,7 @@ use crate::emit_rust::fields::emit_rust_type; use crate::parser_util::Id; impl Class { + #[allow(clippy::vec_init_then_push)] pub(crate) fn write_proxy(&self, context: &Context, methods: &[Method]) -> anyhow::Result { let mut emit_reject_reasons = Vec::new(); @@ -204,7 +205,7 @@ fn mangle_native_method(path: &str, name: &str, args: &[FieldDescriptor]) -> Str let mut res = String::new(); res.push_str("Java_"); res.push_str(&mangle_native(path)); - res.push_str("_"); + res.push('_'); res.push_str(&mangle_native(name)); res.push_str("__"); for d in args { @@ -219,7 +220,7 @@ fn mangle_native(s: &str) -> String { for c in s.chars() { match c { '0'..='9' | 'a'..='z' | 'A'..='Z' => res.push(c), - '/' => res.push_str("_"), + '/' => res.push('_'), '_' => res.push_str("_1"), ';' => res.push_str("_2"), '[' => res.push_str("_3"), diff --git a/java-spaghetti-gen/src/emit_rust/classes.rs b/java-spaghetti-gen/src/emit_rust/classes.rs index cec5b53..494a126 100644 --- a/java-spaghetti-gen/src/emit_rust/classes.rs +++ b/java-spaghetti-gen/src/emit_rust/classes.rs @@ -114,8 +114,8 @@ impl Class { }; let docs = match KnownDocsUrl::from_class(context, self.java.path()) { - Some(url) => format!("{} {} {}", visibility, keyword, url), - None => format!("{} {} {}", visibility, keyword, self.java.path().as_str()), + Some(url) => format!("{visibility} {keyword} {url}"), + None => format!("{visibility} {keyword} {}", self.java.path().as_str()), }; let rust_name = format_ident!("{}", &self.rust.struct_name); @@ -235,7 +235,7 @@ impl Class { out.extend(quote!(impl #rust_name { #contents })); - if context.proxy_included(&self.java.path().as_str()) { + if context.proxy_included(self.java.path().as_str()) { out.extend(self.write_proxy(context, &methods)?); } diff --git a/java-spaghetti-gen/src/emit_rust/fields.rs b/java-spaghetti-gen/src/emit_rust/fields.rs index d924b61..152aa9e 100644 --- a/java-spaghetti-gen/src/emit_rust/fields.rs +++ b/java-spaghetti-gen/src/emit_rust/fields.rs @@ -120,7 +120,7 @@ impl<'a> Field<'a> { match self.rust_names.as_ref().map_err(|e| anyhow!("bad mangling: {e}"))? { FieldMangling::ConstValue(constant, value) => { let constant = format_ident!("{}", constant); - let value = emit_constant(&value, descriptor); + let value = emit_constant(value, descriptor); let ty = if descriptor.dimensions == 0 && let FieldType::Object(cls) = &descriptor.field_type && Id::from(cls).is_string_class() @@ -208,14 +208,14 @@ pub fn emit_constant(constant: &LiteralConstant<'_>, descriptor: &FieldDescripto let value = *value as i16; quote!(#value) } - _ => panic!("invalid constant for char {:?}", constant), + _ => panic!("invalid constant for char {constant:?}"), }; } if descriptor.field_type == FieldType::Boolean && descriptor.dimensions == 0 { return match constant { LiteralConstant::Integer(0) => quote!(false), LiteralConstant::Integer(1) => quote!(true), - _ => panic!("invalid constant for boolean {:?}", constant), + _ => panic!("invalid constant for boolean {constant:?}"), }; } diff --git a/java-spaghetti-gen/src/emit_rust/methods.rs b/java-spaghetti-gen/src/emit_rust/methods.rs index 2d0cbff..c611689 100644 --- a/java-spaghetti-gen/src/emit_rust/methods.rs +++ b/java-spaghetti-gen/src/emit_rust/methods.rs @@ -147,7 +147,7 @@ impl<'a> Method<'a> { let docs = match KnownDocsUrl::from_method(context, self) { Some(url) => format!("{url}"), - None => format!("{}", self.java.name()), + None => self.java.name().to_string(), }; let throwable = context.throwable_rust_path(mod_); diff --git a/java-spaghetti-gen/src/emit_rust/mod.rs b/java-spaghetti-gen/src/emit_rust/mod.rs index 7509f7a..af19f20 100644 --- a/java-spaghetti-gen/src/emit_rust/mod.rs +++ b/java-spaghetti-gen/src/emit_rust/mod.rs @@ -52,7 +52,7 @@ impl<'a> Context<'a> { pub fn java_to_rust_path(&self, java_class: parser_util::Id, mod_: &str) -> Result> { let m = Class::mod_for(self, java_class)?; let s = Class::name_for(self, java_class)?; - let fqn = format!("{}::{}", m, s); + let fqn = format!("{m}::{s}"); // Calculate relative path from B to A. let b: Vec<&str> = mod_.split("::").collect(); diff --git a/java-spaghetti-gen/src/emit_rust/modules.rs b/java-spaghetti-gen/src/emit_rust/modules.rs index cca49e4..bdfd0b2 100644 --- a/java-spaghetti-gen/src/emit_rust/modules.rs +++ b/java-spaghetti-gen/src/emit_rust/modules.rs @@ -21,14 +21,14 @@ impl Module { for (name, module) in self.modules.iter() { writeln!(out)?; - writeln!(out, "pub mod {} {{", name)?; + writeln!(out, "pub mod {name} {{")?; module.write(context, out)?; writeln!(out, "}}")?; } for (_, class) in self.classes.iter() { let res = class.write(context)?; - out.write(dumb_format(res).as_bytes())?; + out.write_all(dumb_format(res).as_bytes())?; } Ok(()) @@ -61,7 +61,7 @@ struct DumbFormatter { impl DumbFormatter { fn newline(&mut self) { - self.f.push_str("\n"); + self.f.push('\n'); for _ in 0..self.indent { self.f.push_str(" "); } @@ -71,7 +71,7 @@ impl DumbFormatter { fn pre_write(&mut self) { if self.space && !self.after_newline { - self.f.push_str(" "); + self.f.push(' '); } self.space = false; self.after_newline = false; diff --git a/java-spaghetti-gen/src/run/mod.rs b/java-spaghetti-gen/src/run/mod.rs index d9b537b..4dbf13e 100644 --- a/java-spaghetti-gen/src/run/mod.rs +++ b/java-spaghetti-gen/src/run/mod.rs @@ -1,5 +1,7 @@ //! Core generation logic +// XXX: rename this submodule +#[allow(clippy::module_inception)] mod run; pub use run::run; diff --git a/java-spaghetti-gen/src/run/run.rs b/java-spaghetti-gen/src/run/run.rs index 929231c..52e36d2 100644 --- a/java-spaghetti-gen/src/run/run.rs +++ b/java-spaghetti-gen/src/run/run.rs @@ -76,10 +76,7 @@ fn gather_file(context: &mut emit_rust::Context, path: &Path) -> Result<(), Box< unknown => { Err(io::Error::new( io::ErrorKind::InvalidInput, - format!( - "Input files must have a '.class' or '.jar' extension, not a '.{}' extension", - unknown - ), + format!("Input files must have a '.class' or '.jar' extension, not a '.{unknown}' extension",), ))?; } } diff --git a/java-spaghetti-gen/src/util/progress.rs b/java-spaghetti-gen/src/util/progress.rs index 41f61ae..a7547ea 100644 --- a/java-spaghetti-gen/src/util/progress.rs +++ b/java-spaghetti-gen/src/util/progress.rs @@ -19,7 +19,7 @@ impl Progress { pub fn force_update(&mut self, msg: &str) { self.can_next_log = Instant::now() + self.debounce; - println!("{}", msg); + println!("{msg}"); } pub fn update(&mut self, msg: &str) { diff --git a/java-spaghetti/Cargo.toml b/java-spaghetti/Cargo.toml index 87f8201..afea5d6 100644 --- a/java-spaghetti/Cargo.toml +++ b/java-spaghetti/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "java-spaghetti" version = "0.2.0" -edition = "2021" +edition = "2024" description = "Glue code to accompany the java-spaghetti code generator for binding to JVM APIs from Rust" documentation = "https://docs.rs/java-spaghetti/" repository = "https://github.com/Dirbaio/java-spaghetti" diff --git a/java-spaghetti/src/array.rs b/java-spaghetti/src/array.rs index 076d843..f9beb56 100644 --- a/java-spaghetti/src/array.rs +++ b/java-spaghetti/src/array.rs @@ -34,6 +34,11 @@ where /// Uses JNI `GetArrayLength` to get the length of the Java array. fn len(self: &Ref<'_, Self>) -> usize; + /// Uses JNI `GetArrayLength` to get the length of the Java array, returns `true` if it is 0. + fn is_empty(self: &Ref<'_, Self>) -> bool { + self.len() == 0 + } + /// Uses JNI `Get{Type}ArrayRegion` to read the contents of the Java array within `[start .. start + elements.len()]`. /// /// Panics if the index is out of bound. @@ -100,7 +105,7 @@ macro_rules! primitive_array { impl PrimitiveArray<$type> for $name { fn new<'env>(env: Env<'env>, size: usize) -> Local<'env, Self> { - assert!(size <= std::i32::MAX as usize); // jsize == jint == i32 + assert!(size <= i32::MAX as usize); // jsize == jint == i32 let size = size as jsize; let jnienv = env.as_raw(); unsafe { @@ -127,8 +132,8 @@ macro_rules! primitive_array { } fn get_region(self: &Ref<'_, Self>, start: usize, elements: &mut [$type]) { - assert!(start <= std::i32::MAX as usize); // jsize == jint == i32 - assert!(elements.len() <= std::i32::MAX as usize); // jsize == jint == i32 + assert!(start <= i32::MAX as usize); // jsize == jint == i32 + assert!(elements.len() <= i32::MAX as usize); // jsize == jint == i32 let self_len = self.len() as jsize; let elements_len = elements.len() as jsize; @@ -150,8 +155,8 @@ macro_rules! primitive_array { } fn set_region(self: &Ref<'_, Self>, start: usize, elements: &[$type]) { - assert!(start <= std::i32::MAX as usize); // jsize == jint == i32 - assert!(elements.len() <= std::i32::MAX as usize); // jsize == jint == i32 + assert!(start <= i32::MAX as usize); // jsize == jint == i32 + assert!(elements.len() <= i32::MAX as usize); // jsize == jint == i32 let self_len = self.len() as jsize; let elements_len = elements.len() as jsize; @@ -210,7 +215,7 @@ unsafe impl JniType for ObjectArray { impl ObjectArray { /// Uses JNI `NewObjectArray` to create a new Java object array. pub fn new<'env>(env: Env<'env>, size: usize) -> Local<'env, Self> { - assert!(size <= std::i32::MAX as usize); // jsize == jint == i32 + assert!(size <= i32::MAX as usize); // jsize == jint == i32 let class = T::static_with_jni_type(|t| unsafe { env.require_class(t) }); let size = size as jsize; @@ -221,6 +226,11 @@ impl ObjectArray { }; // Only sane exception here is an OOM exception env.exception_check::().map_err(|_| "OOM").unwrap(); + + unsafe { + let env = env.as_raw(); + ((**env).v1_2.DeleteLocalRef)(env, class); + } unsafe { Local::from_raw(env, object) } } @@ -235,10 +245,7 @@ impl ObjectArray { /// Uses JNI `NewObjectArray` to create a new Java object array of the exact size, then sets its items /// with the iterator of JNI (null?) references. - pub fn new_from<'env>( - env: Env<'env>, - elements: impl ExactSizeIterator + Iterator>, - ) -> Local<'env, Self> { + pub fn new_from<'env>(env: Env<'env>, elements: impl ExactSizeIterator>) -> Local<'env, Self> { let size = elements.len(); let array = Self::new(env, size); let env = array.env().as_raw(); @@ -260,7 +267,7 @@ impl ObjectArray { /// /// XXX: Expose this via `std::ops::Index`. pub fn get<'env>(self: &Ref<'env, Self>, index: usize) -> Result>, Local<'env, E>> { - assert!(index <= std::i32::MAX as usize); // jsize == jint == i32 XXX: Should maybe be treated as an exception? + assert!(index <= i32::MAX as usize); // jsize == jint == i32 XXX: Should maybe be treated as an exception? let index = index as jsize; let env = self.env(); let result = unsafe { @@ -279,7 +286,7 @@ impl ObjectArray { /// /// XXX: I don't think there's a way to expose this via `std::ops::IndexMut` sadly? pub fn set<'env>(self: &Ref<'env, Self>, index: usize, value: impl AsArg) -> Result<(), Local<'env, E>> { - assert!(index <= std::i32::MAX as usize); // jsize == jint == i32 XXX: Should maybe be treated as an exception? + assert!(index <= i32::MAX as usize); // jsize == jint == i32 XXX: Should maybe be treated as an exception? let index = index as jsize; let env = self.env(); unsafe { diff --git a/java-spaghetti/src/env.rs b/java-spaghetti/src/env.rs index 3cbef15..bb945dd 100644 --- a/java-spaghetti/src/env.rs +++ b/java-spaghetti/src/env.rs @@ -1,8 +1,8 @@ use std::ffi::CStr; use std::marker::PhantomData; use std::ptr::{self, null_mut}; -use std::sync::atomic::{AtomicPtr, Ordering}; use std::sync::OnceLock; +use std::sync::atomic::{AtomicPtr, Ordering}; use jni_sys::*; @@ -69,6 +69,7 @@ pub struct Env<'env> { static CLASS_LOADER: AtomicPtr<_jobject> = AtomicPtr::new(null_mut()); #[allow(clippy::missing_safety_doc)] +#[allow(unsafe_op_in_unsafe_fn)] impl<'env> Env<'env> { pub unsafe fn from_raw(ptr: *mut JNIEnv) -> Self { Self { @@ -232,7 +233,7 @@ impl<'env> Env<'env> { } // If neither found the class, panic. - panic!("couldn't load class {:?}", class); + panic!("couldn't load class {class:?}"); } unsafe fn require_class_jni(self, class: &CStr) -> jclass { diff --git a/java-spaghetti/src/jni_type.rs b/java-spaghetti/src/jni_type.rs index 8a8fa63..2de0a2a 100644 --- a/java-spaghetti/src/jni_type.rs +++ b/java-spaghetti/src/jni_type.rs @@ -4,6 +4,8 @@ use jni_sys::*; /// JNI bindings rely on this type being accurate. /// +/// # Safety +/// /// **unsafe**: Passing the wrong type can cause unsoundness, since the code that interacts with JNI blindly trusts it's correct. /// /// Why the awkward callback style instead of returning `&'static CStr`? Arrays of arrays may need to dynamically diff --git a/java-spaghetti/src/lib.rs b/java-spaghetti/src/lib.rs index 47c2505..517fff4 100644 --- a/java-spaghetti/src/lib.rs +++ b/java-spaghetti/src/lib.rs @@ -62,6 +62,7 @@ pub trait ThrowableType: ReferenceType {} /// You should generally not be interacting with this type directly, but it must be public for codegen. #[doc(hidden)] +#[warn(clippy::missing_safety_doc)] pub unsafe trait ReferenceType: JniType + Sized + 'static {} /// Marker trait indicating `Self` can be assigned to `T`. diff --git a/java-spaghetti/src/refs/arg.rs b/java-spaghetti/src/refs/arg.rs index 42c8a78..264f8ee 100644 --- a/java-spaghetti/src/refs/arg.rs +++ b/java-spaghetti/src/refs/arg.rs @@ -42,7 +42,7 @@ impl Arg { if self.object.is_null() { None } else { - Some(Ref::from_raw(env, self.object)) + Some(unsafe { Ref::from_raw(env, self.object) }) } } @@ -52,7 +52,7 @@ impl Arg { /// the intended use case of immediately converting any [Arg] into [Local] at the start of a JNI callback, /// where Java directly invoked your function with an [Env] + arguments, is sound. pub unsafe fn into_local<'env>(self, env: Env<'env>) -> Option> { - self.into_ref(env).map(|r| r.as_local()) + unsafe { self.into_ref(env) }.map(|r| r.as_local()) } /// This equals [Arg::into_ref] + [Ref::as_global]. @@ -61,6 +61,6 @@ impl Arg { /// /// **unsafe**: The same as [Arg::into_ref]. pub unsafe fn into_global(self, env: Env) -> Option> { - self.into_ref(env).as_ref().map(Ref::as_global) + unsafe { self.into_ref(env) }.as_ref().map(Ref::as_global) } } diff --git a/java-spaghetti/src/refs/local.rs b/java-spaghetti/src/refs/local.rs index f82792e..3c81fc3 100644 --- a/java-spaghetti/src/refs/local.rs +++ b/java-spaghetti/src/refs/local.rs @@ -43,7 +43,7 @@ impl<'env, T: ReferenceType> Local<'env, T> { /// - `object` references an instance of type `T`. pub unsafe fn from_raw(env: Env<'env>, object: jobject) -> Self { Self { - ref_: Ref::from_raw(env, object), + ref_: unsafe { Ref::from_raw(env, object) }, } } diff --git a/java-spaghetti/src/refs/ref_.rs b/java-spaghetti/src/refs/ref_.rs index 04cbc09..fdd5613 100644 --- a/java-spaghetti/src/refs/ref_.rs +++ b/java-spaghetti/src/refs/ref_.rs @@ -88,10 +88,12 @@ impl<'env, T: ReferenceType> Ref<'env, T> { let env = self.env(); let jnienv = env.as_raw(); let class = U::static_with_jni_type(|t| unsafe { env.require_class(t) }); - if !unsafe { ((**jnienv).v1_2.IsInstanceOf)(jnienv, self.as_raw(), class) } { - return Err(crate::CastError); - } - Ok(()) + let assignable = unsafe { + let ret = ((**jnienv).v1_2.IsInstanceOf)(jnienv, self.as_raw(), class); + ((**jnienv).v1_2.DeleteLocalRef)(jnienv, class); + ret + }; + if assignable { Ok(()) } else { Err(crate::CastError) } } /// Casts itself to a JNI reference of type `U` forcefully, without the cost of runtime checking. @@ -100,7 +102,7 @@ impl<'env, T: ReferenceType> Ref<'env, T> { /// /// - `self` references an instance of type `U`. pub unsafe fn cast_unchecked(self) -> Ref<'env, U> { - transmute(self) + unsafe { transmute(self) } } /// Tries to cast itself to a JNI reference of type `U`. @@ -123,7 +125,7 @@ impl<'env, T: ReferenceType> Ref<'env, T> { /// /// - `self` references an instance of type `U`. pub unsafe fn cast_ref_unchecked(&self) -> &Ref<'env, U> { - transmute(self) + unsafe { transmute(self) } } /// Tries to cast the borrowed `Ref` to a JNI reference of type `U`. diff --git a/java-spaghetti/src/string_chars.rs b/java-spaghetti/src/string_chars.rs index 380f9c2..75fb3c1 100644 --- a/java-spaghetti/src/string_chars.rs +++ b/java-spaghetti/src/string_chars.rs @@ -25,8 +25,8 @@ impl<'env> StringChars<'env> { pub unsafe fn from_env_jstring(env: Env<'env>, string: jstring) -> Self { debug_assert!(!string.is_null()); - let chars = env.get_string_chars(string); - let length = env.get_string_length(string); + let chars = unsafe { env.get_string_chars(string) }; + let length = unsafe { env.get_string_length(string) }; Self { env, @@ -42,7 +42,7 @@ impl<'env> StringChars<'env> { } /// [std::char::decode_utf16]\(...\)s these string characters. - pub fn decode(&self) -> char::DecodeUtf16>> { + pub fn decode(&self) -> char::DecodeUtf16>> { char::decode_utf16(self.chars().iter().cloned()) } diff --git a/java-spaghetti/src/vm.rs b/java-spaghetti/src/vm.rs index 7e35413..2cede0f 100644 --- a/java-spaghetti/src/vm.rs +++ b/java-spaghetti/src/vm.rs @@ -49,7 +49,7 @@ impl VM { JNI_EDETACHED => { let ret = unsafe { ((**self.0).v1_2.AttachCurrentThread)(self.0, &mut env, null_mut()) }; if ret != JNI_OK { - panic!("AttachCurrentThread returned unknown error: {}", ret) + panic!("AttachCurrentThread returned unknown error: {ret}") } if !get_thread_exit_flag() { set_thread_attach_flag(self.0); @@ -57,7 +57,7 @@ impl VM { true } JNI_EVERSION => panic!("GetEnv returned JNI_EVERSION"), - unexpected => panic!("GetEnv returned unknown error: {}", unexpected), + unexpected => panic!("GetEnv returned unknown error: {unexpected}"), }; let result = callback(unsafe { Env::from_raw(env as _) }); From 3d2bd34da273ac2fe86bcbfa575791cf6de5c83f Mon Sep 17 00:00:00 2001 From: wuwbobo2021 Date: Mon, 23 Jun 2025 10:02:00 +0800 Subject: [PATCH 3/3] CI: Add clippy check --- .github/workflows/rust_nightly.yml | 3 +++ java-spaghetti/src/refs/ref_.rs | 7 +++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/rust_nightly.yml b/.github/workflows/rust_nightly.yml index 2759c43..df3f660 100644 --- a/.github/workflows/rust_nightly.yml +++ b/.github/workflows/rust_nightly.yml @@ -17,7 +17,10 @@ jobs: - uses: actions/checkout@v4 - run: rustup update nightly && rustup default nightly - run: rustup component add rustfmt + - run: rustup component add clippy - name: Check fmt run: cargo fmt -- --check + - name: Clippy + run: cargo clippy -- -Dwarnings - name: Test run: cargo test diff --git a/java-spaghetti/src/refs/ref_.rs b/java-spaghetti/src/refs/ref_.rs index fdd5613..6163a83 100644 --- a/java-spaghetti/src/refs/ref_.rs +++ b/java-spaghetti/src/refs/ref_.rs @@ -88,11 +88,10 @@ impl<'env, T: ReferenceType> Ref<'env, T> { let env = self.env(); let jnienv = env.as_raw(); let class = U::static_with_jni_type(|t| unsafe { env.require_class(t) }); - let assignable = unsafe { - let ret = ((**jnienv).v1_2.IsInstanceOf)(jnienv, self.as_raw(), class); + let assignable = unsafe { ((**jnienv).v1_2.IsInstanceOf)(jnienv, self.as_raw(), class) }; + unsafe { ((**jnienv).v1_2.DeleteLocalRef)(jnienv, class); - ret - }; + } if assignable { Ok(()) } else { Err(crate::CastError) } }