xref: /aosp_15_r20/external/fbjni/docs/quickref.md (revision 65c59e023c5336bbd4a23be7af78407e3d80e7e7)
1*65c59e02SInna Palant# Quick Reference
2*65c59e02SInna Palant- [JavaClass definition and method registration](#javaclass-definition-and-method-registration)
3*65c59e02SInna Palant- [Wrapping constructors](#wrapping-constructors)
4*65c59e02SInna Palant- [Basic method usage (Java to C++ and C++ to Java)](#basic-method-usage-java-to-c-and-c-to-java)
5*65c59e02SInna Palant- [Methods using Java primitives](#methods-using-java-primitives)
6*65c59e02SInna Palant- [Methods using Java Strings](#methods-using-java-strings)
7*65c59e02SInna Palant- [Working with arrays of primitives](#working-with-arrays-of-primitives)
8*65c59e02SInna Palant- [Working with arrays of objects](#working-with-arrays-of-objects)
9*65c59e02SInna Palant- [References](#references)
10*65c59e02SInna Palant- [Defining a nested class](#defining-a-nested-class)
11*65c59e02SInna Palant- [Defining a child class](#defining-a-child-class)
12*65c59e02SInna Palant- [Getting and setting fields](#getting-and-setting-fields)
13*65c59e02SInna Palant- [Working with JObject and JClass](#working-with-jobject-and-jclass)
14*65c59e02SInna Palant- [Catching and throwing exceptions](#catching-and-throwing-exceptions)
15*65c59e02SInna Palant- [Working with boxed primitives](#working-with-boxed-primitives)
16*65c59e02SInna Palant- [Working with Iterables](#working-with-iterables)
17*65c59e02SInna Palant- [Building Collections](#building-collections)
18*65c59e02SInna Palant- [Transferring data with direct ByteBuffer](#transferring-data-with-direct-bytebuffer)
19*65c59e02SInna Palant## JavaClass definition and method registration
20*65c59e02SInna Palant```cpp
21*65c59e02SInna Palant#include <fbjni/fbjni.h>
22*65c59e02SInna Palantusing namespace facebook::jni;
23*65c59e02SInna Palant```
24*65c59e02SInna Palant```cpp
25*65c59e02SInna Palant// Standard declaration for a normal class (no C++ fields).
26*65c59e02SInna Palantstruct DocTests : JavaClass<DocTests> {
27*65c59e02SInna Palant  static constexpr auto kJavaDescriptor = "Lcom/facebook/jni/DocTests;";
28*65c59e02SInna Palant```
29*65c59e02SInna Palant```cpp
30*65c59e02SInna Palant  // NOTE: The name of this method doesn't matter.
31*65c59e02SInna Palant  static void registerNatives() {
32*65c59e02SInna Palant    javaClassStatic()->registerNatives({
33*65c59e02SInna Palant      makeNativeMethod("nativeVoidMethod", DocTests::nativeVoidMethod),
34*65c59e02SInna Palant      makeNativeMethod("staticNativeVoidMethod", DocTests::staticNativeVoidMethod),
35*65c59e02SInna Palant```
36*65c59e02SInna Palant```cpp
37*65c59e02SInna Palantjint JNI_OnLoad(JavaVM* vm, void*) {
38*65c59e02SInna Palant  return facebook::jni::initialize(vm, [] {
39*65c59e02SInna Palant      DocTests::registerNatives();
40*65c59e02SInna Palant  });
41*65c59e02SInna Palant}
42*65c59e02SInna Palant```
43*65c59e02SInna Palant
44*65c59e02SInna Palant
45*65c59e02SInna Palant## Wrapping constructors
46*65c59e02SInna Palant```java
47*65c59e02SInna Palantclass DataHolder {
48*65c59e02SInna Palant  int i;
49*65c59e02SInna Palant  String s;
50*65c59e02SInna Palant  DataHolder(int i, String s) {
51*65c59e02SInna Palant    this.i = i;
52*65c59e02SInna Palant    this.s = s;
53*65c59e02SInna Palant  }
54*65c59e02SInna Palant  static DataHolder someInstance;
55*65c59e02SInna Palant}
56*65c59e02SInna Palant```
57*65c59e02SInna Palant```cpp
58*65c59e02SInna Palantstruct JDataHolder : JavaClass<JDataHolder> {
59*65c59e02SInna Palant  static constexpr auto kJavaDescriptor = "Lcom/facebook/jni/DataHolder;";
60*65c59e02SInna Palant  // newInstance should be wrapped to ensure compile-time checking of call sites.
61*65c59e02SInna Palant  static local_ref<JDataHolder> create(int i, std::string const& s) {
62*65c59e02SInna Palant    // Constructor is looked up by argument types at *runtime*.
63*65c59e02SInna Palant    return newInstance(i, s);
64*65c59e02SInna Palant  }
65*65c59e02SInna Palant```
66*65c59e02SInna Palant```cpp
67*65c59e02SInna Palant  // Call-site in another file.
68*65c59e02SInna Palant  static local_ref<JDataHolder> runConstructor(
69*65c59e02SInna Palant      alias_ref<JClass> clazz) {
70*65c59e02SInna Palant    // Call to ordinatry C++ function is checked at *compile time*.
71*65c59e02SInna Palant    return JDataHolder::create(1, "hi");
72*65c59e02SInna Palant  }
73*65c59e02SInna Palant```
74*65c59e02SInna Palant
75*65c59e02SInna Palant
76*65c59e02SInna Palant## Basic method usage (Java to C++ and C++ to Java)
77*65c59e02SInna Palant```java
78*65c59e02SInna Palant  native void nativeVoidMethod();
79*65c59e02SInna Palant  static native void staticNativeVoidMethod();
80*65c59e02SInna Palant  void voidMethod() {}
81*65c59e02SInna Palant  static void staticVoidMethod() {}
82*65c59e02SInna Palant```
83*65c59e02SInna Palant```cpp
84*65c59e02SInna Palant public:
85*65c59e02SInna Palant  // Java methods should usually be wrapped by C++ methods for ease-of-use.
86*65c59e02SInna Palant  // (Most other examples in this document will inline these for brevity.)
87*65c59e02SInna Palant  void callVoidMethod() {
88*65c59e02SInna Palant    static const auto method = getClass()->getMethod<void()>("voidMethod");
89*65c59e02SInna Palant    // self() returns the raw JNI reference to this object.
90*65c59e02SInna Palant    method(self());
91*65c59e02SInna Palant  }
92*65c59e02SInna Palant  static void callStaticVoidMethod() {
93*65c59e02SInna Palant    static const auto cls = javaClassStatic();
94*65c59e02SInna Palant    static const auto method = cls->getStaticMethod<void()>("staticVoidMethod");
95*65c59e02SInna Palant    method(cls);
96*65c59e02SInna Palant  }
97*65c59e02SInna Palant
98*65c59e02SInna Palant  // Native implementations of Java methods can be private.
99*65c59e02SInna Palant private:
100*65c59e02SInna Palant  // For non-Hybrid objects, all JNI methods must be static on the C++ side
101*65c59e02SInna Palant  // because only Hybrid objects can have C++ state.
102*65c59e02SInna Palant  static void nativeVoidMethod(
103*65c59e02SInna Palant      // All non-static methods receive "this" as a first argument.
104*65c59e02SInna Palant      alias_ref<DocTests> thiz) {
105*65c59e02SInna Palant    // Make sure we got the right object.
106*65c59e02SInna Palant    assert(thiz->toString() == "instance of DocTests");
107*65c59e02SInna Palant    thiz->callVoidMethod();
108*65c59e02SInna Palant  }
109*65c59e02SInna Palant  static void staticNativeVoidMethod(
110*65c59e02SInna Palant      // All static methods receive the class as a first argument.
111*65c59e02SInna Palant      alias_ref<JClass> clazz) {
112*65c59e02SInna Palant    assert(clazz->toString() == "class com.facebook.jni.DocTests");
113*65c59e02SInna Palant    DocTests::callStaticVoidMethod();
114*65c59e02SInna Palant  }
115*65c59e02SInna Palant```
116*65c59e02SInna Palant
117*65c59e02SInna Palant
118*65c59e02SInna Palant## Methods using Java primitives
119*65c59e02SInna Palant```java
120*65c59e02SInna Palant  static native long addSomeNumbers(byte b, short s, int i);
121*65c59e02SInna Palant  static long doubler(int i) { return i + i; }
122*65c59e02SInna Palant```
123*65c59e02SInna Palant```cpp
124*65c59e02SInna Palant  static jlong addSomeNumbers(alias_ref<JClass> clazz, jbyte b, jshort s, jint i) {
125*65c59e02SInna Palant    static const auto doubler = clazz->getStaticMethod<jlong(jint)>("doubler");
126*65c59e02SInna Palant    jlong l = doubler(clazz, 4);
127*65c59e02SInna Palant    return b + s + i + l;
128*65c59e02SInna Palant  }
129*65c59e02SInna Palant
130*65c59e02SInna Palant```
131*65c59e02SInna Palant  Argument and return types can be in either JNI style or C++ style.
132*65c59e02SInna Palant
133*65c59e02SInna Palant  | Java type | JNI types |
134*65c59e02SInna Palant  | --- | --- |
135*65c59e02SInna Palant  | `boolean` | `jboolean`, `bool` |
136*65c59e02SInna Palant  | `byte` | `jbyte`, `int8_t` |
137*65c59e02SInna Palant  | `char` | `jchar` |
138*65c59e02SInna Palant  | `short` | `jshort`, `short`, `int16_t` |
139*65c59e02SInna Palant  | `int` | `jint`, `int`, `int32_t` |
140*65c59e02SInna Palant  | `long` | `jlong`, `int64_t` |
141*65c59e02SInna Palant  | `float` | `jfloat`, `float` |
142*65c59e02SInna Palant  | `double` | `jdouble`, `double` |
143*65c59e02SInna Palant
144*65c59e02SInna Palant
145*65c59e02SInna Palant## Methods using Java Strings
146*65c59e02SInna Palant```java
147*65c59e02SInna Palant  // Java methods used by the C++ code below.
148*65c59e02SInna Palant  static native String fancyCat(String s1, String s2);
149*65c59e02SInna Palant  static native String getCString();
150*65c59e02SInna Palant  static String doubler(String s) { return s + s; }
151*65c59e02SInna Palant```
152*65c59e02SInna Palant```cpp
153*65c59e02SInna Palant  static std::string fancyCat(
154*65c59e02SInna Palant      alias_ref<JClass> clazz,
155*65c59e02SInna Palant      // Native methods can receive strings as JString (direct JNI reference) ...
156*65c59e02SInna Palant      alias_ref<JString> s1,
157*65c59e02SInna Palant      // or as std::string (converted to real UTF-8).
158*65c59e02SInna Palant      std::string s2) {
159*65c59e02SInna Palant    // Convert JString to std::string.
160*65c59e02SInna Palant    std::string result = s1->toStdString();
161*65c59e02SInna Palant    // Java methods can receive and return JString ...
162*65c59e02SInna Palant    static const auto doubler_java = clazz->getStaticMethod<JString(JString)>("doubler");
163*65c59e02SInna Palant    result += doubler_java(clazz, *s1)->toStdString();
164*65c59e02SInna Palant    // and also std::string (converted from real UTF-8).
165*65c59e02SInna Palant    static const auto doubler_std = clazz->getStaticMethod<std::string(std::string)>("doubler");
166*65c59e02SInna Palant    result += doubler_std(clazz, s2)->toStdString();
167*65c59e02SInna Palant    // They can also receive const char*, but not return it.
168*65c59e02SInna Palant    static const auto doubler_char = clazz->getStaticMethod<std::string(const char*)>("doubler");
169*65c59e02SInna Palant    result += doubler_char(clazz, s2.c_str())->toStdString();
170*65c59e02SInna Palant    // All 3 formats can be returned (std::string shown here, const char* below).
171*65c59e02SInna Palant    return result;
172*65c59e02SInna Palant  }
173*65c59e02SInna Palant
174*65c59e02SInna Palant  static const char* getCString(alias_ref<JClass>) {
175*65c59e02SInna Palant    // This string is converted to JString *after* getCString returns.
176*65c59e02SInna Palant    // Watch your memory lifetimes.
177*65c59e02SInna Palant    return "Watch your memory.";
178*65c59e02SInna Palant  }
179*65c59e02SInna Palant```
180*65c59e02SInna Palant
181*65c59e02SInna Palant
182*65c59e02SInna Palant## Working with arrays of primitives
183*65c59e02SInna Palant```java
184*65c59e02SInna Palant  static native int[] primitiveArrays(int[] arr);
185*65c59e02SInna Palant```
186*65c59e02SInna Palant```cpp
187*65c59e02SInna Palant  static local_ref<JArrayInt> primitiveArrays(
188*65c59e02SInna Palant      alias_ref<JClass> clazz,
189*65c59e02SInna Palant      // JArrayX is available for all primitives.
190*65c59e02SInna Palant      alias_ref<JArrayInt> arr) {
191*65c59e02SInna Palant    size_t size = arr->size();
192*65c59e02SInna Palant    std::vector<jint> buffer(size + 1L);
193*65c59e02SInna Palant    // Copy elements into native memory.
194*65c59e02SInna Palant    arr->getRegion(0, size, buffer.data());
195*65c59e02SInna Palant    // Copy elements into fresh memory (returns unique_ptr<int[]>).
196*65c59e02SInna Palant    auto elements = arr->getRegion(0, size);
197*65c59e02SInna Palant    // Pin can eliminate the need for a copy.
198*65c59e02SInna Palant    {
199*65c59e02SInna Palant      auto pin = arr->pin();
200*65c59e02SInna Palant      for (size_t i = 0; i < pin.size(); i++) {
201*65c59e02SInna Palant        // Can read and/or write pin[i].
202*65c59e02SInna Palant        buffer[size] += pin[i];
203*65c59e02SInna Palant      }
204*65c59e02SInna Palant    }
205*65c59e02SInna Palant    // Allocating a new array and copying data in.
206*65c59e02SInna Palant    // (Data can also be assigned by writing to a pin.)
207*65c59e02SInna Palant    auto ret = JArrayInt::newArray(size + 1);
208*65c59e02SInna Palant    ret->setRegion(0, size + 1, buffer.data());
209*65c59e02SInna Palant    return ret;
210*65c59e02SInna Palant  }
211*65c59e02SInna Palant```
212*65c59e02SInna Palant
213*65c59e02SInna Palant
214*65c59e02SInna Palant## Working with arrays of objects
215*65c59e02SInna Palant```cpp
216*65c59e02SInna Palant  static local_ref<JArrayClass<JString>> classArrays(
217*65c59e02SInna Palant      alias_ref<JClass> clazz,
218*65c59e02SInna Palant      alias_ref<JArrayClass<JDataHolder>> arr) {
219*65c59e02SInna Palant    size_t size = arr->size();
220*65c59e02SInna Palant    local_ref<JArrayClass<JString>> ret = JArrayClass<JString>::newArray(size);
221*65c59e02SInna Palant    for (int i = 0; i < size; ++i) {
222*65c59e02SInna Palant      local_ref<JString> str = arr->getElement(i)->getStr();
223*65c59e02SInna Palant      ret->setElement(i, *str);
224*65c59e02SInna Palant    }
225*65c59e02SInna Palant    return ret;
226*65c59e02SInna Palant  }
227*65c59e02SInna Palant```
228*65c59e02SInna Palant
229*65c59e02SInna Palant
230*65c59e02SInna Palant## References
231*65c59e02SInna Palant
232*65c59e02SInna Palant  ### `alias_ref<JFoo>`
233*65c59e02SInna Palant  `alias_ref` is a non-owning reference, like a bare pointer.
234*65c59e02SInna Palant  It is used almost exclusively for function arguments.
235*65c59e02SInna Palant
236*65c59e02SInna Palant  ### `local_ref<JFoo>`
237*65c59e02SInna Palant  `local_ref` is a ref-counted thread-specific pointer that is invalidated upon returning to Java.
238*65c59e02SInna Palant  For variables used within a function, use `local_ref`.
239*65c59e02SInna Palant  Most functions should return `local_ref` (and let the caller convert to a `global_ref` if necessary).
240*65c59e02SInna Palant
241*65c59e02SInna Palant  ### `global_ref<JFoo>`
242*65c59e02SInna Palant  `global_ref` is a ref-counted pointer.
243*65c59e02SInna Palant  Use this for storing a reference to a Java object that may
244*65c59e02SInna Palant  outlive the current call from Java into C++
245*65c59e02SInna Palant  (e.g. class member fields are usually global refs).
246*65c59e02SInna Palant  You can create a new `global_ref` (from an `alias_ref`/`local_ref`) by calling `make_global`.
247*65c59e02SInna Palant```cpp
248*65c59e02SInna Palant  static local_ref<JObject> convertReferences(
249*65c59e02SInna Palant      alias_ref<JClass> clazz,
250*65c59e02SInna Palant      alias_ref<JMyDerivedClass> derived) {
251*65c59e02SInna Palant    local_ref<JMyDerivedClass> local_derived = make_local(derived);
252*65c59e02SInna Palant    global_ref<JMyDerivedClass> global_derived = make_global(derived);
253*65c59e02SInna Palant    // Store global_derived somewhere.
254*65c59e02SInna Palant    return local_derived;
255*65c59e02SInna Palant  }
256*65c59e02SInna Palant```
257*65c59e02SInna Palant
258*65c59e02SInna Palant
259*65c59e02SInna Palant## Defining a nested class
260*65c59e02SInna Palant```java
261*65c59e02SInna Palantclass Outer {
262*65c59e02SInna Palant  class Nested {}
263*65c59e02SInna Palant}
264*65c59e02SInna Palant```
265*65c59e02SInna Palant```cpp
266*65c59e02SInna Palantstruct JNested : JavaClass<JNested> {
267*65c59e02SInna Palant  static constexpr auto kJavaDescriptor = "Lcom/facebook/jni/Outer$Nested;";
268*65c59e02SInna Palant  static local_ref<JNested> create() {
269*65c59e02SInna Palant    return newInstance();
270*65c59e02SInna Palant  }
271*65c59e02SInna Palant};
272*65c59e02SInna Palant```
273*65c59e02SInna Palant
274*65c59e02SInna Palant
275*65c59e02SInna Palant## Defining a child class
276*65c59e02SInna Palant```java
277*65c59e02SInna Palantclass MyBaseClass {}
278*65c59e02SInna Palantclass MyDerivedClass extends MyBaseClass {}
279*65c59e02SInna Palant```
280*65c59e02SInna Palant```cpp
281*65c59e02SInna Palantstruct JMyBaseClass : JavaClass<JMyBaseClass> {
282*65c59e02SInna Palant  static constexpr auto kJavaDescriptor = "Lcom/facebook/jni/MyBaseClass;";
283*65c59e02SInna Palant};
284*65c59e02SInna Palant
285*65c59e02SInna Palantstruct JMyDerivedClass : JavaClass<JMyDerivedClass, JMyBaseClass> {
286*65c59e02SInna Palant  static constexpr auto kJavaDescriptor = "Lcom/facebook/jni/MyDerivedClass;";
287*65c59e02SInna Palant};
288*65c59e02SInna Palant
289*65c59e02SInna Palant```
290*65c59e02SInna PalantThis will allow implicit casts from Derived to Base and explicit downcasts.
291*65c59e02SInna PalantWhen no base class is given, JObject will be used as the base.
292*65c59e02SInna Palant```cpp
293*65c59e02SInna Palant  static void castReferences(
294*65c59e02SInna Palant      alias_ref<JClass> clazz,
295*65c59e02SInna Palant      alias_ref<JMyBaseClass> base) {
296*65c59e02SInna Palant    // Just like raw pointers, upcasting is implicit.
297*65c59e02SInna Palant    alias_ref<JObject> obj = base;
298*65c59e02SInna Palant    // static_ref_cast is like C++ static_cast.  No runtime checking is done.
299*65c59e02SInna Palant    alias_ref<JMyDerivedClass> derived_1 = static_ref_cast<JMyDerivedClass>(base);
300*65c59e02SInna Palant    // dynamic_ref_cast is like C++ dynamic_cast.
301*65c59e02SInna Palant    // It will check that the runtime Java type is actually derived from the target type.
302*65c59e02SInna Palant    try {
303*65c59e02SInna Palant      alias_ref<JMyDerivedClass> derived_2 = dynamic_ref_cast<JMyDerivedClass>(base);
304*65c59e02SInna Palant      (void)derived_2;
305*65c59e02SInna Palant    } catch (const JniException& exn) {
306*65c59e02SInna Palant      // Throws ClassCastException if the cast fails.
307*65c59e02SInna Palant      throw;
308*65c59e02SInna Palant    }
309*65c59e02SInna Palant```
310*65c59e02SInna Palant
311*65c59e02SInna Palant
312*65c59e02SInna Palant## Getting and setting fields
313*65c59e02SInna Palant```cpp
314*65c59e02SInna Palant  void getAndSetFields() {
315*65c59e02SInna Palant    static const auto cls = javaClassStatic();
316*65c59e02SInna Palant    // Primitive fields.
317*65c59e02SInna Palant    static const auto iField = cls->getField<jint>("i");
318*65c59e02SInna Palant    jint i = this->getFieldValue(iField);
319*65c59e02SInna Palant    this->setFieldValue(iField, i + 1);
320*65c59e02SInna Palant    // Object fields work for standard classes and your own JavaObject classes.
321*65c59e02SInna Palant    static const auto sField = cls->getField<JString>("s");
322*65c59e02SInna Palant    // Object are returned as local refs ...
323*65c59e02SInna Palant    local_ref<JString> s = this->getFieldValue(sField);
324*65c59e02SInna Palant    // and can be set from any ref.
325*65c59e02SInna Palant    this->setFieldValue(sField, make_jstring(s->toStdString() + "1").get());
326*65c59e02SInna Palant    // Static fields work the same, but getStaticField, getStaticFieldValue,
327*65c59e02SInna Palant    // and setStaticFieldValue must all be called on the class object.
328*65c59e02SInna Palant    static const auto someInstanceField = cls->getStaticField<JDataHolder>("someInstance");
329*65c59e02SInna Palant    auto inst = cls->getStaticFieldValue(someInstanceField);
330*65c59e02SInna Palant    if (!inst) {
331*65c59e02SInna Palant      // NOTE: Can't use cls here because it is declared const.
332*65c59e02SInna Palant      getClass()->setStaticFieldValue(someInstanceField, self());
333*65c59e02SInna Palant    }
334*65c59e02SInna Palant  }
335*65c59e02SInna Palant```
336*65c59e02SInna Palant
337*65c59e02SInna Palant
338*65c59e02SInna Palant## Working with JObject and JClass
339*65c59e02SInna Palant```cpp
340*65c59e02SInna Palant  static std::string showJObject(
341*65c59e02SInna Palant      alias_ref<JClass> clazz,
342*65c59e02SInna Palant      // JObject is the base class of all fbjni types.  It corresponds to java.lang.Object.
343*65c59e02SInna Palant      alias_ref<JObject> obj,
344*65c59e02SInna Palant      alias_ref<JDataHolder> data) {
345*65c59e02SInna Palant    local_ref<JClass> objClass = obj->getClass();
346*65c59e02SInna Palant    local_ref<JClass> dataClass = data->getClass();
347*65c59e02SInna Palant    local_ref<JClass> parent = dataClass->getSuperclass();
348*65c59e02SInna Palant    assert(isSameObject(parent, objClass));
349*65c59e02SInna Palant    assert(data->isInstanceOf(parent));
350*65c59e02SInna Palant    assert(objClass->isAssignableFrom(clazz));
351*65c59e02SInna Palant    std::string str = "data=";
352*65c59e02SInna Palant    {
353*65c59e02SInna Palant      // Acquires the object lock until this object goes out of scope.
354*65c59e02SInna Palant      auto lock = data->lock();
355*65c59e02SInna Palant      // Calls Object.toString and converts to std::string.
356*65c59e02SInna Palant      str += data->toString();
357*65c59e02SInna Palant    }
358*65c59e02SInna Palant    // All JavaClass types have a `javaobject` typedef, which is their raw JNI type.
359*65c59e02SInna Palant    static_assert(std::is_same<JObject::javaobject, jobject>::value, "");
360*65c59e02SInna Palant    static_assert(std::is_same<JClass::javaobject, jclass>::value, "");
361*65c59e02SInna Palant    static_assert(!std::is_same<JDataHolder::javaobject, jobject>::value, "");
362*65c59e02SInna Palant    static_assert(std::is_convertible<JDataHolder::javaobject, jobject>::value, "");
363*65c59e02SInna Palant    return str;
364*65c59e02SInna Palant  }
365*65c59e02SInna Palant```
366*65c59e02SInna Palant
367*65c59e02SInna Palant
368*65c59e02SInna Palant## Catching and throwing exceptions
369*65c59e02SInna Palant```cpp
370*65c59e02SInna Palant  static void catchAndThrow(
371*65c59e02SInna Palant      alias_ref<JClass> clazz) {
372*65c59e02SInna Palant    try {
373*65c59e02SInna Palant      clazz->getStaticMethod<void()>("doesNotExist");
374*65c59e02SInna Palant      assert(!"Exception wasn't thrown.");
375*65c59e02SInna Palant    } catch (JniException& exn) {
376*65c59e02SInna Palant      // JniException extends std::exception, so "catch (std::exception& exn)" also works.
377*65c59e02SInna Palant      local_ref<JThrowable> underlying = exn.getThrowable();
378*65c59e02SInna Palant      const char* msg = exn.what();
379*65c59e02SInna Palant      // Throwing exceptions from C++ is fine.
380*65c59e02SInna Palant      // They will be translated to an appropriate Java exception type.
381*65c59e02SInna Palant      throw std::runtime_error(std::string() + "Caught '" + msg + "'");
382*65c59e02SInna Palant    }
383*65c59e02SInna Palant  }
384*65c59e02SInna Palant```
385*65c59e02SInna Palant
386*65c59e02SInna Palant
387*65c59e02SInna Palant## Working with boxed primitives
388*65c59e02SInna Palant```java
389*65c59e02SInna Palant  static native Double scaleUp(Integer number);
390*65c59e02SInna Palant```
391*65c59e02SInna Palant```cpp
392*65c59e02SInna Palant  static local_ref<JDouble> scaleUp(
393*65c59e02SInna Palant      alias_ref<JClass> clazz,
394*65c59e02SInna Palant      alias_ref<JInteger> number) {
395*65c59e02SInna Palant    // Boxed types exist for all Java primitive types.
396*65c59e02SInna Palant    // Unbox with ->value() or ->intValue.
397*65c59e02SInna Palant    jint unboxed = number->value();
398*65c59e02SInna Palant    jdouble scaled = unboxed * 1.5;
399*65c59e02SInna Palant    // Box with autobox() or JDouble::valueOf.
400*65c59e02SInna Palant    local_ref<JDouble> ret = autobox(scaled);
401*65c59e02SInna Palant    return ret;
402*65c59e02SInna Palant  }
403*65c59e02SInna Palant```
404*65c59e02SInna Palant
405*65c59e02SInna Palant
406*65c59e02SInna Palant## Working with Iterables
407*65c59e02SInna Palant```java
408*65c59e02SInna Palant  static native String concatMatches(List<Integer> values, Map<String, Integer> names);
409*65c59e02SInna Palant```
410*65c59e02SInna Palant```cpp
411*65c59e02SInna Palant  static std::string concatMatches(
412*65c59e02SInna Palant      alias_ref<JClass> clazz,
413*65c59e02SInna Palant      // Note that generic types are *not* checked against Java declarations.
414*65c59e02SInna Palant      alias_ref<JList<JInteger>> values,
415*65c59e02SInna Palant      alias_ref<JMap<JString, JInteger>> names) {
416*65c59e02SInna Palant    int sum = 0;
417*65c59e02SInna Palant    std::string ret;
418*65c59e02SInna Palant    // Iterator and Iterable support C++ iteration.
419*65c59e02SInna Palant    // Collection, List, and Set support iteration and ->size().
420*65c59e02SInna Palant    for (const auto& elem : *values) {
421*65c59e02SInna Palant      sum += elem->value();
422*65c59e02SInna Palant    }
423*65c59e02SInna Palant    // Maps iterate like C++ maps.
424*65c59e02SInna Palant    for (const auto& entry : *names) {
425*65c59e02SInna Palant      if (entry.second->value() == sum) {
426*65c59e02SInna Palant        ret += entry.first->toStdString();
427*65c59e02SInna Palant      }
428*65c59e02SInna Palant    }
429*65c59e02SInna Palant    // This works if you build with C++17.
430*65c59e02SInna Palant    // for (const auto& [key, value] : *names) {
431*65c59e02SInna Palant    return ret;
432*65c59e02SInna Palant  }
433*65c59e02SInna Palant```
434*65c59e02SInna Palant
435*65c59e02SInna Palant
436*65c59e02SInna Palant## Building Collections
437*65c59e02SInna Palant```java
438*65c59e02SInna Palant  static native Map<String, List<Integer>> buildCollections();
439*65c59e02SInna Palant```
440*65c59e02SInna Palant```cpp
441*65c59e02SInna Palant  static local_ref<JMap<JString, JList<JInteger>>> buildCollections(
442*65c59e02SInna Palant      alias_ref<JClass> clazz) {
443*65c59e02SInna Palant    auto primes = JArrayList<JInteger>::create();
444*65c59e02SInna Palant    primes->add(autobox(2));
445*65c59e02SInna Palant    primes->add(autobox(3));
446*65c59e02SInna Palant    auto wrapper = JHashMap<JString, JList<JInteger>>::create();
447*65c59e02SInna Palant    wrapper->put(make_jstring("primes"), primes);
448*65c59e02SInna Palant    return wrapper;
449*65c59e02SInna Palant  }
450*65c59e02SInna Palant```
451*65c59e02SInna Palant
452*65c59e02SInna Palant
453*65c59e02SInna Palant## Transferring data with direct ByteBuffer
454*65c59e02SInna Palant```java
455*65c59e02SInna Palant  static native ByteBuffer transformBuffer(ByteBuffer data);
456*65c59e02SInna Palant  static void receiveBuffer(ByteBuffer buffer) {
457*65c59e02SInna Palant    assertThat(buffer.capacity()).isEqualTo(2);
458*65c59e02SInna Palant    assertThat(buffer.get(0)).isEqualTo((byte)2);
459*65c59e02SInna Palant    assertThat(buffer.get(1)).isEqualTo((byte)3);
460*65c59e02SInna Palant  }
461*65c59e02SInna Palant  @Test
462*65c59e02SInna Palant  public void testByteBuffers() {
463*65c59e02SInna Palant    ByteBuffer data = ByteBuffer.allocateDirect(2);
464*65c59e02SInna Palant    data.put(new byte[] {1, 2});
465*65c59e02SInna Palant    ByteBuffer transformed = transformBuffer(data);
466*65c59e02SInna Palant    receiveBuffer(transformed);
467*65c59e02SInna Palant  }
468*65c59e02SInna Palant```
469*65c59e02SInna Palant```cpp
470*65c59e02SInna Palant#include <fbjni/ByteBuffer.h>
471*65c59e02SInna Palant```
472*65c59e02SInna Palant```cpp
473*65c59e02SInna Palant  static local_ref<JByteBuffer> transformBuffer(
474*65c59e02SInna Palant      alias_ref<JClass> clazz,
475*65c59e02SInna Palant      alias_ref<JByteBuffer> data) {
476*65c59e02SInna Palant    // Direct ByteBuffers are an efficient way to transfer bulk data between Java and C++.
477*65c59e02SInna Palant    if (!data->isDirect()) {
478*65c59e02SInna Palant      throw std::runtime_error("Argument is not a direct buffer.");
479*65c59e02SInna Palant    }
480*65c59e02SInna Palant    // Transform data into a local buffer.
481*65c59e02SInna Palant    std::vector<uint8_t> buffer(data->getDirectSize());
482*65c59e02SInna Palant    uint8_t* raw_data = data->getDirectBytes();
483*65c59e02SInna Palant    for (size_t i = 0; i < buffer.size(); ++i) {
484*65c59e02SInna Palant      buffer[i] = raw_data[i] + 1;
485*65c59e02SInna Palant    }
486*65c59e02SInna Palant    // Wrap our data in a buffer and pass to Java.
487*65c59e02SInna Palant    // Note that the buffer *directly* references our memory.
488*65c59e02SInna Palant    local_ref<JByteBuffer> wrapper = JByteBuffer::wrapBytes(buffer.data(), buffer.size());
489*65c59e02SInna Palant    static const auto receiver = clazz->getStaticMethod<void(alias_ref<JByteBuffer>)>("receiveBuffer");
490*65c59e02SInna Palant    receiver(clazz, wrapper);
491*65c59e02SInna Palant    // We can create a new buffer that owns its own memory and safely return it.
492*65c59e02SInna Palant    local_ref<JByteBuffer> ret = JByteBuffer::allocateDirect(buffer.size());
493*65c59e02SInna Palant    std::memcpy(ret->getDirectBytes(), buffer.data(), buffer.size());
494*65c59e02SInna Palant    return ret;
495*65c59e02SInna Palant  }
496*65c59e02SInna Palant```
497*65c59e02SInna Palant
498*65c59e02SInna Palant
499