Skip to content

Commit 7853698

Browse files
committed
jni-macros stash
1 parent ecb817e commit 7853698

File tree

1 file changed

+126
-38
lines changed

1 file changed

+126
-38
lines changed

jni-macros/src/lib.rs

Lines changed: 126 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,12 @@ pub fn jni_cstr(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
8282
/// corresponding JNI signature, including the raw string like
8383
/// "(Ljava/lang/String;I)Ljava/lang/String;" and enumerated argument plus return types.
8484
///
85-
/// This macro can also parse raw JNI signature strings in order to validate them at compile time
86-
/// but it's recommended to use the structured syntax for better readability.
85+
/// This macro can also parse raw JNI signature strings like `"(Ljava/lang/String;I)Z"` in order to
86+
/// validate them at compile time but it's recommended to use the structured syntax for better
87+
/// readability.
88+
///
89+
/// **Note:** The signature and `type_map` syntax supported by this macro is also used by the
90+
/// [`bind_java_type`] and [`native_method`] macros.
8791
///
8892
/// [MethodSignature]: https://docs.rs/jni/latest/jni/signature/struct.MethodSignature.html
8993
/// [FieldSignature]: https://docs.rs/jni/latest/jni/signature/struct.FieldSignature.html
@@ -94,8 +98,8 @@ pub fn jni_cstr(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
9498
/// ```ignore
9599
/// jni_sig!(
96100
/// [jni = <path>],
97-
/// [sig =] <signature>,
98101
/// [type_map = { ... }],
102+
/// [sig =] <signature>,
99103
/// )
100104
/// ```
101105
/// The parser automatically detects whether it's a method signature (has parentheses) or a field
@@ -105,18 +109,19 @@ pub fn jni_cstr(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
105109
///
106110
/// - `jni = <path>` - Optionally override the jni crate path (default: auto-detected via
107111
/// `proc_macro_crate`, must come first if given)
108-
/// - `sig = <signature>` - The signature ('`sig =`' prefix is optional for the signature)
109112
/// - `type_map = { RustType => java.lang.ClassName, ... }` - Optional type mappings for Rust types
113+
/// - `sig = <signature>` - The signature ('`sig =`' prefix is optional for the signature)
110114
///
111-
/// The `sig` and `type_map` properties can appear in any order.
112-
///
113-
/// The `type_map` property can be provided multiple times (mappings are merged).
115+
/// The `type_map` property can be provided multiple times and mappings are merged.
114116
///
115117
/// The design allows for a `macro_rules` wrapper to inject `jni =` or `type_map =` properties,
116118
/// without needing to parse anything else.
117119
///
118120
/// # Type Syntax
119121
///
122+
/// Note: this syntax for signature types is also used by the [`bind_java_type`] and
123+
/// [`native_method`] macros.
124+
///
120125
/// ## Primitive Types
121126
/// - Java primitives: `jboolean`, `jbyte`, `jchar`, `jshort`, `jint`, `jlong`, `jfloat`, `jdouble`
122127
/// - Aliases: `boolean`/`bool`, `byte`/`i8`, `char`, `short`/`i16`, `int`/`i32`, `long`/`i64`,
@@ -150,9 +155,67 @@ pub fn jni_cstr(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
150155
/// - Other built-in types, such as `JList` (`java.util.List`) can be overridden by mapping them to
151156
/// a different type via a `type_map`
152157
///
153-
/// ## Type Mappings
154-
/// - Explicit mapping block for Rust types: `type_map = { RustType => java.class.Name }`
155-
/// - Java types without any `type_map` entry will map to `JObject` (`java.lang.Object`)
158+
/// ## Type Mappings via `type_map` Block
159+
///
160+
/// A `type_map` block:
161+
/// - Maps Rust [Reference] type names to Java class names for use in method/field signatures.
162+
/// - Maps Java class names to Rust types (primarily for use with the [`bind_java_type`] and
163+
/// [`native_method`] macros)
164+
/// - Allows the definition of type aliases for more ergonomic / readable signatures.
165+
///
166+
/// Multiple `type_map` blocks will be merged, so that wrapper macros may forward-declare common
167+
/// type mappings to avoid repetition.
168+
///
169+
/// A `type_map` supports three types of mappings:
170+
///
171+
/// ### Reference Type Mappings
172+
///
173+
/// Map Rust [Reference] types to Java classes like `RustType => java.type.Name`:
174+
///
175+
/// ```ignore
176+
/// type_map = {
177+
/// CustomType => com.example.CustomClass,
178+
/// AnotherType => "com.example.AnotherClass",
179+
/// InnerType => com.example.Outer::Inner,
180+
/// AnotherInnerType => "com.example.Outer$AnotherInner",
181+
/// my_crate::MyType => com.example.MyType,
182+
/// }
183+
/// ```
184+
///
185+
/// The right-side Java type uses the syntax for Java Object Types described above.
186+
///
187+
/// ### Unsafe Primitive Type Mappings
188+
///
189+
/// Map Rust types to Java primitive types using the `unsafe` keyword. This is particularly useful
190+
/// for Rust types that transparently wrap a pointer (e.g., handles) that need to be passed to Java
191+
/// as a `long`:
192+
///
193+
/// ```ignore
194+
/// type_map = {
195+
/// unsafe MyHandle => long,
196+
/// unsafe MyBoxedPointer => long,
197+
/// unsafe MyRawFd => int,
198+
/// }
199+
/// ```
200+
///
201+
/// These mappings are marked `unsafe` because macros like [`bind_java_type`] and [`native_method`]
202+
/// cannot verify type safety between the Rust type and Java primitive type - apart from checking
203+
/// the size and alignment.
204+
///
205+
/// ### Type Aliases
206+
///
207+
/// Creates aliases for existing type mappings using the `typealias` keyword. This can improve
208+
/// readability in signatures before defining full type bindings:
209+
///
210+
/// ```ignore
211+
/// type_map = {
212+
/// MyType => com.example.MyType,
213+
/// typealias MyAlias => MyType,
214+
/// typealias MyObjectAlias => JObject,
215+
/// }
216+
/// ```
217+
///
218+
/// Note: Aliases for array types are not supported.
156219
///
157220
/// # Method Signature Syntax
158221
///
@@ -593,16 +656,28 @@ pub fn jni_mangle(
593656
mangle::jni_mangle2(attr.into(), item.into()).into()
594657
}
595658

596-
/// Creates a compile-time type-checked `NativeMethod` descriptor for a native method.
659+
/// Bind a single native method to a Rust function with type safety and optionally export it.
660+
///
661+
/// This macro can do the following:
662+
/// - Generate a `NativeMethod` struct with type-checked function pointer
663+
/// - Optionally wrap the implementation with `catch_unwind` (via `EnvUnowned::with_env`) and unwrap
664+
/// any returned `Result` with an `ErrorPolicy` (such as `ThrowRuntimeExAndDefault`)
665+
/// - Optionally generate a JNI export symbol for the method
666+
///
667+
/// Firstly, this macro always generates a `NativeMethod` struct with a compile-time guarantee that
668+
/// the provided function pointer matches the JNI signature.
669+
///
670+
/// By default the native method implementation is automatically wrapped with a call to
671+
/// `EnvUnowned::with_env` and any returned `Result` is unwrapped with an `ErrorPolicy` (default
672+
/// `ThrowRuntimeExAndDefault`).
597673
///
598-
/// This macro generates a `NativeMethod` struct with a compile-time guarantee that the
599-
/// provided function pointer matches the JNI signature.
674+
/// If a `java_type` name is specified, it can also generate a JNI export symbol for the method.
600675
///
601-
/// By default the native method implementation is automatically wrapped with a
602-
/// call to `EnvUnowned::with_env` and any returned `Result` is unwrapped with
603-
/// an `ErrorPolicy` (default `ThrowRuntimeExAndDefault`).
676+
/// This can be used as an alternative to the `bind_java_type!` macro if you only have a few native
677+
/// methods to bind and offers stronger type safety than the `#[jni_mangle]` attribute macro.
604678
///
605-
/// It also optionally generates a JNI export symbol for the method.
679+
/// The signature and type mappings syntax is compatible with the `jni_sig!` and `bind_java_type!`
680+
/// macros which makes it easy to share type mapping definitions or migrate between them.
606681
///
607682
/// # Syntax
608683
///
@@ -611,13 +686,13 @@ pub fn jni_mangle(
611686
/// ```ignore
612687
/// native_method! {
613688
/// [jni = <path>,] // Override jni crate path (default: auto-detected, must come first)
614-
/// [rust_type = <Type>,] // Type for 'this' parameter (default: JObject)
689+
/// [rust_type = <Type>,] // Type for 'this' parameter, for instance methods (default: JObject)
615690
/// [java_type = <Type>,] // Fully-qualified Java class name (required if export = true)
616-
/// [name = "<methodName>",] // Java method name
691+
/// [name = "<methodName>",] // Java method name (default: snake_case to lowerCamelCase of Rust fn name)
617692
/// [type_map = { ... },] // Type mappings for custom types
618693
/// [sig = (args) -> ret,] // JNI signature (see `jni_sig!` macro for syntax)
619694
/// [static = true,] // Indicates static method with a `class` parameter instead of `this`
620-
/// [export = true | "Java_name",] // Generate mangled JNI export symbol like `Java_package_Class_method` that JVM can resolve (requires `java_type`)
695+
/// [export = true | "Java_name",] // Generate mangled JNI export symbol like `Java_package_Class_method` that JVM can resolve (requires `java_type` if true)
621696
/// [fn = <function_path>,] // Path to Rust function
622697
/// [error_policy = <Policy>,] // ErrorPolicy for unwrapping Result (default: ThrowRuntimeExAndDefault)
623698
///
@@ -629,16 +704,24 @@ pub fn jni_mangle(
629704
/// # Properties
630705
///
631706
/// - `jni` - Optional override for the jni crate path (must come first if provided)
632-
/// - `rust_type` - Optional type for the `this` parameter (e.g., `MyType`). If omitted, uses `JObject`
633-
/// - `java_type` - Fully-qualified Java class name, required in combination with `export = true` / `extern` native methods
634-
/// - `name` - The Java method name as a string literal
707+
/// - `rust_type` - Optional type for the `this` parameter (e.g., `MyType`). If omitted, uses
708+
/// `JObject`
709+
/// - `java_type` - Fully-qualified Java class name, required in combination with `export = true` /
710+
/// `extern` native methods
711+
/// - `name` - The Java method name as a string literal ( defaults to snake_case to lowerCamelCase
712+
/// conversion of the Rust function name)
635713
/// - `type_map` - Optional type mappings from Rust types to Java class names
636714
/// - `sig` - The method signature (see [`jni_sig!`] macro for syntax)
637-
/// - `fn` - Path to the Rust function that implements this native method (defaults to `RustType::method_name` if shorthand syntax is used)
638-
/// - `static` - Indicates that this is a static method (emits a `class` parameter instead of `this`)
639-
/// - `export` - If `true` or a string literal like `"Java_package_Class_method"`, generates a JNI export symbol for the method (requires `java_type`)
640-
/// - `raw` - If specified, the function receives a raw `EnvUnowned` instead of `&mut Env`, with no `catch_unwind` wrapper and does not return a `Result`
641-
/// - `error_policy` - The `ErrorPolicy` to use when unwrapping the `Result` returned by a non-raw implementation (default: `ThrowRuntimeExAndDefault`)
715+
/// - `fn` - Path to the Rust function that implements this native method (defaults to
716+
/// `RustType::method_name` if shorthand syntax is used)
717+
/// - `static` - Indicates that this is a static method (emits a `class` parameter instead of
718+
/// `this`)
719+
/// - `export` - If `true` or a string literal like `"Java_package_Class_method"`, generates a JNI
720+
/// export symbol for the method (requires `java_type`)
721+
/// - `raw` - If specified, the function receives a raw `EnvUnowned` instead of `&mut Env`, with no
722+
/// `catch_unwind` wrapper and does not return a `Result`
723+
/// - `error_policy` - The `ErrorPolicy` to use when unwrapping the `Result` returned by a non-raw
724+
/// implementation (default: `ThrowRuntimeExAndDefault`)
642725
///
643726
/// ## Shorthand syntax
644727
///
@@ -654,12 +737,15 @@ pub fn jni_mangle(
654737
/// - Converts `method_name` from snake_case to lowerCamelCase for the Java method `name`
655738
/// - Uses `RustType::method_name` as the `fn` path unless overridden
656739
/// - `static` indicates a static method (emits a `class` parameter instead of `this`)
657-
/// - `raw` indicates the function receives a raw `EnvUnowned` instead of `&mut Env`, with no `catch_unwind` wrapper and does not return a `Result`
658-
/// - `extern` says that the function should have a JNI mangled export symbol generated (`java_type` must also be provided)
740+
/// - `raw` indicates the function receives a raw `EnvUnowned` instead of `&mut Env`, with no
741+
/// `catch_unwind` wrapper and does not return a `Result`
742+
/// - `extern` says that the function should have a JNI mangled export symbol generated (`java_type`
743+
/// must also be provided)
659744
///
660745
/// # Non-raw Function Signature Requirements
661746
///
662-
/// If `raw` is not specified, the function must return a `Result` and accept a mutable `Env` reference.
747+
/// If `raw` is not specified, the function must return a `Result` and accept a mutable `Env`
748+
/// reference.
663749
///
664750
/// Non-static, instance method signature:
665751
///
@@ -697,7 +783,8 @@ pub fn jni_mangle(
697783
///
698784
/// # Raw Function Signature Requirements
699785
///
700-
/// If `raw` is specified, the function must accept an `EnvUnowned` parameter and return the exact type specified in the JNI signature.
786+
/// If `raw` is specified, the function must accept an `EnvUnowned` parameter and return the exact
787+
/// type specified in the JNI signature.
701788
///
702789
/// Note that in this case there is no `catch_unwind` wrapper and no automatic error handling.
703790
///
@@ -739,21 +826,22 @@ pub fn jni_mangle(
739826
///
740827
/// # Type Safety
741828
///
742-
/// The macro generates a wrapper function with the exact signature required by JNI,
743-
/// and then calls your implementation function through it. This ensures:
829+
/// The macro generates a wrapper function with the exact signature required by JNI, and then calls
830+
/// your implementation function through it. This ensures:
744831
///
745832
/// - The function pointer passed to `NativeMethod::from_raw_parts` has the correct ABI
746833
/// - Parameter types match the JNI signature
747834
/// - Return type matches the JNI signature
748835
/// - Any type mismatch results in a compile-time error
749836
///
750-
/// **Note:** This macro can not automatically check whether the native method is
751-
/// `static` or not and so it's important that the `static` property is set correctly
752-
/// to ensure the type for the second parameter (`this` vs `class`) is correct.
837+
/// **Note:** This macro can not automatically check whether the native method is `static` or not
838+
/// and so it's important that the `static` property is set correctly to ensure the type for the
839+
/// second parameter (`this` vs `class`) is correct.
753840
///
754841
/// # See Also
755842
///
756-
/// - [`NativeMethod`](https://docs.rs/jni/latest/jni/struct.NativeMethod.html) - The struct created by this macro
843+
/// - [`NativeMethod`](https://docs.rs/jni/latest/jni/struct.NativeMethod.html)
844+
/// - The struct created by this macro
757845
#[proc_macro]
758846
pub fn native_method(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
759847
native_method::native_method_impl(input.into())

0 commit comments

Comments
 (0)