Skip to content

Commit d0194ef

Browse files
committed
Use impl Borrow to support using references of InvocationArgs too
1 parent 3911e14 commit d0194ef

File tree

9 files changed

+240
-198
lines changed

9 files changed

+240
-198
lines changed

README.md

Lines changed: 75 additions & 64 deletions
Large diffs are not rendered by default.

rust/README.md

Lines changed: 56 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -36,30 +36,30 @@ let jvm = JvmBuilder::new().build()?;
3636

3737
// Create a java.lang.String instance
3838
let string_instance = jvm.create_instance(
39-
"java.lang.String", // The Java class to create an instance for
40-
&Vec::new(), // The `InvocationArg`s to use for the constructor call - empty for this example
39+
"java.lang.String", // The Java class to create an instance for
40+
InvocationArg::empty(), // An array of `InvocationArg`s to use for the constructor call - empty for this example
4141
)?;
4242

4343
// The instances returned from invocations and instantiations can be viewed as pointers to Java Objects.
4444
// They can be used for further Java calls.
4545
// For example, the following invokes the `isEmpty` method of the created java.lang.String instance
4646
let boolean_instance = jvm.invoke(
47-
&string_instance, // The String instance created above
48-
"isEmpty", // The method of the String instance to invoke
49-
&Vec::new(), // The `InvocationArg`s to use for the invocation - empty for this example
47+
&string_instance, // The String instance created above
48+
"isEmpty", // The method of the String instance to invoke
49+
InvocationArg::empty(), // The `InvocationArg`s to use for the invocation - empty for this example
5050
)?;
5151

52-
// If we need to transform an `Instance` to Rust value, the `to_rust` should be called
52+
// If we need to transform an `Instance` to some Rust value, the `to_rust` should be called
5353
let rust_boolean: bool = jvm.to_rust(boolean_instance)?;
5454
println!("The isEmpty() method of the java.lang.String instance returned {}", rust_boolean);
5555
// The above prints:
5656
// The isEmpty() method of the java.lang.String instance returned true
5757

5858
// Static invocation
5959
let _static_invocation_result = jvm.invoke_static(
60-
"java.lang.System", // The Java class to invoke
61-
"currentTimeMillis", // The static method of the Java class to invoke
62-
&Vec::new(), // The `InvocationArg`s to use for the invocation - empty for this example
60+
"java.lang.System", // The Java class to invoke
61+
"currentTimeMillis", // The static method of the Java class to invoke
62+
InvocationArg::empty(), // The `InvocationArg`s to use for the invocation - empty for this example
6363
)?;
6464

