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