6565
// Access a field of a class
@@ -127,12 +127,20 @@ let my_vec: Vec<String> = vec![
127127
let i10 = InvocationArg::try_from(my_vec.as_slice())?;
128128
```
129129

130+
The `j4rs` apis accept `InvocationArg`s either as references, or values:
131+
132+
```rust
133+
let inv_args = InvocationArg::try_from("arg from Rust")?;
134+
let _ = jvm.create_instance("java.lang.String", &[&inv_args])?; // Pass a reference
135+
let _ = jvm.create_instance("java.lang.String", &[inv_args])?; // Move
136+
```
137+
130138
The `Instance`s returned by j4rs can be transformed to `InvocationArg`s and be further used for invoking methods as well:
131139

132140
```rust
133141
let one_more_string_instance = jvm.create_instance(
134-
"java.lang.String", // The Java class to create an instance for
135-
&Vec::new(), // The `InvocationArg`s to use for the constructor call - empty for this example
142+
"java.lang.String", // The Java class to create an instance for
143+
InvocationArg::empty(), // The `InvocationArg`s to use for the constructor call - empty for this example
136144
)?;
137145

138146
let i11 = InvocationArg::try_from(one_more_string_instance)?;
@@ -143,7 +151,7 @@ To create an `InvocationArg` that represents a `null` Java value, use the `From`
143151
```rust
144152
let null_string = InvocationArg::from(Null::String); // A null String
145153
let null_integer = InvocationArg::from(Null::Integer); // A null Integer
146-
let null_obj = InvocationArg::from(Null::Of("java.util.List")); // A null object of any other class. E.g. List
154+
let null_obj = InvocationArg::from(Null::Of("java.util.List")); // A null object of any other class. E.g. List
147155
```
148156

149157
### Passing custom arguments from Rust to Java
@@ -237,7 +245,7 @@ We can invoke it like following:
237245

238246
```rust
239247
let s_test = "j4rs_rust";
240-
let my_test = jvm.create_instance("org.astonbitecode.j4rs.tests.MyTest", &[])?;
248+
let my_test = jvm.create_instance("org.astonbitecode.j4rs.tests.MyTest", InvocationArg::empty())?;
241249
let instance = jvm.invoke_async(&my_test, "getStringWithFuture", &[InvocationArg::try_from(s_test)?]).await?;
242250
let string: String = jvm.to_rust(instance)?;
243251
assert_eq!(s_test, string);
@@ -270,17 +278,17 @@ let s1 = InvocationArg::try_from("string1")?;
270278
let s2 = InvocationArg::try_from("string2")?;
271279
let s3 = InvocationArg::try_from("string3")?;
272280

273-
let arr_instance = jvm.create_java_array("java.lang.String", &vec![s1, s2, s3])?;
281+
let arr_instance = jvm.create_java_array("java.lang.String", &[s1, s2, s3])?;
274282
// Invoke the Arrays.asList(...) and retrieve a java.util.List<String>
275283
let list_instance = jvm.invoke_static("java.util.Arrays", "asList", &[InvocationArg::from(arr_instance)])?;
276284
```
277285

278286
### Java Generics
279287

280288
```rust
281-
// Assuming that the following map_instance is a Map<String, Integer>
289+
// Assuming the following map_instance is a Map<String, Integer>
282290
// we may invoke its put method
283-
jvm.invoke(&map_instance, "put", &vec![InvocationArg::try_from("one")?, InvocationArg::try_from(1)?])?;
291+
jvm.invoke(&map_instance, "put", &[InvocationArg::try_from("one")?, InvocationArg::try_from(1)?])?;
284292
```
285293

286294
### Java primitives
@@ -320,15 +328,15 @@ let jvm = JvmBuilder::new().build()?;
320328

321329
// Create an instance
322330
let string_instance = jvm.create_instance(
323-
"java.lang.String",
324-
&vec![InvocationArg::try_from(" a string ")?],
331+
"java.lang.String",
332+
&[InvocationArg::try_from(" a string ")?],
325333
)?;
326334

327335
// Perform chained operations on the instance
328336
let string_size: isize = jvm.chain(string_instance)
329-
.invoke("trim", &[])?
330-
.invoke("length", &[])?
331-
.to_rust()?;
337+
.invoke("trim", InvocationArg::empty())?
338+
.invoke("length", InvocationArg::empty())?
339+
.to_rust()?;
332340

333341
// Assert that the string was trimmed
334342
assert!(string_size == 8);
@@ -349,14 +357,14 @@ In order to initialize a channel that will provide Java callback values, the `Jv
349357
// (the class just needs to extend the
350358
// `org.astonbitecode.j4rs.api.invocation.NativeCallbackToRustChannelSupport`)
351359
let i = jvm.create_instance(
352-
"org.astonbitecode.j4rs.tests.MyTest",
353-
&Vec::new())?;
360+
"org.astonbitecode.j4rs.tests.MyTest",
361+
InvocationArg::empty())?;
354362

355363
// Invoke the method
356364
let instance_receiver_res = jvm.invoke_to_channel(
357-
&i, // The instance to invoke asynchronously
358-
"performCallback", // The method to invoke asynchronoysly
359-
&Vec::new() // The `InvocationArg`s to use for the invocation - empty for this example
365+
&i, // The instance to invoke asynchronously
366+
"performCallback", // The method to invoke asynchronoysly
367+
InvocationArg::empty() // The `InvocationArg`s to use for the invocation - empty for this example
360368
);
361369

362370
// Wait for the response to come
@@ -415,7 +423,7 @@ jvm.deploy_artifact(&MavenArtifact::from("io.my:library:1.2.3"))?;
415423

416424
Maven artifacts are added automatically to the classpath and do not need to be explicitly added.
417425

418-
A good practice is that the deployment of maven artifacts is done by build scripts, during the crate's compilation. This ensures that the classpath is properly populated during the actual Rust code execution.
426+
A good practice is that the deployment of maven artifacts is done by build scripts, during the crate's compilation. This ensures the classpath is properly populated during the actual Rust code execution.
419427

420428
_Note: the deployment does not take care the transitive dependencies yet._
421429

@@ -426,8 +434,8 @@ If we have one jar that needs to be accessed using `j4rs`, we need to add it in
426434
```rust
427435
let entry = ClasspathEntry::new("/home/myuser/dev/myjar-1.0.0.jar");
428436
let jvm: Jvm = JvmBuilder::new()
429-
.classpath_entry(entry)
430-
.build()?;
437+
.classpath_entry(entry)
438+
.build()?;
431439
```
432440

433441
## j4rs Java library
@@ -438,7 +446,7 @@ The jar for `j4rs` is available in the Maven Central. It may be used by adding t
438446
<dependency>
439447
<groupId>io.github.astonbitecode</groupId>
440448
<artifactId>j4rs</artifactId>
441-
<version>0.15.3</version>
449+
<version>0.18.0</version>
442450
<scope>provided</scope>
443451
</dependency>
444452
```
@@ -478,7 +486,8 @@ pub extern fn jni_onload(env: *mut JavaVM, _reserved: jobject) -> jint {
478486
Create an `Activity` and define your native methods normally, as described [here](#java-to-rust-support).
479487

480488
Note:
481-
If you encounter any issues when using j4rs in Android, this may be caused by Java 8 compatibility problems. This is why there is a `Java 7` version of `j4rs`:
489+
If you encounter any issues when using j4rs in older Android versions, this may be caused by Java 8 compatibility problems.
490+
This is why there is a `Java 7` version of `j4rs`:
482491

483492
```xml
484493
<dependency>
@@ -488,6 +497,8 @@ If you encounter any issues when using j4rs in Android, this may be caused by Ja
488497
</dependency>
489498
```
490499

500+
Update: Java 7 is no more supported. `j4rs` 0.13.1 is the last version.
501+
491502
## JavaFX support
492503
(v0.13.0 onwards)
493504

@@ -529,31 +540,31 @@ let jvm = JvmBuilder::new().with_javafx_support().build()?;
529540
let stage = jvm.start_javafx_app()?.rx().recv()?;
530541

531542
// Create a StackPane. Java code: StackPane root = new StackPane();
532-
let root = jvm.create_instance("javafx.scene.layout.StackPane", &[])?;
543+
let root = jvm.create_instance("javafx.scene.layout.StackPane", InvocationArg::empty())?;
533544

534545
// Create the button. Java code: Button btn = new Button();
535-
let btn = jvm.create_instance("javafx.scene.control.Button", &[])?;
546+
let btn = jvm.create_instance("javafx.scene.control.Button", InvocationArg::empty())?;
536547
// Get the action channel for this button
537548
let btn_action_channel = jvm.get_javafx_event_receiver(&btn, FxEventType::ActionEvent_Action)?;
538549
// Set the text of the button. Java code: btn.setText("Say Hello World to Rust");
539550
jvm.invoke(&btn, "setText", &["A button that sends events to Rust".try_into()?])?;
540551
// Add the button to the GUI. Java code: root.getChildren().add(btn);
541552
jvm.chain(&root)?
542-
.invoke("getChildren", &[])?
543-
.invoke("add", &[btn.try_into()?])?
544-
.collect();
553+
.invoke("getChildren", InvocationArg::empty())?
554+
.invoke("add", &[btn.try_into()?])?
555+
.collect();
545556

546557
// Create a new Scene. Java code: Scene scene = new Scene(root, 300, 250);
547558
let scene = jvm.create_instance("javafx.scene.Scene", &[
548-
root.try_into()?,
549-
InvocationArg::try_from(300_f64)?.into_primitive()?,
550-
InvocationArg::try_from(250_f64)?.into_primitive()?])?;
559+
root.try_into()?,
560+
InvocationArg::try_from(300_f64)?.into_primitive()?,
561+
InvocationArg::try_from(250_f64)?.into_primitive()?])?;
551562
// Set the title for the scene. Java code: stage.setTitle("Hello Rust world!");
552563
jvm.invoke(&stage, "setTitle", &["Hello Rust world!".try_into()?])?;
553564
// Set the scene in the stage. Java code: stage.setScene(scene);
554565
jvm.invoke(&stage, "setScene", &[scene.try_into()?])?;
555566
// Show the stage. Java code: stage.show();
556-
jvm.invoke(&stage, "show", &[])?;
567+
jvm.invoke(&stage, "show", InvocationArg::empty())?;
557568

558569
```
559570

@@ -607,7 +618,7 @@ let stage = jvm.start_javafx_app()?.rx().recv()?;
607618
// Set the title for the scene. Java code: stage.setTitle("Hello Rust world!");
608619
jvm.invoke(&stage, "setTitle", &["Hello JavaFX from Rust!".try_into()?])?;
609620
// Show the stage. Java code: stage.show();
610-
jvm.invoke(&stage, "show", &[])?;
621+
jvm.invoke(&stage, "show", InvocationArg::empty())?;
611622

612623
// Load a fxml. This returns an `FxController` which can be used in order to find Nodes by their id,
613624
// add Event Listeners and more.
@@ -662,8 +673,8 @@ Someone may specify a different [base_path](https://docs.rs/j4rs/0.13.0/j4rs/str
662673

663674
```rust
664675
let jvm_res = j4rs::JvmBuilder::new()
665-
.with_base_path("/opt/myapp")
666-
.build();
676+
.with_base_path("/opt/myapp")
677+
.build();
667678
```
668679

669680
The `base_path` defines the location of two directories that are needed for j4rs to work;
@@ -701,8 +712,8 @@ as long as the Jvm creation is done using the `with_base_path` method:
701712

702713
```rust
703714
let jvm_res = j4rs::JvmBuilder::new()
704-
.with_base_path("/opt/myapp")
705-
.build();
715+
.with_base_path("/opt/myapp")
716+
.build();
706717
```
707718

708719
## FAQ

rust/benches/j4rs_benchmark.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,12 @@ use criterion::{black_box, BenchmarkId};
99
use j4rs::{self, Instance, InvocationArg, Jvm};
1010

1111
fn do_instance_creation(jvm: &Jvm) -> Instance {
12-
jvm.create_instance("org.astonbitecode.j4rs.tests.MyTest", &[])
12+
jvm.create_instance("org.astonbitecode.j4rs.tests.MyTest", InvocationArg::empty())
1313
.unwrap()
1414
}
1515

1616
fn do_invocation_w_no_args(jvm: &Jvm, instance: &Instance) -> Instance {
17-
jvm.invoke(instance, "getMyString", &[]).unwrap()
17+
jvm.invoke(instance, "getMyString", InvocationArg::empty()).unwrap()
1818
}
1919

2020
fn do_invocation_w_string_args(jvm: &Jvm, instance: &Instance) -> Instance {
@@ -94,23 +94,23 @@ fn j4rs_benchmark(c: &mut Criterion) {
9494

9595
let jvm: Jvm = j4rs::new_jvm(Vec::new(), Vec::new()).unwrap();
9696
let instance = jvm
97-
.create_instance("org.astonbitecode.j4rs.tests.MyTest", &[])
97+
.create_instance("org.astonbitecode.j4rs.tests.MyTest", InvocationArg::empty())
9898
.unwrap();
9999
c.bench_function("invocations with no args and String result", move |b| {
100100
b.iter(|| do_invocation_w_no_args(black_box(&jvm), black_box(&instance)))
101101
});
102102

103103
let jvm: Jvm = j4rs::new_jvm(Vec::new(), Vec::new()).unwrap();
104104
let instance = jvm
105-
.create_instance("org.astonbitecode.j4rs.tests.MyTest", &[])
105+
.create_instance("org.astonbitecode.j4rs.tests.MyTest", InvocationArg::empty())
106106
.unwrap();
107107
c.bench_function("invocations with String arg and String result", move |b| {
108108
b.iter(|| do_invocation_w_string_args(black_box(&jvm), black_box(&instance)))
109109
});
110110

111111
let jvm: Jvm = j4rs::new_jvm(Vec::new(), Vec::new()).unwrap();
112112
let instance = jvm
113-
.create_instance("org.astonbitecode.j4rs.tests.MyTest", &[])
113+
.create_instance("org.astonbitecode.j4rs.tests.MyTest", InvocationArg::empty())
114114
.unwrap();
115115
c.bench_function(
116116
"invocations with Integer arg and Integer result",
@@ -119,7 +119,7 @@ fn j4rs_benchmark(c: &mut Criterion) {
119119

120120
let jvm: Jvm = j4rs::new_jvm(Vec::new(), Vec::new()).unwrap();
121121
let instance = jvm
122-
.create_instance("org.astonbitecode.j4rs.tests.MyTest", &[])
122+
.create_instance("org.astonbitecode.j4rs.tests.MyTest", InvocationArg::empty())
123123
.unwrap();
124124
c.bench_function(
125125
"invocations with String arg and String result transformed to Rust",
@@ -132,7 +132,7 @@ fn j4rs_benchmark(c: &mut Criterion) {
132132

133133
let jvm: Jvm = j4rs::new_jvm(Vec::new(), Vec::new()).unwrap();
134134
let instance = jvm
135-
.create_instance("org.astonbitecode.j4rs.tests.MyTest", &[])
135+
.create_instance("org.astonbitecode.j4rs.tests.MyTest", InvocationArg::empty())
136136
.unwrap();
137137
c.bench_function("to_rust_unboxed", move |b| {
138138
b.iter(|| use_to_rust_deserialized(black_box(&jvm), black_box(&instance)))
@@ -144,7 +144,7 @@ fn bench_create_java_objects_and_to_rust(c: &mut Criterion) {
144144

145145
let jvm: Jvm = j4rs::new_jvm(Vec::new(), Vec::new()).unwrap();
146146
let instance = jvm
147-
.create_instance("org.astonbitecode.j4rs.tests.MyTest", &[])
147+
.create_instance("org.astonbitecode.j4rs.tests.MyTest", InvocationArg::empty())
148148
.unwrap();
149149

150150
for i in 0..2 {
0 Bytes
Binary file not shown.

rust/src/api/invocation_arg.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,12 @@ pub enum InvocationArg {
5050
}
5151

5252
impl InvocationArg {
53+
54+
/// Return an empty slice of `InvocationArg`s
55+
pub fn empty<'a>() -> &'a[InvocationArg;0] {
56+
&[]
57+
}
58+
5359
/// Creates a InvocationArg::Rust.
5460
/// This is default for the Args that are created from the Rust code.
5561
pub fn new<T>(arg: &T, class_name: &str) -> InvocationArg
@@ -763,7 +769,7 @@ mod inv_arg_unit_tests {
763769
};
764770
let ia = InvocationArg::new(&my_bean, "org.astonbitecode.j4rs.tests.MyBean");
765771

766-
let test_instance = jvm.create_instance("org.astonbitecode.j4rs.tests.MyTest", &[])?;
772+
let test_instance = jvm.create_instance("org.astonbitecode.j4rs.tests.MyTest", InvocationArg::empty())?;
767773
let string_instance = jvm.invoke(&test_instance, "getTheString", &[ia]).unwrap();
768774

769775
let rust_string: String = jvm.to_rust(string_instance).unwrap();

0 commit comments

Comments
 (0)