mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Migrate to FBJNI (#27729)
Summary: This is an incomplete effort to migrate from libfb to libfbjni. This is needed to restore the compatibility with Flipper and other FB Android projects that make use of FBJNI. Effectively, the outcome is that `fbjni` no longer has a checked-in copy here, but instead relies on the public artifacts published at github.com/facebookincubator/fbjni that can be deduplicated at build-time. **A non-exhaustive list of tasks:** * [X] Gradle builds the SDK and RNTester for Android. * [X] Buck build for rntester works in OSS. * [ ] Move from `java-only` release to full `fbjni` release. This requires finding a solution for stripping out `.so` files that the old `Android.mk` insists on including in the final artifacts and will clash with the full distribution. * [ ] Import this and fix potential internal build issues. * [ ] Verify that the changes made to the Hermes integration don't have any unintended consequences. ## Changelog [Android] [Changed] - Migrated from libfb to libfbjni for JNI calls Pull Request resolved: https://github.com/facebook/react-native/pull/27729 Test Plan: - CI is already passing again for Gradle and Buck in OSS. - After applying the following patch, RNTester builds and works with the latest Flipper SDK: ``` diff --git a/RNTester/android/app/build.gradle b/RNTester/android/app/build.gradle index b8a6437d7..eac942104 100644 --- a/RNTester/android/app/build.gradle +++ b/RNTester/android/app/build.gradle @@ -170,10 +170,19 @@ dependencies { debugImplementation files(hermesPath + "hermes-debug.aar") releaseImplementation files(hermesPath + "hermes-release.aar") - debugImplementation("com.facebook.flipper:flipper:0.23.4") { + debugImplementation("com.facebook.flipper🐬+") { exclude group:'com.facebook.yoga' - exclude group:'com.facebook.flipper', module: 'fbjni' - exclude group:'com.facebook.litho', module: 'litho-annotations' + exclude group:'com.facebook.fbjni' + } + + debugImplementation("com.facebook.flipper:flipper-network-plugin:+") { + exclude group:'com.facebook.yoga' + exclude group:'com.facebook.fbjni' + } + + debugImplementation("com.facebook.flipper:flipper-fresco-plugin:+") { + exclude group:'com.facebook.yoga' + exclude group:'com.facebook.fbjni' } if (useIntlJsc) { ``` Reviewed By: mdvacca Differential Revision: D19345270 Pulled By: passy fbshipit-source-id: 33811e7f97f44f2ec5999e1c35339909dc4fd3b1
This commit is contained in:
committed by
Rick Hanlon
parent
89eb287be1
commit
8c11d3ffce
@@ -8,6 +8,7 @@
|
||||
[maven_repositories]
|
||||
central = https://repo1.maven.org/maven2
|
||||
google = https://maven.google.com/
|
||||
jcenter = https://jcenter.bintray.com/
|
||||
|
||||
[alias]
|
||||
rntester = //RNTester/android/app:app
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.react.uiapp;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
@@ -296,7 +296,8 @@ def getNdkBuildFullPath() {
|
||||
}
|
||||
|
||||
def buildReactNdkLib = tasks.register("buildReactNdkLib", Exec) {
|
||||
dependsOn(prepareJSC, prepareHermes, prepareBoost, prepareDoubleConversion, prepareFolly, prepareGlog)
|
||||
dependsOn(prepareJSC, prepareHermes, prepareBoost, prepareDoubleConversion, prepareFolly, prepareGlog, extractAARHeaders, extractJNIFiles)
|
||||
|
||||
inputs.dir("$projectDir/../ReactCommon")
|
||||
inputs.dir("src/main/jni")
|
||||
inputs.dir("src/main/java/com/facebook/react/modules/blob")
|
||||
@@ -344,6 +345,34 @@ def packageReactNdkLibsForBuck = tasks.register("packageReactNdkLibsForBuck", Co
|
||||
into("src/main/jni/prebuilt/lib")
|
||||
}
|
||||
|
||||
task extractAARHeaders {
|
||||
doLast {
|
||||
configurations.extractHeaders.files.each {
|
||||
def file = it.absoluteFile
|
||||
def packageName = file.name.tokenize('-')[0]
|
||||
copy {
|
||||
from zipTree(file)
|
||||
into "$projectDir/src/main/jni/first-party/$packageName/headers"
|
||||
include "**/*.h"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
task extractJNIFiles {
|
||||
doLast {
|
||||
configurations.extractJNI.files.each {
|
||||
def file = it.absoluteFile
|
||||
def packageName = file.name.tokenize('-')[0]
|
||||
copy {
|
||||
from zipTree(file)
|
||||
into "$projectDir/src/main/jni/first-party/$packageName/"
|
||||
include "jni/**/*"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion 28
|
||||
|
||||
@@ -386,7 +415,8 @@ android {
|
||||
}
|
||||
|
||||
tasks.withType(JavaCompile) {
|
||||
compileTask -> compileTask.dependsOn(packageReactNdkLibs)
|
||||
compileTask ->
|
||||
compileTask.dependsOn(packageReactNdkLibs)
|
||||
}
|
||||
|
||||
clean.dependsOn(cleanReactNdkLib)
|
||||
@@ -394,10 +424,16 @@ android {
|
||||
lintOptions {
|
||||
abortOnError(false)
|
||||
}
|
||||
|
||||
packagingOptions {
|
||||
exclude("META-INF/NOTICE")
|
||||
exclude("META-INF/LICENSE")
|
||||
}
|
||||
|
||||
configurations {
|
||||
extractHeaders
|
||||
extractJNI
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
@@ -412,6 +448,9 @@ dependencies {
|
||||
api("com.squareup.okhttp3:okhttp:${OKHTTP_VERSION}")
|
||||
api("com.squareup.okhttp3:okhttp-urlconnection:${OKHTTP_VERSION}")
|
||||
api("com.squareup.okio:okio:1.15.0")
|
||||
api("com.facebook.fbjni:fbjni-java-only:0.0.3")
|
||||
extractHeaders("com.facebook.fbjni:fbjni:0.0.2:headers")
|
||||
extractJNI("com.facebook.fbjni:fbjni:0.0.2")
|
||||
|
||||
testImplementation("junit:junit:${JUNIT_VERSION}")
|
||||
testImplementation("org.powermock:powermock-api-mockito:${POWERMOCK_VERSION}")
|
||||
|
||||
@@ -5,7 +5,7 @@ rn_android_library(
|
||||
srcs = glob(["yoga/*.java"]),
|
||||
visibility = ["PUBLIC"],
|
||||
deps = [
|
||||
react_native_dep("java/com/facebook/jni:jni"),
|
||||
react_native_dep("libraries/fbjni:java"),
|
||||
react_native_dep("java/com/facebook/proguard/annotations:annotations"),
|
||||
react_native_dep("libraries/soloader/java/com/facebook/soloader:soloader"),
|
||||
react_native_dep("third-party/java/infer-annotations:infer-annotations"),
|
||||
|
||||
@@ -19,7 +19,7 @@ LOCAL_C_INCLUDES := $(LOCAL_PATH) $(REACT_NATIVE)/ReactCommon/jsi $(call find-no
|
||||
LOCAL_CPP_FEATURES := exceptions
|
||||
|
||||
LOCAL_STATIC_LIBRARIES := libjsireact libjsi
|
||||
LOCAL_SHARED_LIBRARIES := libfolly_json libfb libreactnativejni libhermes
|
||||
LOCAL_SHARED_LIBRARIES := libfolly_json libfb libfbjni libreactnativejni libhermes
|
||||
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ rn_android_library(
|
||||
deps = [
|
||||
react_native_dep("java/com/facebook/proguard/annotations:annotations"),
|
||||
react_native_dep("libraries/soloader/java/com/facebook/soloader:soloader"),
|
||||
react_native_dep("java/com/facebook/jni:jni"),
|
||||
react_native_dep("libraries/fbjni:java"),
|
||||
":jni_hermes_samplingprofiler",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#include <fb/fbjni.h>
|
||||
#include <fbjni/fbjni.h>
|
||||
#include <string>
|
||||
|
||||
namespace facebook {
|
||||
|
||||
+1
-2
@@ -8,8 +8,7 @@
|
||||
#ifndef HERMESSAMPLINGPROFILER_H_
|
||||
#define HERMESSAMPLINGPROFILER_H_
|
||||
|
||||
#include <fb/fbjni.h>
|
||||
#include <jni/Registration.h>
|
||||
#include <fbjni/fbjni.h>
|
||||
#include <jsi/jsi.h>
|
||||
|
||||
namespace facebook {
|
||||
|
||||
@@ -18,7 +18,7 @@ LOCAL_C_INCLUDES := $(LOCAL_PATH) $(REACT_NATIVE)/ReactCommon/jsi $(call find-no
|
||||
LOCAL_CPP_FEATURES := exceptions
|
||||
|
||||
LOCAL_STATIC_LIBRARIES := libjsireact libjsi
|
||||
LOCAL_SHARED_LIBRARIES := libfolly_json libfb libreactnativejni libhermes
|
||||
LOCAL_SHARED_LIBRARIES := libfolly_json libfb libfbjni libreactnativejni libhermes
|
||||
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
|
||||
@@ -35,6 +35,6 @@ LOCAL_C_INCLUDES := $(LOCAL_PATH) $(REACT_NATIVE)/ReactCommon/jsi $(call find-no
|
||||
LOCAL_CPP_FEATURES := exceptions
|
||||
|
||||
LOCAL_STATIC_LIBRARIES := libjsireact libjsi libhermes-inspector
|
||||
LOCAL_SHARED_LIBRARIES := libfolly_json libfb libreactnativejni libhermes
|
||||
LOCAL_SHARED_LIBRARIES := libfolly_json libfb libfbjni libreactnativejni libhermes
|
||||
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
#include <../instrumentation/HermesMemoryDumper.h>
|
||||
#include <HermesExecutorFactory.h>
|
||||
#include <fb/fbjni.h>
|
||||
#include <fbjni/fbjni.h>
|
||||
#include <hermes/Public/GCConfig.h>
|
||||
#include <hermes/Public/RuntimeConfig.h>
|
||||
#include <jni.h>
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "rn_android_library")
|
||||
|
||||
rn_android_library(
|
||||
name = "jni",
|
||||
srcs = glob(["**/*.java"]),
|
||||
proguard_config = "fbjni.pro",
|
||||
visibility = [
|
||||
"PUBLIC",
|
||||
],
|
||||
deps = [
|
||||
react_native_dep("third-party/android/androidx:annotation"),
|
||||
react_native_dep("java/com/facebook/proguard/annotations:annotations"),
|
||||
react_native_dep("libraries/soloader/java/com/facebook/soloader:soloader"),
|
||||
react_native_dep("third-party/java/jsr-305:jsr-305"),
|
||||
],
|
||||
)
|
||||
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.jni;
|
||||
|
||||
import com.facebook.proguard.annotations.DoNotStrip;
|
||||
import com.facebook.soloader.SoLoader;
|
||||
|
||||
/**
|
||||
* A Java Object that has native memory allocated corresponding to this instance.
|
||||
*
|
||||
* <p>NB: THREAD SAFETY (this comment also exists at Countable.cpp)
|
||||
*
|
||||
* <p>{@link #dispose} deletes the corresponding native object on whatever thread the method is
|
||||
* called on. In the common case when this is called by Countable#finalize(), this will be called on
|
||||
* the system finalizer thread. If you manually call dispose on the Java object, the native object
|
||||
* will be deleted synchronously on that thread.
|
||||
*/
|
||||
@DoNotStrip
|
||||
public class Countable {
|
||||
|
||||
static {
|
||||
SoLoader.loadLibrary("fb");
|
||||
}
|
||||
|
||||
// Private C++ instance
|
||||
@DoNotStrip private long mInstance = 0;
|
||||
|
||||
public native void dispose();
|
||||
|
||||
protected void finalize() throws Throwable {
|
||||
dispose();
|
||||
super.finalize();
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.jni;
|
||||
|
||||
import com.facebook.proguard.annotations.DoNotStrip;
|
||||
import com.facebook.soloader.SoLoader;
|
||||
|
||||
/** Utility class to determine CPU capabilities */
|
||||
@DoNotStrip
|
||||
public class CpuCapabilitiesJni {
|
||||
|
||||
static {
|
||||
SoLoader.loadLibrary("fb");
|
||||
}
|
||||
|
||||
@DoNotStrip
|
||||
public static native boolean nativeDeviceSupportsNeon();
|
||||
|
||||
@DoNotStrip
|
||||
public static native boolean nativeDeviceSupportsVFPFP16();
|
||||
|
||||
@DoNotStrip
|
||||
public static native boolean nativeDeviceSupportsX86();
|
||||
}
|
||||
@@ -1,139 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.jni;
|
||||
|
||||
import java.lang.ref.PhantomReference;
|
||||
import java.lang.ref.ReferenceQueue;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
/**
|
||||
* A thread which invokes the "destruct" routine for objects after they have been garbage collected.
|
||||
*
|
||||
* <p>An object which needs to be destructed should create a static subclass of {@link Destructor}.
|
||||
* Once the referent object is garbage collected, the DestructorThread will callback to the {@link
|
||||
* Destructor#destruct()} method.
|
||||
*
|
||||
* <p>The underlying thread in DestructorThread starts when the first Destructor is constructed and
|
||||
* then runs indefinitely.
|
||||
*/
|
||||
public class DestructorThread {
|
||||
|
||||
/**
|
||||
* N.B The Destructor <b>SHOULD NOT</b> refer back to its referent object either explicitly or
|
||||
* implicitly (for example, as a non-static inner class). This will create a reference cycle where
|
||||
* the referent object will never be garbage collected.
|
||||
*/
|
||||
public abstract static class Destructor extends PhantomReference<Object> {
|
||||
|
||||
private Destructor next;
|
||||
private Destructor previous;
|
||||
|
||||
public Destructor(Object referent) {
|
||||
super(referent, sReferenceQueue);
|
||||
sDestructorStack.push(this);
|
||||
}
|
||||
|
||||
private Destructor() {
|
||||
super(null, sReferenceQueue);
|
||||
}
|
||||
|
||||
/** Callback which is invoked when the original object has been garbage collected. */
|
||||
protected abstract void destruct();
|
||||
}
|
||||
|
||||
/** A list to keep all active Destructors in memory confined to the Destructor thread. */
|
||||
private static DestructorList sDestructorList;
|
||||
/** A thread safe stack where new Destructors are placed before being add to sDestructorList. */
|
||||
private static DestructorStack sDestructorStack;
|
||||
|
||||
private static ReferenceQueue sReferenceQueue;
|
||||
private static Thread sThread;
|
||||
|
||||
static {
|
||||
sDestructorStack = new DestructorStack();
|
||||
sReferenceQueue = new ReferenceQueue();
|
||||
sDestructorList = new DestructorList();
|
||||
sThread =
|
||||
new Thread("HybridData DestructorThread") {
|
||||
@Override
|
||||
public void run() {
|
||||
while (true) {
|
||||
try {
|
||||
Destructor current = (Destructor) sReferenceQueue.remove();
|
||||
current.destruct();
|
||||
|
||||
// If current is in the sDestructorStack,
|
||||
// transfer all the Destructors in the stack to the list.
|
||||
if (current.previous == null) {
|
||||
sDestructorStack.transferAllToList();
|
||||
}
|
||||
|
||||
DestructorList.drop(current);
|
||||
} catch (InterruptedException e) {
|
||||
// Continue. This thread should never be terminated.
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
sThread.start();
|
||||
}
|
||||
|
||||
private static class Terminus extends Destructor {
|
||||
@Override
|
||||
protected void destruct() {
|
||||
throw new IllegalStateException("Cannot destroy Terminus Destructor.");
|
||||
}
|
||||
}
|
||||
|
||||
/** This is a thread safe, lock-free Treiber-like Stack of Destructors. */
|
||||
private static class DestructorStack {
|
||||
private AtomicReference<Destructor> mHead = new AtomicReference<>();
|
||||
|
||||
public void push(Destructor newHead) {
|
||||
Destructor oldHead;
|
||||
do {
|
||||
oldHead = mHead.get();
|
||||
newHead.next = oldHead;
|
||||
} while (!mHead.compareAndSet(oldHead, newHead));
|
||||
}
|
||||
|
||||
public void transferAllToList() {
|
||||
Destructor current = mHead.getAndSet(null);
|
||||
while (current != null) {
|
||||
Destructor next = current.next;
|
||||
sDestructorList.enqueue(current);
|
||||
current = next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** A doubly-linked list of Destructors. */
|
||||
private static class DestructorList {
|
||||
private Destructor mHead;
|
||||
|
||||
public DestructorList() {
|
||||
mHead = new Terminus();
|
||||
mHead.next = new Terminus();
|
||||
mHead.next.previous = mHead;
|
||||
}
|
||||
|
||||
public void enqueue(Destructor current) {
|
||||
current.next = mHead.next;
|
||||
mHead.next = current;
|
||||
|
||||
current.next.previous = current;
|
||||
current.previous = mHead;
|
||||
}
|
||||
|
||||
private static void drop(Destructor current) {
|
||||
current.next.previous = current.previous;
|
||||
current.previous.next = current.next;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.jni;
|
||||
|
||||
import com.facebook.proguard.annotations.DoNotStrip;
|
||||
|
||||
@DoNotStrip
|
||||
public abstract class HybridClassBase extends HybridData {}
|
||||
@@ -1,78 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.jni;
|
||||
|
||||
import com.facebook.proguard.annotations.DoNotStrip;
|
||||
import com.facebook.soloader.SoLoader;
|
||||
|
||||
/**
|
||||
* This object holds a native C++ member for hybrid Java/C++ objects.
|
||||
*
|
||||
* <p>NB: THREAD SAFETY
|
||||
*
|
||||
* <p>{@link #resetNative} deletes the corresponding native object synchronously on whatever thread
|
||||
* the method is called on. Otherwise, deletion will occur on the {@link DestructorThread} thread.
|
||||
*/
|
||||
@DoNotStrip
|
||||
public class HybridData {
|
||||
|
||||
static {
|
||||
SoLoader.loadLibrary("fb");
|
||||
}
|
||||
|
||||
@DoNotStrip private Destructor mDestructor = new Destructor(this);
|
||||
|
||||
/**
|
||||
* To explicitly delete the instance, call resetNative(). If the C++ instance is referenced after
|
||||
* this is called, a NullPointerException will be thrown. resetNative() may be called multiple
|
||||
* times safely. Because the {@link DestructorThread} also calls resetNative, the instance will
|
||||
* not leak if this is not called, but timing of deletion and the thread the C++ dtor is called on
|
||||
* will be at the whim of the Java GC. If you want to control the thread and timing of the
|
||||
* destructor, you should call resetNative() explicitly.
|
||||
*/
|
||||
public synchronized void resetNative() {
|
||||
mDestructor.destruct();
|
||||
}
|
||||
|
||||
/**
|
||||
* N.B. Thread safety. If you call isValid from a different thread than {@link #resetNative()}
|
||||
* then be sure to do so while synchronizing on the hybrid. For example:
|
||||
*
|
||||
* <pre><code>
|
||||
* synchronized(hybrid) {
|
||||
* if (hybrid.isValid) {
|
||||
* // Do stuff.
|
||||
* }
|
||||
* }
|
||||
* </code></pre>
|
||||
*/
|
||||
public boolean isValid() {
|
||||
return mDestructor.mNativePointer != 0;
|
||||
}
|
||||
|
||||
public static class Destructor extends DestructorThread.Destructor {
|
||||
|
||||
// Private C++ instance
|
||||
@DoNotStrip private long mNativePointer;
|
||||
|
||||
Destructor(Object referent) {
|
||||
super(referent);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void destruct() {
|
||||
// When invoked from the DestructorThread instead of resetNative,
|
||||
// the DestructorThread has exclusive ownership of the HybridData
|
||||
// so synchronization is not necessary.
|
||||
deleteNative(mNativePointer);
|
||||
mNativePointer = 0;
|
||||
}
|
||||
|
||||
static native void deleteNative(long pointer);
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.jni;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import com.facebook.proguard.annotations.DoNotStrip;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* To iterate over an Iterator from C++ requires two calls per entry: hasNext() and next(). This
|
||||
* helper reduces it to one call and one field get per entry. It does not use a generic argument,
|
||||
* since in C++, the types will be erased, anyway. This is *not* a {@link java.util.Iterator}.
|
||||
*/
|
||||
@DoNotStrip
|
||||
public class IteratorHelper {
|
||||
private final Iterator mIterator;
|
||||
|
||||
// This is private, but accessed via JNI.
|
||||
@DoNotStrip private @Nullable Object mElement;
|
||||
|
||||
@DoNotStrip
|
||||
public IteratorHelper(Iterator iterator) {
|
||||
mIterator = iterator;
|
||||
}
|
||||
|
||||
@DoNotStrip
|
||||
public IteratorHelper(Iterable iterable) {
|
||||
mIterator = iterable.iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the helper to the next entry in the map, if any. Returns true iff there is an entry to
|
||||
* read.
|
||||
*/
|
||||
@DoNotStrip
|
||||
boolean hasNext() {
|
||||
if (mIterator.hasNext()) {
|
||||
mElement = mIterator.next();
|
||||
return true;
|
||||
} else {
|
||||
mElement = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.jni;
|
||||
|
||||
public class JniTerminateHandler {
|
||||
public static void handleTerminate(Throwable t) throws Throwable {
|
||||
Thread.UncaughtExceptionHandler h = Thread.getDefaultUncaughtExceptionHandler();
|
||||
if (h == null) {
|
||||
// Odd. Let the default std::terminate_handler deal with it.
|
||||
return;
|
||||
}
|
||||
h.uncaughtException(Thread.currentThread(), t);
|
||||
// That should exit. If it doesn't, let the default handler deal with it.
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.jni;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import com.facebook.proguard.annotations.DoNotStrip;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* To iterate over a Map from C++ requires four calls per entry: hasNext(), next(), getKey(),
|
||||
* getValue(). This helper reduces it to one call and two field gets per entry. It does not use a
|
||||
* generic argument, since in C++, the types will be erased, anyway. This is *not* a {@link
|
||||
* java.util.Iterator}.
|
||||
*/
|
||||
@DoNotStrip
|
||||
public class MapIteratorHelper {
|
||||
@DoNotStrip private final Iterator<Map.Entry> mIterator;
|
||||
@DoNotStrip private @Nullable Object mKey;
|
||||
@DoNotStrip private @Nullable Object mValue;
|
||||
|
||||
@DoNotStrip
|
||||
public MapIteratorHelper(Map map) {
|
||||
mIterator = map.entrySet().iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the helper to the next entry in the map, if any. Returns true iff there is an entry to
|
||||
* read.
|
||||
*/
|
||||
@DoNotStrip
|
||||
boolean hasNext() {
|
||||
if (mIterator.hasNext()) {
|
||||
Map.Entry entry = mIterator.next();
|
||||
mKey = entry.getKey();
|
||||
mValue = entry.getValue();
|
||||
return true;
|
||||
} else {
|
||||
mKey = null;
|
||||
mValue = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.jni;
|
||||
|
||||
import com.facebook.proguard.annotations.DoNotStrip;
|
||||
|
||||
/** A Runnable that has a native run implementation. */
|
||||
@DoNotStrip
|
||||
public class NativeRunnable implements Runnable {
|
||||
|
||||
@DoNotStrip private final HybridData mHybridData;
|
||||
|
||||
@DoNotStrip
|
||||
private NativeRunnable(HybridData hybridData) {
|
||||
mHybridData = hybridData;
|
||||
}
|
||||
|
||||
public native void run();
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.jni;
|
||||
|
||||
import com.facebook.proguard.annotations.DoNotStrip;
|
||||
import com.facebook.soloader.SoLoader;
|
||||
|
||||
@DoNotStrip
|
||||
public class ThreadScopeSupport {
|
||||
static {
|
||||
SoLoader.loadLibrary("fb");
|
||||
}
|
||||
|
||||
// This is just used for ThreadScope::withClassLoader to have a java function
|
||||
// in the stack so that jni has access to the correct classloader.
|
||||
@DoNotStrip
|
||||
private static void runStdFunction(long ptr) {
|
||||
runStdFunctionImpl(ptr);
|
||||
}
|
||||
|
||||
private static native void runStdFunctionImpl(long ptr);
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
# For common use cases for the hybrid pattern, keep symbols which may
|
||||
# be referenced only from C++.
|
||||
|
||||
-keepclassmembers class * {
|
||||
com.facebook.jni.HybridData *;
|
||||
<init>(com.facebook.jni.HybridData);
|
||||
}
|
||||
|
||||
-keepclasseswithmembers class * {
|
||||
com.facebook.jni.HybridData *;
|
||||
}
|
||||
@@ -50,7 +50,7 @@ rn_android_library(
|
||||
react_native_target("java/com/facebook/react/turbomodule/core:callinvokerholder"),
|
||||
] + ([react_native_target("jni/react/jni:jni")] if not IS_OSS_BUILD else []),
|
||||
exported_deps = [
|
||||
react_native_dep("java/com/facebook/jni:jni"),
|
||||
react_native_dep("libraries/fbjni:java"),
|
||||
react_native_dep("java/com/facebook/proguard/annotations:annotations"),
|
||||
react_native_dep("third-party/java/jsr-330:jsr-330"),
|
||||
react_native_target("java/com/facebook/react/bridge:interfaces"),
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
#include "StateWrapperImpl.h"
|
||||
|
||||
#import <better/set.h>
|
||||
#include <fb/fbjni.h>
|
||||
#include <fbjni/fbjni.h>
|
||||
#include <jsi/JSIDynamic.h>
|
||||
#include <jsi/jsi.h>
|
||||
#include <react/components/scrollview/ScrollViewProps.h>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <fb/fbjni.h>
|
||||
#include <fbjni/fbjni.h>
|
||||
#include <react/jni/JMessageQueueThread.h>
|
||||
#include <react/jni/ReadableNativeMap.h>
|
||||
#include <react/uimanager/Scheduler.h>
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@
|
||||
|
||||
#include "ComponentFactoryDelegate.h"
|
||||
#include <android/log.h>
|
||||
#include <fb/fbjni.h>
|
||||
#include <fbjni/fbjni.h>
|
||||
#include <jsi/jsi.h>
|
||||
#include <react/uimanager/ComponentDescriptorRegistry.h>
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <fb/fbjni.h>
|
||||
#include <fbjni/fbjni.h>
|
||||
#include <jsi/jsi.h>
|
||||
#include <react/uimanager/ComponentDescriptorRegistry.h>
|
||||
#include <react/uimanager/Scheduler.h>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
#include "EventBeatManager.h"
|
||||
#include <fb/fbjni.h>
|
||||
#include <fbjni/fbjni.h>
|
||||
using namespace facebook::jni;
|
||||
|
||||
namespace facebook {
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#include <mutex>
|
||||
#include <unordered_set>
|
||||
|
||||
#include <fb/fbjni.h>
|
||||
#include <fbjni/fbjni.h>
|
||||
#include <react/core/EventBeat.h>
|
||||
#include <react/utils/RuntimeExecutor.h>
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
#include "EventEmitterWrapper.h"
|
||||
#include <fb/fbjni.h>
|
||||
#include <fbjni/fbjni.h>
|
||||
|
||||
using namespace facebook::jni;
|
||||
|
||||
@@ -33,4 +33,4 @@ void EventEmitterWrapper::registerNatives() {
|
||||
}
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
||||
} // namespace facebook
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <fb/fbjni.h>
|
||||
#include <fbjni/fbjni.h>
|
||||
#include <react/core/EventEmitter.h>
|
||||
#include <react/jni/ReadableNativeMap.h>
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
#include "NodeStateWrapper.h"
|
||||
#include <fb/fbjni.h>
|
||||
#include <fbjni/fbjni.h>
|
||||
#include <react/jni/ReadableNativeMap.h>
|
||||
|
||||
using namespace facebook::jni;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <fb/fbjni.h>
|
||||
#include <fbjni/fbjni.h>
|
||||
#include <react/core/State.h>
|
||||
#include <react/jni/ReadableNativeMap.h>
|
||||
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#include <fb/fbjni.h>
|
||||
#include <fb/xplat_init.h>
|
||||
#include <fbjni/fbjni.h>
|
||||
|
||||
#include "Binding.h"
|
||||
#include "EventBeatManager.h"
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@
|
||||
|
||||
#import "ReactNativeConfigHolder.h"
|
||||
|
||||
#include <fb/fbjni.h>
|
||||
#include <fbjni/fbjni.h>
|
||||
|
||||
using namespace facebook::react;
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <fb/fbjni.h>
|
||||
#include <fbjni/fbjni.h>
|
||||
#include <react/config/ReactNativeConfig.h>
|
||||
#include <react/jni/JMessageQueueThread.h>
|
||||
#include <react/jni/ReadableNativeMap.h>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
#include "StateWrapperImpl.h"
|
||||
#include <fb/fbjni.h>
|
||||
#include <fbjni/fbjni.h>
|
||||
#include <react/jni/ReadableNativeMap.h>
|
||||
|
||||
using namespace facebook::jni;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <fb/fbjni.h>
|
||||
#include <fbjni/fbjni.h>
|
||||
#include <react/core/State.h>
|
||||
#include <react/jni/ReadableNativeMap.h>
|
||||
|
||||
|
||||
@@ -16,6 +16,6 @@ LOCAL_C_INCLUDES := $(LOCAL_PATH)
|
||||
LOCAL_CFLAGS += -fvisibility=hidden -fexceptions -frtti
|
||||
|
||||
LOCAL_STATIC_LIBRARIES := libjsi libjsireact jscruntime
|
||||
LOCAL_SHARED_LIBRARIES := libfolly_json libfb libreactnativejni
|
||||
LOCAL_SHARED_LIBRARIES := libfolly_json libfb libfbjni libreactnativejni
|
||||
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#include <fb/fbjni.h>
|
||||
#include <fbjni/fbjni.h>
|
||||
#include <jsi/JSCRuntime.h>
|
||||
#include <jsireact/JSIExecutor.h>
|
||||
#include <react/jni/JReactMarker.h>
|
||||
|
||||
@@ -16,6 +16,6 @@ LOCAL_C_INCLUDES := $(LOCAL_PATH)
|
||||
LOCAL_CFLAGS += -fvisibility=hidden -fexceptions -frtti
|
||||
|
||||
LOCAL_STATIC_LIBRARIES := libjsi libjsireact
|
||||
LOCAL_SHARED_LIBRARIES := libfolly_json libfb libreactnativejni
|
||||
LOCAL_SHARED_LIBRARIES := libfolly_json libfb libfbjni libreactnativejni
|
||||
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
#include "BlobCollector.h"
|
||||
|
||||
#include <fb/fbjni.h>
|
||||
#include <fbjni/fbjni.h>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <fb/fbjni.h>
|
||||
#include <fbjni/fbjni.h>
|
||||
#include <jsi/jsi.h>
|
||||
|
||||
namespace facebook {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#include <fb/fbjni.h>
|
||||
#include <fbjni/fbjni.h>
|
||||
|
||||
#include "BlobCollector.h"
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ rn_android_library(
|
||||
],
|
||||
deps = [
|
||||
react_native_dep("third-party/android/androidx:annotation"),
|
||||
react_native_dep("java/com/facebook/jni:jni"),
|
||||
react_native_dep("libraries/fbjni:java"),
|
||||
react_native_dep("java/com/facebook/proguard/annotations:annotations"),
|
||||
react_native_dep("libraries/fbcore/src/main/java/com/facebook/common/logging:logging"),
|
||||
react_native_dep("libraries/soloader/java/com/facebook/soloader:soloader"),
|
||||
|
||||
@@ -43,6 +43,6 @@ rn_android_library(
|
||||
deps = [
|
||||
react_native_dep("libraries/soloader/java/com/facebook/soloader:soloader"),
|
||||
react_native_target("java/com/facebook/react/turbomodule/core/interfaces:interfaces"),
|
||||
react_native_dep("java/com/facebook/jni:jni"),
|
||||
react_native_dep("libraries/fbjni:java"),
|
||||
],
|
||||
)
|
||||
|
||||
@@ -17,7 +17,7 @@ LOCAL_CFLAGS += -fexceptions -frtti -std=c++14 -Wall
|
||||
|
||||
LOCAL_STATIC_LIBRARIES = libcallinvoker
|
||||
|
||||
LOCAL_SHARED_LIBRARIES = libfb
|
||||
LOCAL_SHARED_LIBRARIES = libfb libfbjni
|
||||
|
||||
# Name of this module.
|
||||
LOCAL_MODULE := callinvokerholder
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <ReactCommon/CallInvoker.h>
|
||||
#include <fb/fbjni.h>
|
||||
#include <fbjni/fbjni.h>
|
||||
#include <memory>
|
||||
|
||||
namespace facebook {
|
||||
|
||||
+1
-1
@@ -5,8 +5,8 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#include <fb/fbjni.h>
|
||||
#include <fb/xplat_init.h>
|
||||
#include <fbjni/fbjni.h>
|
||||
|
||||
#include "TurboModuleManager.h"
|
||||
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <fb/fbjni.h>
|
||||
#include <fbjni/fbjni.h>
|
||||
#include <jsi/jsi.h>
|
||||
|
||||
#include <ReactCommon/TurboCxxModule.h>
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@
|
||||
#include <ReactCommon/JavaTurboModule.h>
|
||||
#include <ReactCommon/TurboModule.h>
|
||||
#include <ReactCommon/TurboModuleManagerDelegate.h>
|
||||
#include <fb/fbjni.h>
|
||||
#include <fbjni/fbjni.h>
|
||||
#include <jsi/jsi.h>
|
||||
#include <react/jni/CxxModuleWrapper.h>
|
||||
#include <react/jni/JMessageQueueThread.h>
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@
|
||||
|
||||
#include <ReactCommon/CallInvoker.h>
|
||||
#include <ReactCommon/JavaTurboModule.h>
|
||||
#include <fb/fbjni.h>
|
||||
#include <fbjni/fbjni.h>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
|
||||
@@ -9,21 +9,7 @@ include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_SRC_FILES:= \
|
||||
assert.cpp \
|
||||
jni/ByteBuffer.cpp \
|
||||
jni/Countable.cpp \
|
||||
jni/Environment.cpp \
|
||||
jni/Exceptions.cpp \
|
||||
jni/fbjni.cpp \
|
||||
jni/Hybrid.cpp \
|
||||
jni/jni_helpers.cpp \
|
||||
jni/LocalString.cpp \
|
||||
jni/OnLoad.cpp \
|
||||
jni/ReadableByteChannel.cpp \
|
||||
jni/References.cpp \
|
||||
jni/WeakReference.cpp \
|
||||
log.cpp \
|
||||
lyra/lyra.cpp \
|
||||
onload.cpp \
|
||||
|
||||
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
|
||||
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
|
||||
@@ -38,7 +24,6 @@ LOCAL_CFLAGS += -DHAVE_POSIX_CLOCKS
|
||||
|
||||
LOCAL_LDLIBS := -llog -ldl -landroid
|
||||
LOCAL_EXPORT_LDLIBS := -llog
|
||||
|
||||
LOCAL_MODULE := libfb
|
||||
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
PROJECT_NAME = "Facebook Android Support"
|
||||
JAVADOC_AUTOBRIEF = YES
|
||||
EXTRACT_ALL = YES
|
||||
RECURSIVE = YES
|
||||
EXCLUDE = tests
|
||||
EXCLUDE_PATTERNS = *.cpp
|
||||
GENERATE_HTML = YES
|
||||
GENERATE_LATEX = NO
|
||||
ENABLE_PREPROCESSING = YES
|
||||
HIDE_UNDOC_MEMBERS = YES
|
||||
HIDE_SCOPE_NAMES = YES
|
||||
HIDE_FRIEND_COMPOUNDS = YES
|
||||
HIDE_UNDOC_CLASSES = YES
|
||||
SHOW_INCLUDE_FILES = NO
|
||||
#ENABLED_SECTIONS = INTERNAL
|
||||
@@ -1,22 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#include <fb/Environment.h>
|
||||
#include <fb/ALog.h>
|
||||
#include <fb/fbjni/Common.h>
|
||||
#include <fb/fbjni/Exceptions.h>
|
||||
#include <fb/fbjni/ReferenceAllocators.h>
|
||||
#include <fb/fbjni/References.h>
|
||||
#include <fb/fbjni/Meta.h>
|
||||
#include <fb/fbjni/CoreClasses.h>
|
||||
#include <fb/fbjni/Iterator.h>
|
||||
#include <fb/fbjni/Hybrid.h>
|
||||
#include <fb/fbjni/Registration.h>
|
||||
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreClasses.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
namespace detail {
|
||||
template <typename T, typename jprim>
|
||||
struct JPrimitive : JavaClass<T> {
|
||||
using typename JavaClass<T>::javaobject;
|
||||
using JavaClass<T>::javaClassStatic;
|
||||
static local_ref<javaobject> valueOf(jprim val) {
|
||||
static auto cls = javaClassStatic();
|
||||
static auto method =
|
||||
cls->template getStaticMethod<javaobject(jprim)>("valueOf");
|
||||
return method(cls, val);
|
||||
}
|
||||
jprim value() const {
|
||||
static auto method =
|
||||
javaClassStatic()->template getMethod<jprim()>(T::kValueMethod);
|
||||
return method(this->self());
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
|
||||
#define DEFINE_BOXED_PRIMITIVE(LITTLE, BIG) \
|
||||
struct J ## BIG : detail::JPrimitive<J ## BIG, j ## LITTLE> { \
|
||||
static auto constexpr kJavaDescriptor = "Ljava/lang/" #BIG ";"; \
|
||||
static auto constexpr kValueMethod = #LITTLE "Value"; \
|
||||
j ## LITTLE LITTLE ## Value() const { \
|
||||
return value(); \
|
||||
} \
|
||||
}; \
|
||||
inline local_ref<jobject> autobox(j ## LITTLE val) { \
|
||||
return J ## BIG::valueOf(val); \
|
||||
}
|
||||
|
||||
DEFINE_BOXED_PRIMITIVE(boolean, Boolean)
|
||||
DEFINE_BOXED_PRIMITIVE(byte, Byte)
|
||||
DEFINE_BOXED_PRIMITIVE(char, Character)
|
||||
DEFINE_BOXED_PRIMITIVE(short, Short)
|
||||
DEFINE_BOXED_PRIMITIVE(int, Integer)
|
||||
DEFINE_BOXED_PRIMITIVE(long, Long)
|
||||
DEFINE_BOXED_PRIMITIVE(float, Float)
|
||||
DEFINE_BOXED_PRIMITIVE(double, Double)
|
||||
|
||||
#undef DEFINE_BOXED_PRIMITIVE
|
||||
|
||||
struct JVoid : public jni::JavaClass<JVoid> {
|
||||
static auto constexpr kJavaDescriptor = "Ljava/lang/Void;";
|
||||
};
|
||||
|
||||
inline local_ref<jobject> autobox(alias_ref<jobject> val) {
|
||||
return make_local(val);
|
||||
}
|
||||
|
||||
}}
|
||||
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <fb/visibility.h>
|
||||
|
||||
#include "CoreClasses.h"
|
||||
#include "References-forward.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
class JBuffer : public JavaClass<JBuffer> {
|
||||
public:
|
||||
static constexpr const char* kJavaDescriptor = "Ljava/nio/Buffer;";
|
||||
|
||||
void rewind() const;
|
||||
};
|
||||
|
||||
// JNI's NIO support has some awkward preconditions and error reporting. This
|
||||
// class provides much more user-friendly access.
|
||||
class FBEXPORT JByteBuffer : public JavaClass<JByteBuffer, JBuffer> {
|
||||
public:
|
||||
static constexpr const char* kJavaDescriptor = "Ljava/nio/ByteBuffer;";
|
||||
|
||||
static local_ref<JByteBuffer> wrapBytes(uint8_t* data, size_t size);
|
||||
|
||||
bool isDirect() const;
|
||||
|
||||
uint8_t* getDirectBytes() const;
|
||||
size_t getDirectSize() const;
|
||||
};
|
||||
|
||||
}}
|
||||
@@ -1,104 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/** @file Common.h
|
||||
*
|
||||
* Defining the stuff that don't deserve headers of their own...
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#include <fb/visibility.h>
|
||||
#include <fb/Environment.h>
|
||||
|
||||
#ifdef FBJNI_DEBUG_REFS
|
||||
# ifdef __ANDROID__
|
||||
# include <android/log.h>
|
||||
# else
|
||||
# include <cstdio>
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// If a pending JNI Java exception is found, wraps it in a JniException object and throws it as
|
||||
// a C++ exception.
|
||||
#define FACEBOOK_JNI_THROW_PENDING_EXCEPTION() \
|
||||
::facebook::jni::throwPendingJniExceptionAsCppException()
|
||||
|
||||
// If the condition is true, throws a JniException object, which wraps the pending JNI Java
|
||||
// exception if any. If no pending exception is found, throws a JniException object that wraps a
|
||||
// RuntimeException throwable.
|
||||
#define FACEBOOK_JNI_THROW_EXCEPTION_IF(CONDITION) \
|
||||
::facebook::jni::throwCppExceptionIf(CONDITION)
|
||||
|
||||
/// @cond INTERNAL
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
FBEXPORT void throwPendingJniExceptionAsCppException();
|
||||
FBEXPORT void throwCppExceptionIf(bool condition);
|
||||
|
||||
[[noreturn]] FBEXPORT void throwNewJavaException(jthrowable);
|
||||
[[noreturn]] FBEXPORT void throwNewJavaException(const char* throwableName, const char* msg);
|
||||
template<typename... Args>
|
||||
[[noreturn]] void throwNewJavaException(const char* throwableName, const char* fmt, Args... args);
|
||||
|
||||
|
||||
/**
|
||||
* This needs to be called at library load time, typically in your JNI_OnLoad method.
|
||||
*
|
||||
* The intended use is to return the result of initialize() directly
|
||||
* from JNI_OnLoad and to do nothing else there. Library specific
|
||||
* initialization code should go in the function passed to initialize
|
||||
* (which can be, and probably should be, a C++ lambda). This approach
|
||||
* provides correct error handling and translation errors during
|
||||
* initialization into Java exceptions when appropriate.
|
||||
*
|
||||
* Failure to call this will cause your code to crash in a remarkably
|
||||
* unhelpful way (typically a segfault) while trying to handle an exception
|
||||
* which occurs later.
|
||||
*/
|
||||
FBEXPORT jint initialize(JavaVM*, std::function<void()>&&) noexcept;
|
||||
|
||||
namespace internal {
|
||||
|
||||
/**
|
||||
* Retrieve a pointer the JNI environment of the current thread.
|
||||
*
|
||||
* @pre The current thread must be attached to the VM
|
||||
*/
|
||||
inline JNIEnv* getEnv() noexcept {
|
||||
// TODO(T6594868) Benchmark against raw JNI access
|
||||
return Environment::current();
|
||||
}
|
||||
|
||||
// Define to get extremely verbose logging of references and to enable reference stats
|
||||
#ifdef FBJNI_DEBUG_REFS
|
||||
template<typename... Args>
|
||||
inline void dbglog(const char* msg, Args... args) {
|
||||
# ifdef __ANDROID__
|
||||
__android_log_print(ANDROID_LOG_VERBOSE, "fbjni_dbg", msg, args...);
|
||||
# else
|
||||
std::fprintf(stderr, msg, args...);
|
||||
# endif
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
template<typename... Args>
|
||||
inline void dbglog(const char*, Args...) {
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
}}}
|
||||
|
||||
/// @endcond
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreClasses.h"
|
||||
#include "File.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
class AContext : public JavaClass<AContext> {
|
||||
public:
|
||||
static constexpr const char* kJavaDescriptor = "Landroid/content/Context;";
|
||||
|
||||
// Define a method that calls into the represented Java class
|
||||
local_ref<JFile::javaobject> getCacheDir() {
|
||||
static auto method = getClass()->getMethod<JFile::javaobject()>("getCacheDir");
|
||||
return method(self());
|
||||
}
|
||||
|
||||
local_ref<JFile::javaobject> getFilesDir() {
|
||||
static auto method = getClass()->getMethod<JFile::javaobject()>("getFilesDir");
|
||||
return method(self());
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,659 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string.h>
|
||||
#include <type_traits>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "Common.h"
|
||||
#include "Exceptions.h"
|
||||
#include "Meta.h"
|
||||
#include "MetaConvert.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
// jobject /////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
inline bool isSameObject(alias_ref<JObject> lhs, alias_ref<JObject> rhs) noexcept {
|
||||
return internal::getEnv()->IsSameObject(lhs.get(), rhs.get()) != JNI_FALSE;
|
||||
}
|
||||
|
||||
inline local_ref<JClass> JObject::getClass() const noexcept {
|
||||
return adopt_local(internal::getEnv()->GetObjectClass(self()));
|
||||
}
|
||||
|
||||
inline bool JObject::isInstanceOf(alias_ref<JClass> cls) const noexcept {
|
||||
return internal::getEnv()->IsInstanceOf(self(), cls.get()) != JNI_FALSE;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline T JObject::getFieldValue(JField<T> field) const noexcept {
|
||||
return field.get(self());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline local_ref<T*> JObject::getFieldValue(JField<T*> field) const noexcept {
|
||||
return adopt_local(field.get(self()));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void JObject::setFieldValue(JField<T> field, T value) noexcept {
|
||||
field.set(self(), value);
|
||||
}
|
||||
|
||||
inline std::string JObject::toString() const {
|
||||
static auto method = findClassLocal("java/lang/Object")->getMethod<jstring()>("toString");
|
||||
|
||||
return method(self())->toStdString();
|
||||
}
|
||||
|
||||
|
||||
// Class is here instead of CoreClasses.h because we need
|
||||
// alias_ref to be complete.
|
||||
class MonitorLock {
|
||||
public:
|
||||
inline MonitorLock() noexcept;
|
||||
inline MonitorLock(alias_ref<JObject> object) noexcept;
|
||||
inline ~MonitorLock() noexcept;
|
||||
|
||||
inline MonitorLock(MonitorLock&& other) noexcept;
|
||||
inline MonitorLock& operator=(MonitorLock&& other) noexcept;
|
||||
|
||||
inline MonitorLock(const MonitorLock&) = delete;
|
||||
inline MonitorLock& operator=(const MonitorLock&) = delete;
|
||||
|
||||
private:
|
||||
inline void reset() noexcept;
|
||||
alias_ref<JObject> owned_;
|
||||
};
|
||||
|
||||
MonitorLock::MonitorLock() noexcept : owned_(nullptr) {}
|
||||
|
||||
MonitorLock::MonitorLock(alias_ref<JObject> object) noexcept
|
||||
: owned_(object) {
|
||||
internal::getEnv()->MonitorEnter(object.get());
|
||||
}
|
||||
|
||||
void MonitorLock::reset() noexcept {
|
||||
if (owned_) {
|
||||
internal::getEnv()->MonitorExit(owned_.get());
|
||||
if (internal::getEnv()->ExceptionCheck()) {
|
||||
abort(); // Lock mismatch
|
||||
}
|
||||
owned_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
MonitorLock::~MonitorLock() noexcept {
|
||||
reset();
|
||||
}
|
||||
|
||||
MonitorLock::MonitorLock(MonitorLock&& other) noexcept
|
||||
: owned_(other.owned_)
|
||||
{
|
||||
other.owned_ = nullptr;
|
||||
}
|
||||
|
||||
MonitorLock& MonitorLock::operator=(MonitorLock&& other) noexcept {
|
||||
reset();
|
||||
owned_ = other.owned_;
|
||||
other.owned_ = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline MonitorLock JObject::lock() const noexcept {
|
||||
return MonitorLock(this_);
|
||||
}
|
||||
|
||||
inline jobject JObject::self() const noexcept {
|
||||
return this_;
|
||||
}
|
||||
|
||||
inline void swap(JObject& a, JObject& b) noexcept {
|
||||
using std::swap;
|
||||
swap(a.this_, b.this_);
|
||||
}
|
||||
|
||||
// JavaClass ///////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace detail {
|
||||
template<typename JC, typename... Args>
|
||||
static local_ref<JC> newInstance(Args... args) {
|
||||
static auto cls = JC::javaClassStatic();
|
||||
static auto constructor = cls->template getConstructor<typename JC::javaobject(Args...)>();
|
||||
return cls->newObject(constructor, args...);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename T, typename B, typename J>
|
||||
auto JavaClass<T, B, J>::self() const noexcept -> javaobject {
|
||||
return static_cast<javaobject>(JObject::self());
|
||||
}
|
||||
|
||||
// jclass //////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace detail {
|
||||
|
||||
// This is not a real type. It is used so people won't accidentally
|
||||
// use a void* to initialize a NativeMethod.
|
||||
struct NativeMethodWrapper;
|
||||
|
||||
}
|
||||
|
||||
struct NativeMethod {
|
||||
const char* name;
|
||||
std::string descriptor;
|
||||
detail::NativeMethodWrapper* wrapper;
|
||||
};
|
||||
|
||||
inline local_ref<JClass> JClass::getSuperclass() const noexcept {
|
||||
return adopt_local(internal::getEnv()->GetSuperclass(self()));
|
||||
}
|
||||
|
||||
inline void JClass::registerNatives(std::initializer_list<NativeMethod> methods) {
|
||||
const auto env = internal::getEnv();
|
||||
|
||||
JNINativeMethod jnimethods[methods.size()];
|
||||
size_t i = 0;
|
||||
for (auto it = methods.begin(); it < methods.end(); ++it, ++i) {
|
||||
jnimethods[i].name = it->name;
|
||||
jnimethods[i].signature = it->descriptor.c_str();
|
||||
jnimethods[i].fnPtr = reinterpret_cast<void*>(it->wrapper);
|
||||
}
|
||||
|
||||
auto result = env->RegisterNatives(self(), jnimethods, methods.size());
|
||||
FACEBOOK_JNI_THROW_EXCEPTION_IF(result != JNI_OK);
|
||||
}
|
||||
|
||||
inline bool JClass::isAssignableFrom(alias_ref<JClass> other) const noexcept {
|
||||
const auto env = internal::getEnv();
|
||||
// Ths method has behavior compatible with the
|
||||
// java.lang.Class#isAssignableFrom method. The order of the
|
||||
// arguments to the JNI IsAssignableFrom C function is "opposite"
|
||||
// from what some might expect, which makes this code look a little
|
||||
// odd, but it is correct.
|
||||
const auto result = env->IsAssignableFrom(other.get(), self());
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
inline JConstructor<F> JClass::getConstructor() const {
|
||||
return getConstructor<F>(jmethod_traits_from_cxx<F>::constructor_descriptor().c_str());
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
inline JConstructor<F> JClass::getConstructor(const char* descriptor) const {
|
||||
constexpr auto constructor_method_name = "<init>";
|
||||
return getMethod<F>(constructor_method_name, descriptor);
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
inline JMethod<F> JClass::getMethod(const char* name) const {
|
||||
return getMethod<F>(name, jmethod_traits_from_cxx<F>::descriptor().c_str());
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
inline JMethod<F> JClass::getMethod(
|
||||
const char* name,
|
||||
const char* descriptor) const {
|
||||
const auto env = internal::getEnv();
|
||||
const auto method = env->GetMethodID(self(), name, descriptor);
|
||||
FACEBOOK_JNI_THROW_EXCEPTION_IF(!method);
|
||||
return JMethod<F>{method};
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
inline JStaticMethod<F> JClass::getStaticMethod(const char* name) const {
|
||||
return getStaticMethod<F>(name, jmethod_traits_from_cxx<F>::descriptor().c_str());
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
inline JStaticMethod<F> JClass::getStaticMethod(
|
||||
const char* name,
|
||||
const char* descriptor) const {
|
||||
const auto env = internal::getEnv();
|
||||
const auto method = env->GetStaticMethodID(self(), name, descriptor);
|
||||
FACEBOOK_JNI_THROW_EXCEPTION_IF(!method);
|
||||
return JStaticMethod<F>{method};
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
inline JNonvirtualMethod<F> JClass::getNonvirtualMethod(const char* name) const {
|
||||
return getNonvirtualMethod<F>(name, jmethod_traits_from_cxx<F>::descriptor().c_str());
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
inline JNonvirtualMethod<F> JClass::getNonvirtualMethod(
|
||||
const char* name,
|
||||
const char* descriptor) const {
|
||||
const auto env = internal::getEnv();
|
||||
const auto method = env->GetMethodID(self(), name, descriptor);
|
||||
FACEBOOK_JNI_THROW_EXCEPTION_IF(!method);
|
||||
return JNonvirtualMethod<F>{method};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline JField<enable_if_t<IsJniScalar<T>(), T>>
|
||||
JClass::getField(const char* name) const {
|
||||
return getField<T>(name, jtype_traits<T>::descriptor().c_str());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline JField<enable_if_t<IsJniScalar<T>(), T>> JClass::getField(
|
||||
const char* name,
|
||||
const char* descriptor) const {
|
||||
const auto env = internal::getEnv();
|
||||
auto field = env->GetFieldID(self(), name, descriptor);
|
||||
FACEBOOK_JNI_THROW_EXCEPTION_IF(!field);
|
||||
return JField<T>{field};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline JStaticField<enable_if_t<IsJniScalar<T>(), T>> JClass::getStaticField(
|
||||
const char* name) const {
|
||||
return getStaticField<T>(name, jtype_traits<T>::descriptor().c_str());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline JStaticField<enable_if_t<IsJniScalar<T>(), T>> JClass::getStaticField(
|
||||
const char* name,
|
||||
const char* descriptor) const {
|
||||
const auto env = internal::getEnv();
|
||||
auto field = env->GetStaticFieldID(self(), name, descriptor);
|
||||
FACEBOOK_JNI_THROW_EXCEPTION_IF(!field);
|
||||
return JStaticField<T>{field};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline T JClass::getStaticFieldValue(JStaticField<T> field) const noexcept {
|
||||
return field.get(self());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline local_ref<T*> JClass::getStaticFieldValue(JStaticField<T*> field) noexcept {
|
||||
return adopt_local(field.get(self()));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void JClass::setStaticFieldValue(JStaticField<T> field, T value) noexcept {
|
||||
field.set(self(), value);
|
||||
}
|
||||
|
||||
template<typename R, typename... Args>
|
||||
inline local_ref<R> JClass::newObject(
|
||||
JConstructor<R(Args...)> constructor,
|
||||
Args... args) const {
|
||||
const auto env = internal::getEnv();
|
||||
auto object = env->NewObject(self(), constructor.getId(),
|
||||
detail::callToJni(
|
||||
detail::Convert<typename std::decay<Args>::type>::toCall(args))...);
|
||||
FACEBOOK_JNI_THROW_EXCEPTION_IF(!object);
|
||||
return adopt_local(static_cast<R>(object));
|
||||
}
|
||||
|
||||
inline jclass JClass::self() const noexcept {
|
||||
return static_cast<jclass>(JObject::self());
|
||||
}
|
||||
|
||||
inline void registerNatives(const char* name, std::initializer_list<NativeMethod> methods) {
|
||||
findClassLocal(name)->registerNatives(methods);
|
||||
}
|
||||
|
||||
|
||||
// jstring /////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
inline local_ref<JString> make_jstring(const std::string& modifiedUtf8) {
|
||||
return make_jstring(modifiedUtf8.c_str());
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
// convert to std::string from jstring
|
||||
template <>
|
||||
struct Convert<std::string> {
|
||||
typedef jstring jniType;
|
||||
static std::string fromJni(jniType t) {
|
||||
return wrap_alias(t)->toStdString();
|
||||
}
|
||||
static jniType toJniRet(const std::string& t) {
|
||||
return make_jstring(t).release();
|
||||
}
|
||||
static local_ref<JString> toCall(const std::string& t) {
|
||||
return make_jstring(t);
|
||||
}
|
||||
};
|
||||
|
||||
// convert return from const char*
|
||||
template <>
|
||||
struct Convert<const char*> {
|
||||
typedef jstring jniType;
|
||||
// no automatic synthesis of const char*. (It can't be freed.)
|
||||
static jniType toJniRet(const char* t) {
|
||||
return make_jstring(t).release();
|
||||
}
|
||||
static local_ref<JString> toCall(const char* t) {
|
||||
return make_jstring(t);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// jtypeArray //////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace detail {
|
||||
inline size_t JArray::size() const noexcept {
|
||||
const auto env = internal::getEnv();
|
||||
return env->GetArrayLength(self());
|
||||
}
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
template<typename Target>
|
||||
inline ElementProxy<Target>::ElementProxy(
|
||||
Target* target,
|
||||
size_t idx)
|
||||
: target_{target}, idx_{idx} {}
|
||||
|
||||
template<typename Target>
|
||||
inline ElementProxy<Target>& ElementProxy<Target>::operator=(const T& o) {
|
||||
target_->setElement(idx_, o);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename Target>
|
||||
inline ElementProxy<Target>& ElementProxy<Target>::operator=(alias_ref<T>& o) {
|
||||
target_->setElement(idx_, o.get());
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename Target>
|
||||
inline ElementProxy<Target>& ElementProxy<Target>::operator=(alias_ref<T>&& o) {
|
||||
target_->setElement(idx_, o.get());
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename Target>
|
||||
inline ElementProxy<Target>& ElementProxy<Target>::operator=(const ElementProxy<Target>& o) {
|
||||
auto src = o.target_->getElement(o.idx_);
|
||||
target_->setElement(idx_, src.get());
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename Target>
|
||||
inline ElementProxy<Target>::ElementProxy::operator const local_ref<T> () const {
|
||||
return target_->getElement(idx_);
|
||||
}
|
||||
|
||||
template<typename Target>
|
||||
inline ElementProxy<Target>::ElementProxy::operator local_ref<T> () {
|
||||
return target_->getElement(idx_);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::string JArrayClass<T>::get_instantiated_java_descriptor() {
|
||||
return "[" + jtype_traits<T>::descriptor();
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
std::string JArrayClass<T>::get_instantiated_base_name() {
|
||||
return get_instantiated_java_descriptor();
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
auto JArrayClass<T>::newArray(size_t size) -> local_ref<javaobject> {
|
||||
static auto elementClass = findClassStatic(jtype_traits<T>::base_name().c_str());
|
||||
const auto env = internal::getEnv();
|
||||
auto rawArray = env->NewObjectArray(size, elementClass.get(), nullptr);
|
||||
FACEBOOK_JNI_THROW_EXCEPTION_IF(!rawArray);
|
||||
return adopt_local(static_cast<javaobject>(rawArray));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void JArrayClass<T>::setElement(size_t idx, const T& value) {
|
||||
const auto env = internal::getEnv();
|
||||
env->SetObjectArrayElement(this->self(), idx, value);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline local_ref<T> JArrayClass<T>::getElement(size_t idx) {
|
||||
const auto env = internal::getEnv();
|
||||
auto rawElement = env->GetObjectArrayElement(this->self(), idx);
|
||||
return adopt_local(static_cast<T>(rawElement));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline detail::ElementProxy<JArrayClass<T>> JArrayClass<T>::operator[](size_t index) {
|
||||
return detail::ElementProxy<JArrayClass<T>>(this, index);
|
||||
}
|
||||
|
||||
// jarray /////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <typename JArrayType>
|
||||
auto JPrimitiveArray<JArrayType>::getRegion(jsize start, jsize length)
|
||||
-> std::unique_ptr<T[]> {
|
||||
using T = typename jtype_traits<JArrayType>::entry_type;
|
||||
auto buf = std::unique_ptr<T[]>{new T[length]};
|
||||
getRegion(start, length, buf.get());
|
||||
return buf;
|
||||
}
|
||||
|
||||
template <typename JArrayType>
|
||||
std::string JPrimitiveArray<JArrayType>::get_instantiated_java_descriptor() {
|
||||
return jtype_traits<JArrayType>::descriptor();
|
||||
}
|
||||
template <typename JArrayType>
|
||||
std::string JPrimitiveArray<JArrayType>::get_instantiated_base_name() {
|
||||
return JPrimitiveArray::get_instantiated_java_descriptor();
|
||||
}
|
||||
|
||||
template <typename JArrayType>
|
||||
auto JPrimitiveArray<JArrayType>::pin() -> PinnedPrimitiveArray<T, PinnedArrayAlloc<T>> {
|
||||
return PinnedPrimitiveArray<T, PinnedArrayAlloc<T>>{this->self(), 0, 0};
|
||||
}
|
||||
|
||||
template <typename JArrayType>
|
||||
auto JPrimitiveArray<JArrayType>::pinRegion(jsize start, jsize length)
|
||||
-> PinnedPrimitiveArray<T, PinnedRegionAlloc<T>> {
|
||||
return PinnedPrimitiveArray<T, PinnedRegionAlloc<T>>{this->self(), start, length};
|
||||
}
|
||||
|
||||
template <typename JArrayType>
|
||||
auto JPrimitiveArray<JArrayType>::pinCritical()
|
||||
-> PinnedPrimitiveArray<T, PinnedCriticalAlloc<T>> {
|
||||
return PinnedPrimitiveArray<T, PinnedCriticalAlloc<T>>{this->self(), 0, 0};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
class PinnedArrayAlloc {
|
||||
public:
|
||||
static void allocate(
|
||||
alias_ref<typename jtype_traits<T>::array_type> array,
|
||||
jsize start,
|
||||
jsize length,
|
||||
T** elements,
|
||||
size_t* size,
|
||||
jboolean* isCopy) {
|
||||
(void) start;
|
||||
(void) length;
|
||||
*elements = array->getElements(isCopy);
|
||||
*size = array->size();
|
||||
}
|
||||
static void release(
|
||||
alias_ref<typename jtype_traits<T>::array_type> array,
|
||||
T* elements,
|
||||
jint start,
|
||||
jint size,
|
||||
jint mode) {
|
||||
(void) start;
|
||||
(void) size;
|
||||
array->releaseElements(elements, mode);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class PinnedCriticalAlloc {
|
||||
public:
|
||||
static void allocate(
|
||||
alias_ref<typename jtype_traits<T>::array_type> array,
|
||||
jsize start,
|
||||
jsize length,
|
||||
T** elements,
|
||||
size_t* size,
|
||||
jboolean* isCopy) {
|
||||
const auto env = internal::getEnv();
|
||||
*elements = static_cast<T*>(env->GetPrimitiveArrayCritical(array.get(), isCopy));
|
||||
FACEBOOK_JNI_THROW_EXCEPTION_IF(!elements);
|
||||
*size = array->size();
|
||||
}
|
||||
static void release(
|
||||
alias_ref<typename jtype_traits<T>::array_type> array,
|
||||
T* elements,
|
||||
jint start,
|
||||
jint size,
|
||||
jint mode) {
|
||||
const auto env = internal::getEnv();
|
||||
env->ReleasePrimitiveArrayCritical(array.get(), elements, mode);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class PinnedRegionAlloc {
|
||||
public:
|
||||
static void allocate(
|
||||
alias_ref<typename jtype_traits<T>::array_type> array,
|
||||
jsize start,
|
||||
jsize length,
|
||||
T** elements,
|
||||
size_t* size,
|
||||
jboolean* isCopy) {
|
||||
auto buf = array->getRegion(start, length);
|
||||
FACEBOOK_JNI_THROW_EXCEPTION_IF(!buf);
|
||||
*elements = buf.release();
|
||||
*size = length;
|
||||
*isCopy = true;
|
||||
}
|
||||
static void release(
|
||||
alias_ref<typename jtype_traits<T>::array_type> array,
|
||||
T* elements,
|
||||
jint start,
|
||||
jint size,
|
||||
jint mode) {
|
||||
std::unique_ptr<T[]> holder;
|
||||
if (mode == 0 || mode == JNI_ABORT) {
|
||||
holder.reset(elements);
|
||||
}
|
||||
if (mode == 0 || mode == JNI_COMMIT) {
|
||||
array->setRegion(start, size, elements);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// PinnedPrimitiveArray ///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
PinnedPrimitiveArray<T, Alloc>::PinnedPrimitiveArray(PinnedPrimitiveArray&& o) {
|
||||
*this = std::move(o);
|
||||
}
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
PinnedPrimitiveArray<T, Alloc>&
|
||||
PinnedPrimitiveArray<T, Alloc>::operator=(PinnedPrimitiveArray&& o) {
|
||||
if (array_) {
|
||||
release();
|
||||
}
|
||||
array_ = std::move(o.array_);
|
||||
elements_ = o.elements_;
|
||||
isCopy_ = o.isCopy_;
|
||||
size_ = o.size_;
|
||||
start_ = o.start_;
|
||||
o.clear();
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
T* PinnedPrimitiveArray<T, Alloc>::get() {
|
||||
return elements_;
|
||||
}
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
inline void PinnedPrimitiveArray<T, Alloc>::release() {
|
||||
releaseImpl(0);
|
||||
clear();
|
||||
}
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
inline void PinnedPrimitiveArray<T, Alloc>::commit() {
|
||||
releaseImpl(JNI_COMMIT);
|
||||
}
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
inline void PinnedPrimitiveArray<T, Alloc>::abort() {
|
||||
releaseImpl(JNI_ABORT);
|
||||
clear();
|
||||
}
|
||||
|
||||
template <typename T, typename Alloc>
|
||||
inline void PinnedPrimitiveArray<T, Alloc>::releaseImpl(jint mode) {
|
||||
FACEBOOK_JNI_THROW_EXCEPTION_IF(array_.get() == nullptr);
|
||||
Alloc::release(array_, elements_, start_, size_, mode);
|
||||
}
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
inline void PinnedPrimitiveArray<T, Alloc>::clear() noexcept {
|
||||
array_ = nullptr;
|
||||
elements_ = nullptr;
|
||||
isCopy_ = false;
|
||||
start_ = 0;
|
||||
size_ = 0;
|
||||
}
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
inline T& PinnedPrimitiveArray<T, Alloc>::operator[](size_t index) {
|
||||
FACEBOOK_JNI_THROW_EXCEPTION_IF(elements_ == nullptr);
|
||||
return elements_[index];
|
||||
}
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
inline bool PinnedPrimitiveArray<T, Alloc>::isCopy() const noexcept {
|
||||
return isCopy_ == JNI_TRUE;
|
||||
}
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
inline size_t PinnedPrimitiveArray<T, Alloc>::size() const noexcept {
|
||||
return size_;
|
||||
}
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
inline PinnedPrimitiveArray<T, Alloc>::~PinnedPrimitiveArray() noexcept {
|
||||
if (elements_) {
|
||||
release();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
inline PinnedPrimitiveArray<T, Alloc>::PinnedPrimitiveArray(alias_ref<typename jtype_traits<T>::array_type> array, jint start, jint length) {
|
||||
array_ = array;
|
||||
start_ = start;
|
||||
Alloc::allocate(array, start, length, &elements_, &size_, &isCopy_);
|
||||
}
|
||||
|
||||
template<typename T, typename Base, typename JType>
|
||||
inline alias_ref<JClass> JavaClass<T, Base, JType>::javaClassStatic() {
|
||||
static auto cls = findClassStatic(jtype_traits<typename T::javaobject>::base_name().c_str());
|
||||
return cls;
|
||||
}
|
||||
|
||||
template<typename T, typename Base, typename JType>
|
||||
inline local_ref<JClass> JavaClass<T, Base, JType>::javaClassLocal() {
|
||||
std::string className(jtype_traits<typename T::javaobject>::base_name().c_str());
|
||||
return findClassLocal(className.c_str());
|
||||
}
|
||||
|
||||
}}
|
||||
@@ -1,615 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/** @file CoreClasses.h
|
||||
*
|
||||
* In CoreClasses.h wrappers for the core classes (jobject, jclass, and jstring) is defined
|
||||
* to provide access to corresponding JNI functions + some convenience.
|
||||
*/
|
||||
|
||||
#include "References-forward.h"
|
||||
#include "Meta-forward.h"
|
||||
#include "TypeTraits.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#include <fb/visibility.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
class JClass;
|
||||
class JObject;
|
||||
|
||||
/// Lookup a class by name. Note this functions returns an alias_ref that
|
||||
/// points to a leaked global reference. This is appropriate for classes
|
||||
/// that are never unloaded (which is any class in an Android app and most
|
||||
/// Java programs).
|
||||
///
|
||||
/// The most common use case for this is storing the result
|
||||
/// in a "static auto" variable, or a static global.
|
||||
///
|
||||
/// @return Returns a leaked global reference to the class
|
||||
FBEXPORT alias_ref<JClass> findClassStatic(const char* name);
|
||||
|
||||
/// Lookup a class by name. Note this functions returns a local reference,
|
||||
/// which means that it must not be stored in a static variable.
|
||||
///
|
||||
/// The most common use case for this is one-time initialization
|
||||
/// (like caching method ids).
|
||||
///
|
||||
/// @return Returns a global reference to the class
|
||||
FBEXPORT local_ref<JClass> findClassLocal(const char* name);
|
||||
|
||||
/// Check to see if two references refer to the same object. Comparison with nullptr
|
||||
/// returns true if and only if compared to another nullptr. A weak reference that
|
||||
/// refers to a reclaimed object count as nullptr.
|
||||
FBEXPORT bool isSameObject(alias_ref<JObject> lhs, alias_ref<JObject> rhs) noexcept;
|
||||
|
||||
// Together, these classes allow convenient use of any class with the fbjni
|
||||
// helpers. To use:
|
||||
//
|
||||
// struct MyClass : public JavaClass<MyClass> {
|
||||
// constexpr static auto kJavaDescriptor = "Lcom/example/package/MyClass;";
|
||||
// };
|
||||
//
|
||||
// Then, an alias_ref<MyClass::javaobject> will be backed by an instance of
|
||||
// MyClass. JavaClass provides a convenient way to add functionality to these
|
||||
// smart references.
|
||||
//
|
||||
// For example:
|
||||
//
|
||||
// struct MyClass : public JavaClass<MyClass> {
|
||||
// constexpr static auto kJavaDescriptor = "Lcom/example/package/MyClass;";
|
||||
//
|
||||
// void foo() {
|
||||
// static auto method = javaClassStatic()->getMethod<void()>("foo");
|
||||
// method(self());
|
||||
// }
|
||||
//
|
||||
// static local_ref<javaobject> create(int i) {
|
||||
// return newInstance(i);
|
||||
// }
|
||||
// };
|
||||
//
|
||||
// auto obj = MyClass::create(10);
|
||||
// obj->foo();
|
||||
//
|
||||
// While users of a JavaClass-type can lookup methods and fields through the
|
||||
// underlying JClass, those calls can only be checked at runtime. It is recommended
|
||||
// that the JavaClass-type instead explicitly expose it's methods as in the example
|
||||
// above.
|
||||
|
||||
namespace detail {
|
||||
template<typename JC, typename... Args>
|
||||
static local_ref<JC> newInstance(Args... args);
|
||||
}
|
||||
|
||||
class MonitorLock;
|
||||
|
||||
class FBEXPORT JObject : detail::JObjectBase {
|
||||
public:
|
||||
static constexpr auto kJavaDescriptor = "Ljava/lang/Object;";
|
||||
|
||||
static constexpr const char* get_instantiated_java_descriptor() { return nullptr; }
|
||||
static constexpr const char* get_instantiated_base_name() { return nullptr; }
|
||||
|
||||
/// Get a @ref local_ref of the object's class
|
||||
local_ref<JClass> getClass() const noexcept;
|
||||
|
||||
/// Checks if the object is an instance of a class
|
||||
bool isInstanceOf(alias_ref<JClass> cls) const noexcept;
|
||||
|
||||
/// Get the primitive value of a field
|
||||
template<typename T>
|
||||
T getFieldValue(JField<T> field) const noexcept;
|
||||
|
||||
/// Get and wrap the value of a field in a @ref local_ref
|
||||
template<typename T>
|
||||
local_ref<T*> getFieldValue(JField<T*> field) const noexcept;
|
||||
|
||||
/// Set the value of field. Any Java type is accepted, including the primitive types
|
||||
/// and raw reference types.
|
||||
template<typename T>
|
||||
void setFieldValue(JField<T> field, T value) noexcept;
|
||||
|
||||
/// Convenience method to create a std::string representing the object
|
||||
std::string toString() const;
|
||||
|
||||
// Take this object's monitor lock
|
||||
MonitorLock lock() const noexcept;
|
||||
|
||||
typedef _jobject _javaobject;
|
||||
typedef _javaobject* javaobject;
|
||||
|
||||
protected:
|
||||
jobject self() const noexcept;
|
||||
private:
|
||||
friend void swap(JObject& a, JObject& b) noexcept;
|
||||
template<typename>
|
||||
friend struct detail::ReprAccess;
|
||||
template<typename, typename, typename>
|
||||
friend class JavaClass;
|
||||
|
||||
template <typename, typename>
|
||||
friend class JObjectWrapper;
|
||||
};
|
||||
|
||||
// This is only to maintain backwards compatibility with things that are
|
||||
// already providing a specialization of JObjectWrapper. Any such instances
|
||||
// should be updated to use a JavaClass.
|
||||
template<>
|
||||
class JObjectWrapper<jobject> : public JObject {
|
||||
};
|
||||
|
||||
|
||||
namespace detail {
|
||||
template <typename, typename Base, typename JType>
|
||||
struct JTypeFor {
|
||||
static_assert(
|
||||
std::is_base_of<
|
||||
std::remove_pointer<jobject>::type,
|
||||
typename std::remove_pointer<JType>::type
|
||||
>::value, "");
|
||||
using _javaobject = typename std::remove_pointer<JType>::type;
|
||||
using javaobject = JType;
|
||||
};
|
||||
|
||||
template <typename T, typename Base>
|
||||
struct JTypeFor<T, Base, void> {
|
||||
// JNI pattern for jobject assignable pointer
|
||||
struct _javaobject : Base::_javaobject {
|
||||
// This allows us to map back to the defining type (in ReprType, for
|
||||
// example).
|
||||
typedef T JniRefRepr;
|
||||
};
|
||||
using javaobject = _javaobject*;
|
||||
};
|
||||
}
|
||||
|
||||
// JavaClass provides a method to inform fbjni about user-defined Java types.
|
||||
// Given a class:
|
||||
// struct Foo : JavaClass<Foo> {
|
||||
// static constexpr auto kJavaDescriptor = "Lcom/example/package/Foo;";
|
||||
// };
|
||||
// fbjni can determine the java type/method signatures for Foo::javaobject and
|
||||
// smart refs (like alias_ref<Foo::javaobject>) will hold an instance of Foo
|
||||
// and provide access to it through the -> and * operators.
|
||||
//
|
||||
// The "Base" template argument can be used to specify the JavaClass superclass
|
||||
// of this type (for instance, JString's Base is JObject).
|
||||
//
|
||||
// The "JType" template argument is used to provide a jni type (like jstring,
|
||||
// jthrowable) to be used as javaobject. This should only be necessary for
|
||||
// built-in jni types and not user-defined ones.
|
||||
template <typename T, typename Base = JObject, typename JType = void>
|
||||
class FBEXPORT JavaClass : public Base {
|
||||
using JObjType = typename detail::JTypeFor<T, Base, JType>;
|
||||
public:
|
||||
using _javaobject = typename JObjType::_javaobject;
|
||||
using javaobject = typename JObjType::javaobject;
|
||||
|
||||
using JavaBase = JavaClass;
|
||||
|
||||
static alias_ref<JClass> javaClassStatic();
|
||||
static local_ref<JClass> javaClassLocal();
|
||||
protected:
|
||||
/// Allocates a new object and invokes the specified constructor
|
||||
/// Like JClass's getConstructor, this function can only check at runtime if
|
||||
/// the class actually has a constructor that accepts the corresponding types.
|
||||
/// While a JavaClass-type can expose this function directly, it is recommended
|
||||
/// to instead to use this to explicitly only expose those constructors that
|
||||
/// the Java class actually has (i.e. with static create() functions).
|
||||
template<typename... Args>
|
||||
static local_ref<T> newInstance(Args... args) {
|
||||
return detail::newInstance<T>(args...);
|
||||
}
|
||||
|
||||
javaobject self() const noexcept;
|
||||
};
|
||||
|
||||
/// Wrapper to provide functionality to jclass references
|
||||
struct NativeMethod;
|
||||
|
||||
class FBEXPORT JClass : public JavaClass<JClass, JObject, jclass> {
|
||||
public:
|
||||
/// Java type descriptor
|
||||
static constexpr const char* kJavaDescriptor = "Ljava/lang/Class;";
|
||||
|
||||
/// Get a @local_ref to the super class of this class
|
||||
local_ref<JClass> getSuperclass() const noexcept;
|
||||
|
||||
/// Register native methods for the class. Usage looks like this:
|
||||
///
|
||||
/// classRef->registerNatives({
|
||||
/// makeNativeMethod("nativeMethodWithAutomaticDescriptor",
|
||||
/// methodWithAutomaticDescriptor),
|
||||
/// makeNativeMethod("nativeMethodWithExplicitDescriptor",
|
||||
/// "(Lcom/facebook/example/MyClass;)V",
|
||||
/// methodWithExplicitDescriptor),
|
||||
/// makeCriticalNativeMethod_DO_NOT_USE_OR_YOU_WILL_BE_FIRED("criticalNativeMethodWithAutomaticDescriptor",
|
||||
/// criticalNativeMethodWithAutomaticDescriptor),
|
||||
/// makeCriticalNativeMethod_DO_NOT_USE_OR_YOU_WILL_BE_FIRED("criticalNativeMethodWithExplicitDescriptor",
|
||||
/// "(IIF)Z",
|
||||
/// criticalNativeMethodWithExplicitDescriptor),
|
||||
/// });
|
||||
///
|
||||
/// By default, C++ exceptions raised will be converted to Java exceptions.
|
||||
/// To avoid this and get the "standard" JNI behavior of a crash when a C++
|
||||
/// exception is crashing out of the JNI method, declare the method noexcept.
|
||||
/// This does NOT apply to critical native methods, where exceptions causes
|
||||
/// a crash.
|
||||
void registerNatives(std::initializer_list<NativeMethod> methods);
|
||||
|
||||
/// Check to see if the class is assignable from another class
|
||||
/// @pre cls != nullptr
|
||||
bool isAssignableFrom(alias_ref<JClass> cls) const noexcept;
|
||||
|
||||
/// Convenience method to lookup the constructor with descriptor as specified by the
|
||||
/// type arguments
|
||||
template<typename F>
|
||||
JConstructor<F> getConstructor() const;
|
||||
|
||||
/// Convenience method to lookup the constructor with specified descriptor
|
||||
template<typename F>
|
||||
JConstructor<F> getConstructor(const char* descriptor) const;
|
||||
|
||||
/// Look up the method with given name and descriptor as specified with the type arguments
|
||||
template<typename F>
|
||||
JMethod<F> getMethod(const char* name) const;
|
||||
|
||||
/// Look up the method with given name and descriptor
|
||||
template<typename F>
|
||||
JMethod<F> getMethod(const char* name, const char* descriptor) const;
|
||||
|
||||
/// Lookup the field with the given name and deduced descriptor
|
||||
template<typename T>
|
||||
JField<enable_if_t<IsJniScalar<T>(), T>> getField(const char* name) const;
|
||||
|
||||
/// Lookup the field with the given name and descriptor
|
||||
template<typename T>
|
||||
JField<enable_if_t<IsJniScalar<T>(), T>> getField(const char* name, const char* descriptor) const;
|
||||
|
||||
/// Lookup the static field with the given name and deduced descriptor
|
||||
template<typename T>
|
||||
JStaticField<enable_if_t<IsJniScalar<T>(), T>> getStaticField(const char* name) const;
|
||||
|
||||
/// Lookup the static field with the given name and descriptor
|
||||
template<typename T>
|
||||
JStaticField<enable_if_t<IsJniScalar<T>(), T>> getStaticField(
|
||||
const char* name,
|
||||
const char* descriptor) const;
|
||||
|
||||
/// Get the primitive value of a static field
|
||||
template<typename T>
|
||||
T getStaticFieldValue(JStaticField<T> field) const noexcept;
|
||||
|
||||
/// Get and wrap the value of a field in a @ref local_ref
|
||||
template<typename T>
|
||||
local_ref<T*> getStaticFieldValue(JStaticField<T*> field) noexcept;
|
||||
|
||||
/// Set the value of field. Any Java type is accepted, including the primitive types
|
||||
/// and raw reference types.
|
||||
template<typename T>
|
||||
void setStaticFieldValue(JStaticField<T> field, T value) noexcept;
|
||||
|
||||
/// Allocates a new object and invokes the specified constructor
|
||||
template<typename R, typename... Args>
|
||||
local_ref<R> newObject(JConstructor<R(Args...)> constructor, Args... args) const;
|
||||
|
||||
/// Look up the static method with given name and descriptor as specified with the type arguments
|
||||
template<typename F>
|
||||
JStaticMethod<F> getStaticMethod(const char* name) const;
|
||||
|
||||
/// Look up the static method with given name and descriptor
|
||||
template<typename F>
|
||||
JStaticMethod<F> getStaticMethod(const char* name, const char* descriptor) const;
|
||||
|
||||
/// Look up the non virtual method with given name and descriptor as specified with the
|
||||
/// type arguments
|
||||
template<typename F>
|
||||
JNonvirtualMethod<F> getNonvirtualMethod(const char* name) const;
|
||||
|
||||
/// Look up the non virtual method with given name and descriptor
|
||||
template<typename F>
|
||||
JNonvirtualMethod<F> getNonvirtualMethod(const char* name, const char* descriptor) const;
|
||||
|
||||
private:
|
||||
jclass self() const noexcept;
|
||||
};
|
||||
|
||||
// Convenience method to register methods on a class without holding
|
||||
// onto the class object.
|
||||
void registerNatives(const char* name, std::initializer_list<NativeMethod> methods);
|
||||
|
||||
/// Wrapper to provide functionality to jstring references
|
||||
class FBEXPORT JString : public JavaClass<JString, JObject, jstring> {
|
||||
public:
|
||||
/// Java type descriptor
|
||||
static constexpr const char* kJavaDescriptor = "Ljava/lang/String;";
|
||||
|
||||
/// Convenience method to convert a jstring object to a std::string
|
||||
std::string toStdString() const;
|
||||
};
|
||||
|
||||
/// Convenience functions to convert a std::string or const char* into a @ref local_ref to a
|
||||
/// jstring
|
||||
FBEXPORT local_ref<JString> make_jstring(const char* modifiedUtf8);
|
||||
FBEXPORT local_ref<JString> make_jstring(const std::string& modifiedUtf8);
|
||||
|
||||
namespace detail {
|
||||
template<typename Target>
|
||||
class ElementProxy {
|
||||
private:
|
||||
Target* target_;
|
||||
size_t idx_;
|
||||
|
||||
public:
|
||||
using T = typename Target::javaentry;
|
||||
ElementProxy(Target* target, size_t idx);
|
||||
|
||||
ElementProxy& operator=(const T& o);
|
||||
|
||||
ElementProxy& operator=(alias_ref<T>& o);
|
||||
|
||||
ElementProxy& operator=(alias_ref<T>&& o);
|
||||
|
||||
ElementProxy& operator=(const ElementProxy& o);
|
||||
|
||||
operator const local_ref<T> () const;
|
||||
|
||||
operator local_ref<T> ();
|
||||
};
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
class FBEXPORT JArray : public JavaClass<JArray, JObject, jarray> {
|
||||
public:
|
||||
// This cannot be used in a scope that derives a descriptor (like in a method
|
||||
// signature). Use a more derived type instead (like JArrayInt or
|
||||
// JArrayClass<T>).
|
||||
static constexpr const char* kJavaDescriptor = nullptr;
|
||||
size_t size() const noexcept;
|
||||
};
|
||||
|
||||
// This is used so that the JArrayClass<T> javaobject extends jni's
|
||||
// jobjectArray. This class should not be used directly. A general Object[]
|
||||
// should use JArrayClass<jobject>.
|
||||
class FBEXPORT JTypeArray : public JavaClass<JTypeArray, JArray, jobjectArray> {
|
||||
// This cannot be used in a scope that derives a descriptor (like in a method
|
||||
// signature).
|
||||
static constexpr const char* kJavaDescriptor = nullptr;
|
||||
};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
class JArrayClass : public JavaClass<JArrayClass<T>, detail::JTypeArray> {
|
||||
public:
|
||||
static_assert(is_plain_jni_reference<T>(), "");
|
||||
// javaentry is the jni type of an entry in the array (i.e. jint).
|
||||
using javaentry = T;
|
||||
// javaobject is the jni type of the array.
|
||||
using javaobject = typename JavaClass<JArrayClass<T>, detail::JTypeArray>::javaobject;
|
||||
static constexpr const char* kJavaDescriptor = nullptr;
|
||||
static std::string get_instantiated_java_descriptor();
|
||||
static std::string get_instantiated_base_name();
|
||||
|
||||
/// Allocate a new array from Java heap, for passing as a JNI parameter or return value.
|
||||
/// NOTE: if using as a return value, you want to call release() instead of get() on the
|
||||
/// smart pointer.
|
||||
static local_ref<javaobject> newArray(size_t count);
|
||||
|
||||
/// Assign an object to the array.
|
||||
/// Typically you will use the shorthand (*ref)[idx]=value;
|
||||
void setElement(size_t idx, const T& value);
|
||||
|
||||
/// Read an object from the array.
|
||||
/// Typically you will use the shorthand
|
||||
/// T value = (*ref)[idx];
|
||||
/// If you use auto, you'll get an ElementProxy, which may need to be cast.
|
||||
local_ref<T> getElement(size_t idx);
|
||||
|
||||
/// EXPERIMENTAL SUBSCRIPT SUPPORT
|
||||
/// This implementation of [] returns a proxy object which then has a bunch of specializations
|
||||
/// (adopt_local free function, operator= and casting overloads on the ElementProxy) that can
|
||||
/// make code look like it is dealing with a T rather than an obvious proxy. In particular, the
|
||||
/// proxy in this iteration does not read a value and therefore does not create a LocalRef
|
||||
/// until one of these other operators is used. There are certainly holes that you may find
|
||||
/// by using idioms that haven't been tried yet. Consider yourself warned. On the other hand,
|
||||
/// it does make for some idiomatic assignment code; see TestBuildStringArray in fbjni_tests
|
||||
/// for some examples.
|
||||
detail::ElementProxy<JArrayClass> operator[](size_t idx);
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
using jtypeArray = typename JArrayClass<T>::javaobject;
|
||||
|
||||
template<typename T>
|
||||
local_ref<typename JArrayClass<T>::javaobject> adopt_local_array(jobjectArray ref) {
|
||||
return adopt_local(static_cast<typename JArrayClass<T>::javaobject>(ref));
|
||||
}
|
||||
|
||||
template<typename Target>
|
||||
local_ref<typename Target::javaentry> adopt_local(detail::ElementProxy<Target> elementProxy) {
|
||||
return static_cast<local_ref<typename Target::javaentry>>(elementProxy);
|
||||
}
|
||||
|
||||
template <typename T, typename PinAlloc>
|
||||
class PinnedPrimitiveArray;
|
||||
|
||||
template <typename T> class PinnedArrayAlloc;
|
||||
template <typename T> class PinnedRegionAlloc;
|
||||
template <typename T> class PinnedCriticalAlloc;
|
||||
|
||||
/// Wrapper to provide functionality to jarray references.
|
||||
/// This is an empty holder by itself. Construct a PinnedPrimitiveArray to actually interact with
|
||||
/// the elements of the array.
|
||||
template <typename JArrayType>
|
||||
class FBEXPORT JPrimitiveArray :
|
||||
public JavaClass<JPrimitiveArray<JArrayType>, detail::JArray, JArrayType> {
|
||||
static_assert(is_jni_primitive_array<JArrayType>(), "");
|
||||
public:
|
||||
static constexpr const char* kJavaDescriptor = nullptr;
|
||||
static std::string get_instantiated_java_descriptor();
|
||||
static std::string get_instantiated_base_name();
|
||||
|
||||
using T = typename jtype_traits<JArrayType>::entry_type;
|
||||
|
||||
static local_ref<JArrayType> newArray(size_t count);
|
||||
|
||||
void getRegion(jsize start, jsize length, T* buf);
|
||||
std::unique_ptr<T[]> getRegion(jsize start, jsize length);
|
||||
void setRegion(jsize start, jsize length, const T* buf);
|
||||
|
||||
/// Returns a view of the underlying array. This will either be a "pinned"
|
||||
/// version of the array (in which case changes to one immediately affect the
|
||||
/// other) or a copy of the array (in which cases changes to the view will take
|
||||
/// affect when destroyed or on calls to release()/commit()).
|
||||
PinnedPrimitiveArray<T, PinnedArrayAlloc<T>> pin();
|
||||
|
||||
/// Returns a view of part of the underlying array. A pinned region is always
|
||||
/// backed by a copy of the region.
|
||||
PinnedPrimitiveArray<T, PinnedRegionAlloc<T>> pinRegion(jsize start, jsize length);
|
||||
|
||||
/// Returns a view of the underlying array like pin(). However, while the pin
|
||||
/// is held, the code is considered within a "critical region". In a critical
|
||||
/// region, native code must not call JNI functions or make any calls that may
|
||||
/// block on other Java threads. These restrictions make it more likely that
|
||||
/// the view will be "pinned" rather than copied (for example, the VM may
|
||||
/// suspend garbage collection within a critical region).
|
||||
PinnedPrimitiveArray<T, PinnedCriticalAlloc<T>> pinCritical();
|
||||
|
||||
private:
|
||||
friend class PinnedArrayAlloc<T>;
|
||||
T* getElements(jboolean* isCopy);
|
||||
void releaseElements(T* elements, jint mode);
|
||||
};
|
||||
|
||||
FBEXPORT local_ref<jbooleanArray> make_boolean_array(jsize size);
|
||||
FBEXPORT local_ref<jbyteArray> make_byte_array(jsize size);
|
||||
FBEXPORT local_ref<jcharArray> make_char_array(jsize size);
|
||||
FBEXPORT local_ref<jshortArray> make_short_array(jsize size);
|
||||
FBEXPORT local_ref<jintArray> make_int_array(jsize size);
|
||||
FBEXPORT local_ref<jlongArray> make_long_array(jsize size);
|
||||
FBEXPORT local_ref<jfloatArray> make_float_array(jsize size);
|
||||
FBEXPORT local_ref<jdoubleArray> make_double_array(jsize size);
|
||||
|
||||
using JArrayBoolean = JPrimitiveArray<jbooleanArray>;
|
||||
using JArrayByte = JPrimitiveArray<jbyteArray>;
|
||||
using JArrayChar = JPrimitiveArray<jcharArray>;
|
||||
using JArrayShort = JPrimitiveArray<jshortArray>;
|
||||
using JArrayInt = JPrimitiveArray<jintArray>;
|
||||
using JArrayLong = JPrimitiveArray<jlongArray>;
|
||||
using JArrayFloat = JPrimitiveArray<jfloatArray>;
|
||||
using JArrayDouble = JPrimitiveArray<jdoubleArray>;
|
||||
|
||||
/// RAII class for pinned primitive arrays
|
||||
/// This currently only supports read/write access to existing java arrays. You can't create a
|
||||
/// primitive array this way yet. This class also pins the entire array into memory during the
|
||||
/// lifetime of the PinnedPrimitiveArray. If you need to unpin the array manually, call the
|
||||
/// release() or abort() functions. During a long-running block of code, you
|
||||
/// should unpin the array as soon as you're done with it, to avoid holding up
|
||||
/// the Java garbage collector.
|
||||
template <typename T, typename PinAlloc>
|
||||
class PinnedPrimitiveArray {
|
||||
public:
|
||||
static_assert(is_jni_primitive<T>::value,
|
||||
"PinnedPrimitiveArray requires primitive jni type.");
|
||||
|
||||
using ArrayType = typename jtype_traits<T>::array_type;
|
||||
|
||||
PinnedPrimitiveArray(PinnedPrimitiveArray&&);
|
||||
PinnedPrimitiveArray(const PinnedPrimitiveArray&) = delete;
|
||||
~PinnedPrimitiveArray() noexcept;
|
||||
|
||||
PinnedPrimitiveArray& operator=(PinnedPrimitiveArray&&);
|
||||
PinnedPrimitiveArray& operator=(const PinnedPrimitiveArray&) = delete;
|
||||
|
||||
T* get();
|
||||
void release();
|
||||
/// Unpins the array. If the array is a copy, pending changes are discarded.
|
||||
void abort();
|
||||
/// If the array is a copy, copies pending changes to the underlying java array.
|
||||
void commit();
|
||||
|
||||
bool isCopy() const noexcept;
|
||||
|
||||
const T& operator[](size_t index) const;
|
||||
T& operator[](size_t index);
|
||||
size_t size() const noexcept;
|
||||
|
||||
private:
|
||||
alias_ref<ArrayType> array_;
|
||||
size_t start_;
|
||||
T* elements_;
|
||||
jboolean isCopy_;
|
||||
size_t size_;
|
||||
|
||||
void allocate(alias_ref<ArrayType>, jint start, jint length);
|
||||
void releaseImpl(jint mode);
|
||||
void clear() noexcept;
|
||||
|
||||
PinnedPrimitiveArray(alias_ref<ArrayType>, jint start, jint length);
|
||||
|
||||
friend class JPrimitiveArray<typename jtype_traits<T>::array_type>;
|
||||
};
|
||||
|
||||
struct FBEXPORT JStackTraceElement : JavaClass<JStackTraceElement> {
|
||||
static auto constexpr kJavaDescriptor = "Ljava/lang/StackTraceElement;";
|
||||
|
||||
static local_ref<javaobject> create(const std::string& declaringClass, const std::string& methodName, const std::string& file, int line);
|
||||
|
||||
std::string getClassName() const;
|
||||
std::string getMethodName() const;
|
||||
std::string getFileName() const;
|
||||
int getLineNumber() const;
|
||||
};
|
||||
|
||||
/// Wrapper to provide functionality to jthrowable references
|
||||
class FBEXPORT JThrowable : public JavaClass<JThrowable, JObject, jthrowable> {
|
||||
public:
|
||||
static constexpr const char* kJavaDescriptor = "Ljava/lang/Throwable;";
|
||||
|
||||
using JStackTrace = JArrayClass<JStackTraceElement::javaobject>;
|
||||
|
||||
local_ref<JThrowable> initCause(alias_ref<JThrowable> cause);
|
||||
local_ref<JStackTrace> getStackTrace();
|
||||
void setStackTrace(alias_ref<JArrayClass<JStackTraceElement::javaobject>>);
|
||||
};
|
||||
|
||||
#pragma push_macro("PlainJniRefMap")
|
||||
#undef PlainJniRefMap
|
||||
#define PlainJniRefMap(rtype, jtype) \
|
||||
namespace detail { \
|
||||
template<> \
|
||||
struct RefReprType<jtype> { \
|
||||
using type = rtype; \
|
||||
}; \
|
||||
}
|
||||
|
||||
PlainJniRefMap(JArrayBoolean, jbooleanArray);
|
||||
PlainJniRefMap(JArrayByte, jbyteArray);
|
||||
PlainJniRefMap(JArrayChar, jcharArray);
|
||||
PlainJniRefMap(JArrayShort, jshortArray);
|
||||
PlainJniRefMap(JArrayInt, jintArray);
|
||||
PlainJniRefMap(JArrayLong, jlongArray);
|
||||
PlainJniRefMap(JArrayFloat, jfloatArray);
|
||||
PlainJniRefMap(JArrayDouble, jdoubleArray);
|
||||
PlainJniRefMap(JObject, jobject);
|
||||
PlainJniRefMap(JClass, jclass);
|
||||
PlainJniRefMap(JString, jstring);
|
||||
PlainJniRefMap(JThrowable, jthrowable);
|
||||
|
||||
#pragma pop_macro("PlainJniRefMap")
|
||||
|
||||
}}
|
||||
|
||||
#include "CoreClasses-inl.h"
|
||||
@@ -1,128 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file Exceptions.h
|
||||
*
|
||||
* After invoking a JNI function that can throw a Java exception, the macro
|
||||
* @ref FACEBOOK_JNI_THROW_PENDING_EXCEPTION() or @ref FACEBOOK_JNI_THROW_EXCEPTION_IF()
|
||||
* should be invoked.
|
||||
*
|
||||
* IMPORTANT! IMPORTANT! IMPORTANT! IMPORTANT! IMPORTANT! IMPORTANT! IMPORTANT! IMPORTANT!
|
||||
* To use these methods you MUST call initExceptionHelpers() when your library is loaded.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <alloca.h>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#include <fb/visibility.h>
|
||||
|
||||
#include "Common.h"
|
||||
#include "References.h"
|
||||
#include "CoreClasses.h"
|
||||
|
||||
#if defined(__ANDROID__) && defined(__ARM_ARCH_5TE__) && !defined(FBJNI_NO_EXCEPTION_PTR)
|
||||
// ARMv5 NDK does not support exception_ptr so we cannot use that when building for it.
|
||||
#define FBJNI_NO_EXCEPTION_PTR
|
||||
#endif
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
class JThrowable;
|
||||
|
||||
class JCppException : public JavaClass<JCppException, JThrowable> {
|
||||
public:
|
||||
static auto constexpr kJavaDescriptor = "Lcom/facebook/jni/CppException;";
|
||||
|
||||
static local_ref<JCppException> create(const char* str) {
|
||||
return newInstance(make_jstring(str));
|
||||
}
|
||||
|
||||
static local_ref<JCppException> create(const std::exception& ex) {
|
||||
return newInstance(make_jstring(ex.what()));
|
||||
}
|
||||
};
|
||||
|
||||
// JniException ////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* This class wraps a Java exception into a C++ exception; if the exception is routed back
|
||||
* to the Java side, it can be unwrapped and just look like a pure Java interaction. The class
|
||||
* is resilient to errors while creating the exception, falling back to some pre-allocated
|
||||
* exceptions if a new one cannot be allocated or populated.
|
||||
*
|
||||
* Note: the what() method of this class is not thread-safe (t6900503).
|
||||
*/
|
||||
class FBEXPORT JniException : public std::exception {
|
||||
public:
|
||||
JniException();
|
||||
~JniException();
|
||||
|
||||
explicit JniException(alias_ref<jthrowable> throwable);
|
||||
|
||||
JniException(JniException &&rhs);
|
||||
|
||||
JniException(const JniException &other);
|
||||
|
||||
local_ref<JThrowable> getThrowable() const noexcept;
|
||||
|
||||
virtual const char* what() const noexcept;
|
||||
|
||||
void setJavaException() const noexcept;
|
||||
|
||||
private:
|
||||
global_ref<JThrowable> throwable_;
|
||||
mutable std::string what_;
|
||||
mutable bool isMessageExtracted_;
|
||||
const static std::string kExceptionMessageFailure_;
|
||||
|
||||
void populateWhat() const noexcept;
|
||||
};
|
||||
|
||||
// Exception throwing & translating functions //////////////////////////////////////////////////////
|
||||
|
||||
// Functions that throw C++ exceptions
|
||||
|
||||
static const int kMaxExceptionMessageBufferSize = 512;
|
||||
|
||||
// These methods are the preferred way to throw a Java exception from
|
||||
// a C++ function. They create and throw a C++ exception which wraps
|
||||
// a Java exception, so the C++ flow is interrupted. Then, when
|
||||
// translatePendingCppExceptionToJavaException is called at the
|
||||
// topmost level of the native stack, the wrapped Java exception is
|
||||
// thrown to the java caller.
|
||||
template<typename... Args>
|
||||
[[noreturn]] void throwNewJavaException(const char* throwableName, const char* fmt, Args... args) {
|
||||
int msgSize = snprintf(nullptr, 0, fmt, args...);
|
||||
|
||||
char *msg = (char*) alloca(msgSize + 1);
|
||||
snprintf(msg, kMaxExceptionMessageBufferSize, fmt, args...);
|
||||
throwNewJavaException(throwableName, msg);
|
||||
}
|
||||
|
||||
// Identifies any pending C++ exception and throws it as a Java exception. If the exception can't
|
||||
// be thrown, it aborts the program.
|
||||
FBEXPORT void translatePendingCppExceptionToJavaException();
|
||||
|
||||
#ifndef FBJNI_NO_EXCEPTION_PTR
|
||||
FBEXPORT local_ref<JThrowable> getJavaExceptionForCppException(std::exception_ptr ptr);
|
||||
#endif
|
||||
|
||||
FBEXPORT local_ref<JThrowable> getJavaExceptionForCppBackTrace();
|
||||
|
||||
FBEXPORT local_ref<JThrowable> getJavaExceptionForCppBackTrace(const char* msg);
|
||||
// For convenience, some exception names in java.lang are available here.
|
||||
|
||||
const char* const gJavaLangIllegalArgumentException = "java/lang/IllegalArgumentException";
|
||||
|
||||
}}
|
||||
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreClasses.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
class JFile : public JavaClass<JFile> {
|
||||
public:
|
||||
static constexpr const char* kJavaDescriptor = "Ljava/io/File;";
|
||||
|
||||
// Define a method that calls into the represented Java class
|
||||
std::string getAbsolutePath() {
|
||||
static auto method = getClass()->getMethod<jstring()>("getAbsolutePath");
|
||||
return method(self())->toStdString();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,304 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
|
||||
#include <fb/assert.h>
|
||||
#include <fb/visibility.h>
|
||||
|
||||
#include "CoreClasses.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
namespace detail {
|
||||
|
||||
class BaseHybridClass {
|
||||
public:
|
||||
virtual ~BaseHybridClass() {}
|
||||
};
|
||||
|
||||
struct FBEXPORT HybridData : public JavaClass<HybridData> {
|
||||
constexpr static auto kJavaDescriptor = "Lcom/facebook/jni/HybridData;";
|
||||
static local_ref<HybridData> create();
|
||||
};
|
||||
|
||||
class HybridDestructor : public JavaClass<HybridDestructor> {
|
||||
public:
|
||||
static auto constexpr kJavaDescriptor = "Lcom/facebook/jni/HybridData$Destructor;";
|
||||
|
||||
template <typename T=detail::BaseHybridClass>
|
||||
T* getNativePointer() {
|
||||
static auto pointerField = javaClassStatic()->getField<jlong>("mNativePointer");
|
||||
auto* value = reinterpret_cast<detail::BaseHybridClass*>(getFieldValue(pointerField));
|
||||
if (!value) {
|
||||
throwNewJavaException("java/lang/NullPointerException", "java.lang.NullPointerException");
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
template <typename T=detail::BaseHybridClass>
|
||||
void setNativePointer(std::unique_ptr<T> new_value) {
|
||||
static auto pointerField = javaClassStatic()->getField<jlong>("mNativePointer");
|
||||
auto old_value = std::unique_ptr<T>(reinterpret_cast<T*>(getFieldValue(pointerField)));
|
||||
if (new_value && old_value) {
|
||||
FBCRASH("Attempt to set C++ native pointer twice");
|
||||
}
|
||||
setFieldValue(pointerField, reinterpret_cast<jlong>(new_value.release()));
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
detail::BaseHybridClass* getNativePointer(T t) {
|
||||
return getHolder(t)->getNativePointer();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void setNativePointer(T t, std::unique_ptr<detail::BaseHybridClass> new_value) {
|
||||
getHolder(t)->setNativePointer(std::move(new_value));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
local_ref<HybridDestructor> getHolder(T t) {
|
||||
static auto holderField = t->getClass()->template getField<HybridDestructor::javaobject>("mDestructor");
|
||||
return t->getFieldValue(holderField);
|
||||
}
|
||||
|
||||
// JavaClass for HybridClassBase
|
||||
struct FBEXPORT HybridClassBase : public JavaClass<HybridClassBase> {
|
||||
constexpr static auto kJavaDescriptor = "Lcom/facebook/jni/HybridClassBase;";
|
||||
|
||||
static bool isHybridClassBase(alias_ref<jclass> jclass) {
|
||||
return HybridClassBase::javaClassStatic()->isAssignableFrom(jclass);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Base, typename Enabled = void>
|
||||
struct HybridTraits {
|
||||
// This static assert should actually always fail if we don't use one of the
|
||||
// specializations below.
|
||||
static_assert(
|
||||
std::is_base_of<JObject, Base>::value ||
|
||||
std::is_base_of<BaseHybridClass, Base>::value,
|
||||
"The base of a HybridClass must be either another HybridClass or derived from JObject.");
|
||||
};
|
||||
|
||||
template <>
|
||||
struct HybridTraits<BaseHybridClass> {
|
||||
using CxxBase = BaseHybridClass;
|
||||
using JavaBase = JObject;
|
||||
};
|
||||
|
||||
template <typename Base>
|
||||
struct HybridTraits<
|
||||
Base,
|
||||
typename std::enable_if<std::is_base_of<BaseHybridClass, Base>::value>::type> {
|
||||
using CxxBase = Base;
|
||||
using JavaBase = typename Base::JavaPart;
|
||||
};
|
||||
|
||||
template <typename Base>
|
||||
struct HybridTraits<
|
||||
Base,
|
||||
typename std::enable_if<std::is_base_of<JObject, Base>::value>::type> {
|
||||
using CxxBase = BaseHybridClass;
|
||||
using JavaBase = Base;
|
||||
};
|
||||
|
||||
// convert to HybridClass* from jhybridobject
|
||||
template <typename T>
|
||||
struct FBEXPORT Convert<
|
||||
T, typename std::enable_if<
|
||||
std::is_base_of<BaseHybridClass, typename std::remove_pointer<T>::type>::value>::type> {
|
||||
typedef typename std::remove_pointer<T>::type::jhybridobject jniType;
|
||||
static T fromJni(jniType t) {
|
||||
if (t == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
return wrap_alias(t)->cthis();
|
||||
}
|
||||
// There is no automatic return conversion for objects.
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct RefReprType<T, typename std::enable_if<std::is_base_of<BaseHybridClass, T>::value, void>::type> {
|
||||
static_assert(std::is_same<T, void>::value,
|
||||
"HybridFoo (where HybridFoo derives from HybridClass<HybridFoo>) is not supported in this context. "
|
||||
"For an xxx_ref<HybridFoo>, you may want: xxx_ref<HybridFoo::javaobject> or HybridFoo*.");
|
||||
using Repr = T;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
template <typename T, typename Base = detail::BaseHybridClass>
|
||||
class FBEXPORT HybridClass : public detail::HybridTraits<Base>::CxxBase {
|
||||
public:
|
||||
struct JavaPart : JavaClass<JavaPart, typename detail::HybridTraits<Base>::JavaBase> {
|
||||
// At this point, T is incomplete, and so we cannot access
|
||||
// T::kJavaDescriptor directly. jtype_traits support this escape hatch for
|
||||
// such a case.
|
||||
static constexpr const char* kJavaDescriptor = nullptr;
|
||||
static std::string get_instantiated_java_descriptor();
|
||||
static std::string get_instantiated_base_name();
|
||||
|
||||
using HybridType = T;
|
||||
|
||||
// This will reach into the java object and extract the C++ instance from
|
||||
// the mHybridData and return it.
|
||||
T* cthis();
|
||||
|
||||
friend class HybridClass;
|
||||
};
|
||||
|
||||
using jhybridobject = typename JavaPart::javaobject;
|
||||
using javaobject = typename JavaPart::javaobject;
|
||||
typedef detail::HybridData::javaobject jhybriddata;
|
||||
|
||||
static alias_ref<JClass> javaClassStatic() {
|
||||
return JavaPart::javaClassStatic();
|
||||
}
|
||||
|
||||
static local_ref<JClass> javaClassLocal() {
|
||||
std::string className(T::kJavaDescriptor + 1, strlen(T::kJavaDescriptor) - 2);
|
||||
return findClassLocal(className.c_str());
|
||||
}
|
||||
|
||||
protected:
|
||||
typedef HybridClass HybridBase;
|
||||
|
||||
// This ensures that a C++ hybrid part cannot be created on its own
|
||||
// by default. If a hybrid wants to enable this, it can provide its
|
||||
// own public ctor, or change the accessibility of this to public.
|
||||
using detail::HybridTraits<Base>::CxxBase::CxxBase;
|
||||
|
||||
static void registerHybrid(std::initializer_list<NativeMethod> methods) {
|
||||
javaClassStatic()->registerNatives(methods);
|
||||
}
|
||||
|
||||
static local_ref<detail::HybridData> makeHybridData(std::unique_ptr<T> cxxPart) {
|
||||
auto hybridData = detail::HybridData::create();
|
||||
setNativePointer(hybridData, std::move(cxxPart));
|
||||
return hybridData;
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
static local_ref<detail::HybridData> makeCxxInstance(Args&&... args) {
|
||||
return makeHybridData(std::unique_ptr<T>(new T(std::forward<Args>(args)...)));
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
static void setCxxInstance(alias_ref<jhybridobject> o, Args&&... args) {
|
||||
setNativePointer(o, std::unique_ptr<T>(new T(std::forward<Args>(args)...)));
|
||||
}
|
||||
|
||||
public:
|
||||
// Factory method for creating a hybrid object where the arguments
|
||||
// are used to initialize the C++ part directly without passing them
|
||||
// through java. This method requires the Java part to have a ctor
|
||||
// which takes a HybridData, and for the C++ part to have a ctor
|
||||
// compatible with the arguments passed here. For safety, the ctor
|
||||
// can be private, and the hybrid declared a friend of its base, so
|
||||
// the hybrid can only be created from here.
|
||||
//
|
||||
// Exception behavior: This can throw an exception if creating the
|
||||
// C++ object fails, or any JNI methods throw.
|
||||
template <typename... Args>
|
||||
static local_ref<JavaPart> newObjectCxxArgs(Args&&... args) {
|
||||
static bool isHybrid = detail::HybridClassBase::isHybridClassBase(javaClassStatic());
|
||||
auto cxxPart = std::unique_ptr<T>(new T(std::forward<Args>(args)...));
|
||||
|
||||
local_ref<JavaPart> result;
|
||||
if (isHybrid) {
|
||||
result = JavaPart::newInstance();
|
||||
setNativePointer(result, std::move(cxxPart));
|
||||
}
|
||||
else {
|
||||
auto hybridData = makeHybridData(std::move(cxxPart));
|
||||
result = JavaPart::newInstance(hybridData);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// TODO? Create reusable interface for Allocatable classes and use it to
|
||||
// strengthen type-checking (and possibly provide a default
|
||||
// implementation of allocate().)
|
||||
template <typename... Args>
|
||||
static local_ref<jhybridobject> allocateWithCxxArgs(Args&&... args) {
|
||||
auto hybridData = makeCxxInstance(std::forward<Args>(args)...);
|
||||
static auto allocateMethod =
|
||||
javaClassStatic()->template getStaticMethod<jhybridobject(jhybriddata)>("allocate");
|
||||
return allocateMethod(javaClassStatic(), hybridData.get());
|
||||
}
|
||||
|
||||
// Factory method for creating a hybrid object where the arguments
|
||||
// are passed to the java ctor.
|
||||
template <typename... Args>
|
||||
static local_ref<JavaPart> newObjectJavaArgs(Args&&... args) {
|
||||
return JavaPart::newInstance(std::move(args)...);
|
||||
}
|
||||
|
||||
// If a hybrid class throws an exception which derives from
|
||||
// std::exception, it will be passed to mapException on the hybrid
|
||||
// class, or nearest ancestor. This allows boilerplate exception
|
||||
// translation code (for example, calling throwNewJavaException on a
|
||||
// particular java class) to be hoisted to a common function. If
|
||||
// mapException returns, then the std::exception will be translated
|
||||
// to Java.
|
||||
static void mapException(const std::exception& ex) {}
|
||||
};
|
||||
|
||||
template <typename T, typename B>
|
||||
inline T* HybridClass<T, B>::JavaPart::cthis() {
|
||||
detail::BaseHybridClass* result = 0;
|
||||
static bool isHybrid = detail::HybridClassBase::isHybridClassBase(this->getClass());
|
||||
if (isHybrid) {
|
||||
result = getNativePointer(this);
|
||||
} else {
|
||||
static auto field =
|
||||
HybridClass<T, B>::JavaPart::javaClassStatic()->template getField<detail::HybridData::javaobject>("mHybridData");
|
||||
auto hybridData = this->getFieldValue(field);
|
||||
if (!hybridData) {
|
||||
throwNewJavaException("java/lang/NullPointerException", "java.lang.NullPointerException");
|
||||
}
|
||||
|
||||
result = getNativePointer(hybridData);
|
||||
}
|
||||
|
||||
// This would require some serious programmer error.
|
||||
FBASSERTMSGF(result != 0, "Incorrect C++ type in hybrid field");
|
||||
// I'd like to use dynamic_cast here, but -fno-rtti is the default.
|
||||
return static_cast<T*>(result);
|
||||
};
|
||||
|
||||
template <typename T, typename B>
|
||||
/* static */ inline std::string HybridClass<T, B>::JavaPart::get_instantiated_java_descriptor() {
|
||||
return T::kJavaDescriptor;
|
||||
}
|
||||
|
||||
template <typename T, typename B>
|
||||
/* static */ inline std::string HybridClass<T, B>::JavaPart::get_instantiated_base_name() {
|
||||
auto name = get_instantiated_java_descriptor();
|
||||
return name.substr(1, name.size() - 2);
|
||||
}
|
||||
|
||||
// Given a *_ref object which refers to a hybrid class, this will reach inside
|
||||
// of it, find the mHybridData, extract the C++ instance pointer, cast it to
|
||||
// the appropriate type, and return it.
|
||||
template <typename T>
|
||||
inline auto cthis(T jthis) -> decltype(jthis->cthis()) {
|
||||
return jthis->cthis();
|
||||
}
|
||||
|
||||
void HybridDataOnLoad();
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,197 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename E>
|
||||
struct IteratorHelper : public JavaClass<IteratorHelper<E>> {
|
||||
constexpr static auto kJavaDescriptor = "Lcom/facebook/jni/IteratorHelper;";
|
||||
|
||||
typedef local_ref<E> value_type;
|
||||
typedef ptrdiff_t difference_type;
|
||||
typedef value_type* pointer;
|
||||
typedef value_type& reference;
|
||||
typedef std::forward_iterator_tag iterator_category;
|
||||
|
||||
typedef JavaClass<IteratorHelper<E>> JavaBase_;
|
||||
|
||||
bool hasNext() const {
|
||||
static auto hasNextMethod =
|
||||
JavaBase_::javaClassStatic()->template getMethod<jboolean()>("hasNext");
|
||||
return hasNextMethod(JavaBase_::self());
|
||||
}
|
||||
|
||||
value_type next() {
|
||||
static auto elementField =
|
||||
JavaBase_::javaClassStatic()->template getField<jobject>("mElement");
|
||||
return dynamic_ref_cast<E>(JavaBase_::getFieldValue(elementField));
|
||||
}
|
||||
|
||||
static void reset(value_type& v) {
|
||||
v.reset();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename K, typename V>
|
||||
struct MapIteratorHelper : public JavaClass<MapIteratorHelper<K,V>> {
|
||||
constexpr static auto kJavaDescriptor = "Lcom/facebook/jni/MapIteratorHelper;";
|
||||
|
||||
typedef std::pair<local_ref<K>, local_ref<V>> value_type;
|
||||
|
||||
typedef JavaClass<MapIteratorHelper<K,V>> JavaBase_;
|
||||
|
||||
bool hasNext() const {
|
||||
static auto hasNextMethod =
|
||||
JavaBase_::javaClassStatic()->template getMethod<jboolean()>("hasNext");
|
||||
return hasNextMethod(JavaBase_::self());
|
||||
}
|
||||
|
||||
value_type next() {
|
||||
static auto keyField = JavaBase_::javaClassStatic()->template getField<jobject>("mKey");
|
||||
static auto valueField = JavaBase_::javaClassStatic()->template getField<jobject>("mValue");
|
||||
return std::make_pair(dynamic_ref_cast<K>(JavaBase_::getFieldValue(keyField)),
|
||||
dynamic_ref_cast<V>(JavaBase_::getFieldValue(valueField)));
|
||||
}
|
||||
|
||||
static void reset(value_type& v) {
|
||||
v.first.reset();
|
||||
v.second.reset();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class Iterator {
|
||||
public:
|
||||
typedef typename T::value_type value_type;
|
||||
typedef ptrdiff_t difference_type;
|
||||
typedef value_type* pointer;
|
||||
typedef value_type& reference;
|
||||
typedef std::input_iterator_tag iterator_category;
|
||||
|
||||
// begin ctor
|
||||
Iterator(global_ref<typename T::javaobject>&& helper)
|
||||
: helper_(std::move(helper))
|
||||
, i_(-1) {
|
||||
++(*this);
|
||||
}
|
||||
|
||||
// end ctor
|
||||
Iterator()
|
||||
: i_(-1) {}
|
||||
|
||||
bool operator==(const Iterator& it) const { return i_ == it.i_; }
|
||||
bool operator!=(const Iterator& it) const { return !(*this == it); }
|
||||
const value_type& operator*() const { assert(i_ != -1); return entry_; }
|
||||
const value_type* operator->() const { assert(i_ != -1); return &entry_; }
|
||||
Iterator& operator++() { // preincrement
|
||||
bool hasNext = helper_->hasNext();
|
||||
if (hasNext) {
|
||||
++i_;
|
||||
entry_ = helper_->next();
|
||||
} else {
|
||||
i_ = -1;
|
||||
helper_->reset(entry_);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
Iterator operator++(int) { // postincrement
|
||||
Iterator ret;
|
||||
ret.i_ = i_;
|
||||
ret.entry_ = std::move(entry_);
|
||||
++(*this);
|
||||
return ret;
|
||||
}
|
||||
|
||||
global_ref<typename T::javaobject> helper_;
|
||||
// set to -1 at end
|
||||
std::ptrdiff_t i_;
|
||||
value_type entry_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
struct JIterator<E>::Iterator : public detail::Iterator<detail::IteratorHelper<E>> {
|
||||
using detail::Iterator<detail::IteratorHelper<E>>::Iterator;
|
||||
};
|
||||
|
||||
template <typename E>
|
||||
typename JIterator<E>::Iterator JIterator<E>::begin() const {
|
||||
static auto ctor = detail::IteratorHelper<E>::javaClassStatic()->
|
||||
template getConstructor<typename detail::IteratorHelper<E>::javaobject(
|
||||
typename JIterator<E>::javaobject)>();
|
||||
return Iterator(
|
||||
make_global(
|
||||
detail::IteratorHelper<E>::javaClassStatic()->newObject(ctor, this->self())));
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
typename JIterator<E>::Iterator JIterator<E>::end() const {
|
||||
return Iterator();
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
struct JIterable<E>::Iterator : public detail::Iterator<detail::IteratorHelper<E>> {
|
||||
using detail::Iterator<detail::IteratorHelper<E>>::Iterator;
|
||||
};
|
||||
|
||||
template <typename E>
|
||||
typename JIterable<E>::Iterator JIterable<E>::begin() const {
|
||||
static auto ctor = detail::IteratorHelper<E>::javaClassStatic()->
|
||||
template getConstructor<typename detail::IteratorHelper<E>::javaobject(
|
||||
typename JIterable<E>::javaobject)>();
|
||||
return Iterator(
|
||||
make_global(
|
||||
detail::IteratorHelper<E>::javaClassStatic()->newObject(ctor, this->self())));
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
typename JIterable<E>::Iterator JIterable<E>::end() const {
|
||||
return Iterator();
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
size_t JCollection<E>::size() const {
|
||||
static auto sizeMethod =
|
||||
JCollection<E>::javaClassStatic()->template getMethod<jint()>("size");
|
||||
return sizeMethod(this->self());
|
||||
}
|
||||
|
||||
template <typename K, typename V>
|
||||
struct JMap<K,V>::Iterator : public detail::Iterator<detail::MapIteratorHelper<K,V>> {
|
||||
using detail::Iterator<detail::MapIteratorHelper<K,V>>::Iterator;
|
||||
};
|
||||
|
||||
template <typename K, typename V>
|
||||
size_t JMap<K,V>::size() const {
|
||||
static auto sizeMethod =
|
||||
JMap<K,V>::javaClassStatic()->template getMethod<jint()>("size");
|
||||
return sizeMethod(this->self());
|
||||
}
|
||||
|
||||
template <typename K, typename V>
|
||||
typename JMap<K,V>::Iterator JMap<K,V>::begin() const {
|
||||
static auto ctor = detail::MapIteratorHelper<K,V>::javaClassStatic()->
|
||||
template getConstructor<typename detail::MapIteratorHelper<K,V>::javaobject(
|
||||
typename JMap<K,V>::javaobject)>();
|
||||
return Iterator(
|
||||
make_global(
|
||||
detail::MapIteratorHelper<K,V>::javaClassStatic()->newObject(ctor, this->self())));
|
||||
}
|
||||
|
||||
template <typename K, typename V>
|
||||
typename JMap<K,V>::Iterator JMap<K,V>::end() const {
|
||||
return Iterator();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,144 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreClasses.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
/**
|
||||
* JavaClass which represents a reference to a java.util.Iterator instance. It
|
||||
* provides begin()/end() methods to provide C++-style iteration over the
|
||||
* underlying collection. The class has a template parameter for the element
|
||||
* type, which defaults to jobject. For example:
|
||||
*
|
||||
* alias_ref<JIterator<jstring>::javaobject> my_iter = ...;
|
||||
*
|
||||
* In the simplest case, it can be used just as alias_ref<JIterator<>::javaobject>,
|
||||
* for example in a method declaration.
|
||||
*/
|
||||
template <typename E = jobject>
|
||||
struct JIterator : JavaClass<JIterator<E>> {
|
||||
constexpr static auto kJavaDescriptor = "Ljava/util/Iterator;";
|
||||
|
||||
struct Iterator;
|
||||
|
||||
/**
|
||||
* To iterate:
|
||||
*
|
||||
* for (const auto& element : *jiter) { ... }
|
||||
*
|
||||
* The JIterator iterator value_type is local_ref<E>, containing a reference
|
||||
* to an element instance.
|
||||
*
|
||||
* If the Iterator returns objects whch are not convertible to the given
|
||||
* element type, iteration will throw a java ClassCastException.
|
||||
*
|
||||
* For example, to convert an iterator over a collection of java strings to
|
||||
* an std::vector of std::strings:
|
||||
*
|
||||
* std::vector<std::string> vs;
|
||||
* for (const auto& elem : *jiter) {
|
||||
* vs.push_back(elem->toStdString());
|
||||
* }
|
||||
*
|
||||
* Or if you prefer using std algorithms:
|
||||
*
|
||||
* std::vector<std::string> vs;
|
||||
* std::transform(jiter->begin(), jiter->end(), std::back_inserter(vs),
|
||||
* [](const local_ref<jstring>& elem) { return elem->toStdString(); });
|
||||
*
|
||||
* The iterator is a InputIterator.
|
||||
*/
|
||||
Iterator begin() const;
|
||||
Iterator end() const;
|
||||
};
|
||||
|
||||
/**
|
||||
* Similar to JIterator, except this represents any object which implements the
|
||||
* java.lang.Iterable interface. It will create the Java Iterator as a part of
|
||||
* begin().
|
||||
*/
|
||||
template <typename E = jobject>
|
||||
struct JIterable : JavaClass<JIterable<E>> {
|
||||
constexpr static auto kJavaDescriptor = "Ljava/lang/Iterable;";
|
||||
|
||||
struct Iterator;
|
||||
|
||||
Iterator begin() const;
|
||||
Iterator end() const;
|
||||
};
|
||||
|
||||
/**
|
||||
* JavaClass types which represent Collection, List, and Set are also provided.
|
||||
* These preserve the Java class hierarchy.
|
||||
*/
|
||||
template <typename E = jobject>
|
||||
struct JCollection : JavaClass<JCollection<E>, JIterable<E>> {
|
||||
constexpr static auto kJavaDescriptor = "Ljava/util/Collection;";
|
||||
|
||||
/**
|
||||
* Returns the number of elements in the collection.
|
||||
*/
|
||||
size_t size() const;
|
||||
};
|
||||
|
||||
template <typename E = jobject>
|
||||
struct JList : JavaClass<JList<E>, JCollection<E>> {
|
||||
constexpr static auto kJavaDescriptor = "Ljava/util/List;";
|
||||
};
|
||||
|
||||
template <typename E = jobject>
|
||||
struct JSet : JavaClass<JSet<E>, JCollection<E>> {
|
||||
constexpr static auto kJavaDescriptor = "Ljava/util/Set;";
|
||||
};
|
||||
|
||||
/**
|
||||
* JavaClass which represents a reference to a java.util.Map instance. It adds
|
||||
* wrappers around Java methods, including begin()/end() methods to provide
|
||||
* C++-style iteration over the Java Map. The class has template parameters
|
||||
* for the key and value types, which default to jobject. For example:
|
||||
*
|
||||
* alias_ref<JMap<jstring, MyJClass::javaobject>::javaobject> my_map = ...;
|
||||
*
|
||||
* In the simplest case, it can be used just as alias_ref<JMap<>::javaobject>,
|
||||
* for example in a method declaration.
|
||||
*/
|
||||
template <typename K = jobject, typename V = jobject>
|
||||
struct JMap : JavaClass<JMap<K,V>> {
|
||||
constexpr static auto kJavaDescriptor = "Ljava/util/Map;";
|
||||
|
||||
struct Iterator;
|
||||
|
||||
/**
|
||||
* Returns the number of pairs in the map.
|
||||
*/
|
||||
size_t size() const;
|
||||
|
||||
/**
|
||||
* To iterate over the Map:
|
||||
*
|
||||
* for (const auto& entry : *jmap) { ... }
|
||||
*
|
||||
* The JMap iterator value_type is std::pair<local_ref<K>, local_ref<V>>
|
||||
* containing references to key and value instances.
|
||||
*
|
||||
* If the Map contains objects whch are not convertible to the given key and
|
||||
* value types, iteration will throw a java ClassCastException.
|
||||
*
|
||||
* The iterator is a InputIterator.
|
||||
*/
|
||||
Iterator begin() const;
|
||||
Iterator end() const;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#include "Iterator-inl.h"
|
||||
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreClasses.h"
|
||||
#include "NativeRunnable.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
class JThread : public JavaClass<JThread> {
|
||||
public:
|
||||
static constexpr const char* kJavaDescriptor = "Ljava/lang/Thread;";
|
||||
|
||||
void start() {
|
||||
static auto method = javaClassStatic()->getMethod<void()>("start");
|
||||
method(self());
|
||||
}
|
||||
|
||||
void join() {
|
||||
static auto method = javaClassStatic()->getMethod<void()>("join");
|
||||
method(self());
|
||||
}
|
||||
|
||||
static local_ref<JThread> create(std::function<void()>&& runnable) {
|
||||
auto jrunnable = JNativeRunnable::newObjectCxxArgs(std::move(runnable));
|
||||
return newInstance(static_ref_cast<JRunnable::javaobject>(jrunnable));
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <fb/visibility.h>
|
||||
|
||||
#include "CoreClasses.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
/**
|
||||
* Wrap Java's WeakReference instead of using JNI WeakGlobalRefs.
|
||||
* A WeakGlobalRef can yield a strong reference even after the object has been
|
||||
* finalized. See comment in the djinni library.
|
||||
* https://github.com/dropbox/djinni/blob/master/support-lib/jni/djinni_support.hpp
|
||||
*/
|
||||
template<typename T = jobject>
|
||||
class JWeakReference : public JavaClass<JWeakReference<T>> {
|
||||
|
||||
typedef JavaClass<JWeakReference<T>> JavaBase_;
|
||||
|
||||
public:
|
||||
static constexpr const char* kJavaDescriptor = "Ljava/lang/ref/WeakReference;";
|
||||
|
||||
static local_ref<JWeakReference<T>> newInstance(alias_ref<T> object) {
|
||||
return JavaBase_::newInstance(static_ref_cast<jobject>(object));
|
||||
}
|
||||
|
||||
local_ref<T> get() const {
|
||||
static auto method = JavaBase_::javaClassStatic()->template getMethod<jobject()>("get");
|
||||
return static_ref_cast<T>(method(JavaBase_::self()));
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
template<typename F>
|
||||
class JMethod;
|
||||
template<typename F>
|
||||
class JStaticMethod;
|
||||
template<typename F>
|
||||
class JNonvirtualMethod;
|
||||
template<typename F>
|
||||
struct JConstructor;
|
||||
template<typename F>
|
||||
class JField;
|
||||
template<typename F>
|
||||
class JStaticField;
|
||||
|
||||
/// Type traits for Java types (currently providing Java type descriptors)
|
||||
template<typename T>
|
||||
struct jtype_traits;
|
||||
|
||||
/// Type traits for Java methods (currently providing Java type descriptors)
|
||||
template<typename F>
|
||||
struct jmethod_traits;
|
||||
|
||||
}}
|
||||
@@ -1,427 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#include "Common.h"
|
||||
#include "Exceptions.h"
|
||||
#include "MetaConvert.h"
|
||||
#include "References.h"
|
||||
#include "Boxed.h"
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
# include <fb/Build.h>
|
||||
#endif
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
// JMethod /////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
inline JMethodBase::JMethodBase(jmethodID method_id) noexcept
|
||||
: method_id_{method_id}
|
||||
{}
|
||||
|
||||
inline JMethodBase::operator bool() const noexcept {
|
||||
return method_id_ != nullptr;
|
||||
}
|
||||
|
||||
inline jmethodID JMethodBase::getId() const noexcept {
|
||||
return method_id_;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
template <int idx, typename... Args>
|
||||
struct ArgsArraySetter;
|
||||
|
||||
template <int idx, typename Arg, typename... Args>
|
||||
struct ArgsArraySetter<idx, Arg, Args...> {
|
||||
static void set(alias_ref<JArrayClass<jobject>::javaobject> array, Arg arg0, Args... args) {
|
||||
// TODO(xxxxxxxx): Use Convert<Args>... to do conversions like the fast path.
|
||||
(*array)[idx] = autobox(arg0);
|
||||
ArgsArraySetter<idx + 1, Args...>::set(array, args...);
|
||||
}
|
||||
};
|
||||
|
||||
template <int idx>
|
||||
struct ArgsArraySetter<idx> {
|
||||
static void set(alias_ref<JArrayClass<jobject>::javaobject> array) {
|
||||
(void)array;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename... Args>
|
||||
local_ref<JArrayClass<jobject>::javaobject> makeArgsArray(Args... args) {
|
||||
auto arr = JArrayClass<jobject>::newArray(sizeof...(args));
|
||||
ArgsArraySetter<0, Args...>::set(arr, args...);
|
||||
return arr;
|
||||
}
|
||||
|
||||
|
||||
inline bool needsSlowPath(alias_ref<jobject> obj) {
|
||||
#if defined(__ANDROID__)
|
||||
// On Android 6.0, art crashes when attempting to call a function on a Proxy.
|
||||
// So, when we detect that case we must use the safe, slow workaround. That is,
|
||||
// we resolve the method id to the corresponding java.lang.reflect.Method object
|
||||
// and make the call via it's invoke() method.
|
||||
static auto is_bad_android = build::Build::getAndroidSdk() == 23;
|
||||
|
||||
if (!is_bad_android) return false;
|
||||
static auto proxy_class = findClassStatic("java/lang/reflect/Proxy");
|
||||
return obj->isInstanceOf(proxy_class);
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
inline void JMethod<void(Args...)>::operator()(alias_ref<jobject> self, Args... args) {
|
||||
const auto env = Environment::current();
|
||||
env->CallVoidMethod(
|
||||
self.get(),
|
||||
getId(),
|
||||
detail::callToJni(detail::Convert<typename std::decay<Args>::type>::toCall(args))...);
|
||||
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
|
||||
}
|
||||
|
||||
#pragma push_macro("DEFINE_PRIMITIVE_CALL")
|
||||
#undef DEFINE_PRIMITIVE_CALL
|
||||
#define DEFINE_PRIMITIVE_CALL(TYPE, METHOD) \
|
||||
template<typename... Args> \
|
||||
inline TYPE JMethod<TYPE(Args...)>::operator()(alias_ref<jobject> self, Args... args) { \
|
||||
const auto env = internal::getEnv(); \
|
||||
auto result = env->Call ## METHOD ## Method( \
|
||||
self.get(), \
|
||||
getId(), \
|
||||
detail::callToJni(detail::Convert<typename std::decay<Args>::type>::toCall(args))...); \
|
||||
FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); \
|
||||
return result; \
|
||||
}
|
||||
|
||||
DEFINE_PRIMITIVE_CALL(jboolean, Boolean)
|
||||
DEFINE_PRIMITIVE_CALL(jbyte, Byte)
|
||||
DEFINE_PRIMITIVE_CALL(jchar, Char)
|
||||
DEFINE_PRIMITIVE_CALL(jshort, Short)
|
||||
DEFINE_PRIMITIVE_CALL(jint, Int)
|
||||
DEFINE_PRIMITIVE_CALL(jlong, Long)
|
||||
DEFINE_PRIMITIVE_CALL(jfloat, Float)
|
||||
DEFINE_PRIMITIVE_CALL(jdouble, Double)
|
||||
#pragma pop_macro("DEFINE_PRIMITIVE_CALL")
|
||||
|
||||
/// JMethod specialization for references that wraps the return value in a @ref local_ref
|
||||
template<typename R, typename... Args>
|
||||
class JMethod<R(Args...)> : public JMethodBase {
|
||||
public:
|
||||
// TODO: static_assert is jobject-derived or local_ref jobject
|
||||
using JniRet = typename detail::Convert<typename std::decay<R>::type>::jniType;
|
||||
static_assert(IsPlainJniReference<JniRet>(), "JniRet must be a JNI reference");
|
||||
using JMethodBase::JMethodBase;
|
||||
JMethod() noexcept {};
|
||||
JMethod(const JMethod& other) noexcept = default;
|
||||
|
||||
/// Invoke a method and return a local reference wrapping the result
|
||||
local_ref<JniRet> operator()(alias_ref<jobject> self, Args... args);
|
||||
|
||||
friend class JClass;
|
||||
};
|
||||
|
||||
template<typename R, typename... Args>
|
||||
inline auto JMethod<R(Args...)>::operator()(alias_ref<jobject> self, Args... args) -> local_ref<JniRet> {
|
||||
const auto env = Environment::current();
|
||||
auto result = env->CallObjectMethod(
|
||||
self.get(),
|
||||
getId(),
|
||||
detail::callToJni(detail::Convert<typename std::decay<Args>::type>::toCall(args))...);
|
||||
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
|
||||
return adopt_local(static_cast<JniRet>(result));
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
inline void JStaticMethod<void(Args...)>::operator()(alias_ref<jclass> cls, Args... args) {
|
||||
const auto env = internal::getEnv();
|
||||
env->CallStaticVoidMethod(
|
||||
cls.get(),
|
||||
getId(),
|
||||
detail::callToJni(detail::Convert<typename std::decay<Args>::type>::toCall(args))...);
|
||||
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
|
||||
}
|
||||
|
||||
#pragma push_macro("DEFINE_PRIMITIVE_STATIC_CALL")
|
||||
#undef DEFINE_PRIMITIVE_STATIC_CALL
|
||||
#define DEFINE_PRIMITIVE_STATIC_CALL(TYPE, METHOD) \
|
||||
template<typename... Args> \
|
||||
inline TYPE JStaticMethod<TYPE(Args...)>::operator()(alias_ref<jclass> cls, Args... args) { \
|
||||
const auto env = internal::getEnv(); \
|
||||
auto result = env->CallStatic ## METHOD ## Method( \
|
||||
cls.get(), \
|
||||
getId(), \
|
||||
detail::callToJni(detail::Convert<typename std::decay<Args>::type>::toCall(args))...); \
|
||||
FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); \
|
||||
return result; \
|
||||
}
|
||||
|
||||
DEFINE_PRIMITIVE_STATIC_CALL(jboolean, Boolean)
|
||||
DEFINE_PRIMITIVE_STATIC_CALL(jbyte, Byte)
|
||||
DEFINE_PRIMITIVE_STATIC_CALL(jchar, Char)
|
||||
DEFINE_PRIMITIVE_STATIC_CALL(jshort, Short)
|
||||
DEFINE_PRIMITIVE_STATIC_CALL(jint, Int)
|
||||
DEFINE_PRIMITIVE_STATIC_CALL(jlong, Long)
|
||||
DEFINE_PRIMITIVE_STATIC_CALL(jfloat, Float)
|
||||
DEFINE_PRIMITIVE_STATIC_CALL(jdouble, Double)
|
||||
#pragma pop_macro("DEFINE_PRIMITIVE_STATIC_CALL")
|
||||
|
||||
/// JStaticMethod specialization for references that wraps the return value in a @ref local_ref
|
||||
template<typename R, typename... Args>
|
||||
class JStaticMethod<R(Args...)> : public JMethodBase {
|
||||
|
||||
public:
|
||||
using JniRet = typename detail::Convert<typename std::decay<R>::type>::jniType;
|
||||
static_assert(IsPlainJniReference<JniRet>(), "T* must be a JNI reference");
|
||||
using JMethodBase::JMethodBase;
|
||||
JStaticMethod() noexcept {};
|
||||
JStaticMethod(const JStaticMethod& other) noexcept = default;
|
||||
|
||||
/// Invoke a method and return a local reference wrapping the result
|
||||
local_ref<JniRet> operator()(alias_ref<jclass> cls, Args... args) {
|
||||
const auto env = internal::getEnv();
|
||||
auto result = env->CallStaticObjectMethod(
|
||||
cls.get(),
|
||||
getId(),
|
||||
detail::callToJni(detail::Convert<typename std::decay<Args>::type>::toCall(args))...);
|
||||
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
|
||||
return adopt_local(static_cast<JniRet>(result));
|
||||
}
|
||||
|
||||
friend class JClass;
|
||||
};
|
||||
|
||||
template<typename... Args>
|
||||
inline void
|
||||
JNonvirtualMethod<void(Args...)>::operator()(alias_ref<jobject> self, alias_ref<jclass> cls, Args... args) {
|
||||
const auto env = internal::getEnv();
|
||||
env->CallNonvirtualVoidMethod(
|
||||
self.get(),
|
||||
cls.get(),
|
||||
getId(),
|
||||
detail::callToJni(detail::Convert<typename std::decay<Args>::type>::toCall(args))...);
|
||||
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
|
||||
}
|
||||
|
||||
#pragma push_macro("DEFINE_PRIMITIVE_NON_VIRTUAL_CALL")
|
||||
#undef DEFINE_PRIMITIVE_NON_VIRTUAL_CALL
|
||||
#define DEFINE_PRIMITIVE_NON_VIRTUAL_CALL(TYPE, METHOD) \
|
||||
template<typename... Args> \
|
||||
inline TYPE \
|
||||
JNonvirtualMethod<TYPE(Args...)>::operator()(alias_ref<jobject> self, alias_ref<jclass> cls, Args... args) { \
|
||||
const auto env = internal::getEnv(); \
|
||||
auto result = env->CallNonvirtual ## METHOD ## Method( \
|
||||
self.get(), \
|
||||
cls.get(), \
|
||||
getId(), \
|
||||
detail::callToJni(detail::Convert<typename std::decay<Args>::type>::toCall(args))...); \
|
||||
FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); \
|
||||
return result; \
|
||||
}
|
||||
|
||||
DEFINE_PRIMITIVE_NON_VIRTUAL_CALL(jboolean, Boolean)
|
||||
DEFINE_PRIMITIVE_NON_VIRTUAL_CALL(jbyte, Byte)
|
||||
DEFINE_PRIMITIVE_NON_VIRTUAL_CALL(jchar, Char)
|
||||
DEFINE_PRIMITIVE_NON_VIRTUAL_CALL(jshort, Short)
|
||||
DEFINE_PRIMITIVE_NON_VIRTUAL_CALL(jint, Int)
|
||||
DEFINE_PRIMITIVE_NON_VIRTUAL_CALL(jlong, Long)
|
||||
DEFINE_PRIMITIVE_NON_VIRTUAL_CALL(jfloat, Float)
|
||||
DEFINE_PRIMITIVE_NON_VIRTUAL_CALL(jdouble, Double)
|
||||
#pragma pop_macro("DEFINE_PRIMITIVE_NON_VIRTUAL_CALL")
|
||||
|
||||
/// JNonvirtualMethod specialization for references that wraps the return value in a @ref local_ref
|
||||
template<typename R, typename... Args>
|
||||
class JNonvirtualMethod<R(Args...)> : public JMethodBase {
|
||||
public:
|
||||
using JniRet = typename detail::Convert<typename std::decay<R>::type>::jniType;
|
||||
static_assert(IsPlainJniReference<JniRet>(), "T* must be a JNI reference");
|
||||
using JMethodBase::JMethodBase;
|
||||
JNonvirtualMethod() noexcept {};
|
||||
JNonvirtualMethod(const JNonvirtualMethod& other) noexcept = default;
|
||||
|
||||
/// Invoke a method and return a local reference wrapping the result
|
||||
local_ref<JniRet> operator()(alias_ref<jobject> self, alias_ref<jclass> cls, Args... args){
|
||||
const auto env = internal::getEnv();
|
||||
auto result = env->CallNonvirtualObjectMethod(
|
||||
self.get(),
|
||||
cls.get(),
|
||||
getId(),
|
||||
detail::callToJni(detail::Convert<typename std::decay<Args>::type>::toCall(args))...);
|
||||
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
|
||||
return adopt_local(static_cast<JniRet>(result));
|
||||
}
|
||||
|
||||
friend class JClass;
|
||||
};
|
||||
|
||||
template <typename... Args>
|
||||
local_ref<jobject> slowCall(jmethodID method_id, alias_ref<jobject> self, Args... args) {
|
||||
static auto invoke = findClassStatic("java/lang/reflect/Method")
|
||||
->getMethod<jobject(jobject, JArrayClass<jobject>::javaobject)>("invoke");
|
||||
// TODO(xxxxxxx): Provide fbjni interface to ToReflectedMethod.
|
||||
auto reflected = adopt_local(Environment::current()->ToReflectedMethod(self->getClass().get(), method_id, JNI_FALSE));
|
||||
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
|
||||
if (!reflected) throw std::runtime_error("Unable to get reflected java.lang.reflect.Method");
|
||||
auto argsArray = makeArgsArray(args...);
|
||||
// No need to check for exceptions since invoke is itself a JMethod that will do that for us.
|
||||
return invoke(reflected, self.get(), argsArray.get());
|
||||
}
|
||||
|
||||
|
||||
// JField<T> ///////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template<typename T>
|
||||
inline JField<T>::JField(jfieldID field) noexcept
|
||||
: field_id_{field}
|
||||
{}
|
||||
|
||||
template<typename T>
|
||||
inline JField<T>::operator bool() const noexcept {
|
||||
return field_id_ != nullptr;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline jfieldID JField<T>::getId() const noexcept {
|
||||
return field_id_;
|
||||
}
|
||||
|
||||
#pragma push_macro("DEFINE_FIELD_PRIMITIVE_GET_SET")
|
||||
#undef DEFINE_FIELD_PRIMITIVE_GET_SET
|
||||
#define DEFINE_FIELD_PRIMITIVE_GET_SET(TYPE, METHOD) \
|
||||
template<> \
|
||||
inline TYPE JField<TYPE>::get(jobject object) const noexcept { \
|
||||
const auto env = internal::getEnv(); \
|
||||
return env->Get ## METHOD ## Field(object, field_id_); \
|
||||
} \
|
||||
\
|
||||
template<> \
|
||||
inline void JField<TYPE>::set(jobject object, TYPE value) noexcept { \
|
||||
const auto env = internal::getEnv(); \
|
||||
env->Set ## METHOD ## Field(object, field_id_, value); \
|
||||
}
|
||||
|
||||
DEFINE_FIELD_PRIMITIVE_GET_SET(jboolean, Boolean)
|
||||
DEFINE_FIELD_PRIMITIVE_GET_SET(jbyte, Byte)
|
||||
DEFINE_FIELD_PRIMITIVE_GET_SET(jchar, Char)
|
||||
DEFINE_FIELD_PRIMITIVE_GET_SET(jshort, Short)
|
||||
DEFINE_FIELD_PRIMITIVE_GET_SET(jint, Int)
|
||||
DEFINE_FIELD_PRIMITIVE_GET_SET(jlong, Long)
|
||||
DEFINE_FIELD_PRIMITIVE_GET_SET(jfloat, Float)
|
||||
DEFINE_FIELD_PRIMITIVE_GET_SET(jdouble, Double)
|
||||
#pragma pop_macro("DEFINE_FIELD_PRIMITIVE_GET_SET")
|
||||
|
||||
template<typename T>
|
||||
inline T JField<T>::get(jobject object) const noexcept {
|
||||
return static_cast<T>(internal::getEnv()->GetObjectField(object, field_id_));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void JField<T>::set(jobject object, T value) noexcept {
|
||||
internal::getEnv()->SetObjectField(object, field_id_, static_cast<jobject>(value));
|
||||
}
|
||||
|
||||
// JStaticField<T> /////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template<typename T>
|
||||
inline JStaticField<T>::JStaticField(jfieldID field) noexcept
|
||||
: field_id_{field}
|
||||
{}
|
||||
|
||||
template<typename T>
|
||||
inline JStaticField<T>::operator bool() const noexcept {
|
||||
return field_id_ != nullptr;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline jfieldID JStaticField<T>::getId() const noexcept {
|
||||
return field_id_;
|
||||
}
|
||||
|
||||
#pragma push_macro("DEFINE_STATIC_FIELD_PRIMITIVE_GET_SET")
|
||||
#undef DEFINE_STATIC_FIELD_PRIMITIVE_GET_SET
|
||||
#define DEFINE_STATIC_FIELD_PRIMITIVE_GET_SET(TYPE, METHOD) \
|
||||
template<> \
|
||||
inline TYPE JStaticField<TYPE>::get(jclass jcls) const noexcept { \
|
||||
const auto env = internal::getEnv(); \
|
||||
return env->GetStatic ## METHOD ## Field(jcls, field_id_); \
|
||||
} \
|
||||
\
|
||||
template<> \
|
||||
inline void JStaticField<TYPE>::set(jclass jcls, TYPE value) noexcept { \
|
||||
const auto env = internal::getEnv(); \
|
||||
env->SetStatic ## METHOD ## Field(jcls, field_id_, value); \
|
||||
}
|
||||
|
||||
DEFINE_STATIC_FIELD_PRIMITIVE_GET_SET(jboolean, Boolean)
|
||||
DEFINE_STATIC_FIELD_PRIMITIVE_GET_SET(jbyte, Byte)
|
||||
DEFINE_STATIC_FIELD_PRIMITIVE_GET_SET(jchar, Char)
|
||||
DEFINE_STATIC_FIELD_PRIMITIVE_GET_SET(jshort, Short)
|
||||
DEFINE_STATIC_FIELD_PRIMITIVE_GET_SET(jint, Int)
|
||||
DEFINE_STATIC_FIELD_PRIMITIVE_GET_SET(jlong, Long)
|
||||
DEFINE_STATIC_FIELD_PRIMITIVE_GET_SET(jfloat, Float)
|
||||
DEFINE_STATIC_FIELD_PRIMITIVE_GET_SET(jdouble, Double)
|
||||
#pragma pop_macro("DEFINE_STATIC_FIELD_PRIMITIVE_GET_SET")
|
||||
|
||||
template<typename T>
|
||||
inline T JStaticField<T>::get(jclass jcls) const noexcept {
|
||||
const auto env = internal::getEnv();
|
||||
return static_cast<T>(env->GetStaticObjectField(jcls, field_id_));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void JStaticField<T>::set(jclass jcls, T value) noexcept {
|
||||
internal::getEnv()->SetStaticObjectField(jcls, field_id_, value);
|
||||
}
|
||||
|
||||
|
||||
// jmethod_traits //////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// TODO(T6608405) Adapt this to implement a register natives method that requires no descriptor
|
||||
namespace internal {
|
||||
|
||||
template<typename Head>
|
||||
inline std::string JavaDescriptor() {
|
||||
return jtype_traits<Head>::descriptor();
|
||||
}
|
||||
|
||||
template<typename Head, typename Elem, typename... Tail>
|
||||
inline std::string JavaDescriptor() {
|
||||
return JavaDescriptor<Head>() + JavaDescriptor<Elem, Tail...>();
|
||||
}
|
||||
|
||||
template<typename R, typename Arg1, typename... Args>
|
||||
inline std::string JMethodDescriptor() {
|
||||
return "(" + JavaDescriptor<Arg1, Args...>() + ")" + JavaDescriptor<R>();
|
||||
}
|
||||
|
||||
template<typename R>
|
||||
inline std::string JMethodDescriptor() {
|
||||
return "()" + JavaDescriptor<R>();
|
||||
}
|
||||
|
||||
} // internal
|
||||
|
||||
template<typename R, typename... Args>
|
||||
inline std::string jmethod_traits<R(Args...)>::descriptor() {
|
||||
return internal::JMethodDescriptor<R, Args...>();
|
||||
}
|
||||
|
||||
template<typename R, typename... Args>
|
||||
inline std::string jmethod_traits<R(Args...)>::constructor_descriptor() {
|
||||
return internal::JMethodDescriptor<void, Args...>();
|
||||
}
|
||||
|
||||
}}
|
||||
@@ -1,338 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/** @file meta.h
|
||||
*
|
||||
* Provides wrappers for meta data such as methods and fields.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
#include <string>
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#include "References-forward.h"
|
||||
|
||||
#ifdef __ANDROID__
|
||||
# include <android/log.h>
|
||||
# define XLOG_TAG "fb-jni"
|
||||
# define XLOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, XLOG_TAG, __VA_ARGS__)
|
||||
# define XLOGD(...) __android_log_print(ANDROID_LOG_DEBUG, XLOG_TAG, __VA_ARGS__)
|
||||
# define XLOGI(...) __android_log_print(ANDROID_LOG_INFO, XLOG_TAG, __VA_ARGS__)
|
||||
# define XLOGW(...) __android_log_print(ANDROID_LOG_WARN, XLOG_TAG, __VA_ARGS__)
|
||||
# define XLOGE(...) __android_log_print(ANDROID_LOG_ERROR, XLOG_TAG, __VA_ARGS__)
|
||||
# define XLOGWTF(...) __android_log_print(ANDROID_LOG_FATAL, XLOG_TAG, __VA_ARGS__)
|
||||
#endif
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
// This will get the reflected Java Method from the method_id, get it's invoke
|
||||
// method, and call the method via that. This shouldn't ever be needed, but
|
||||
// Android 6.0 crashes when calling a method on a java.lang.Proxy via jni.
|
||||
template <typename... Args>
|
||||
local_ref<jobject> slowCall(jmethodID method_id, alias_ref<jobject> self, Args... args);
|
||||
|
||||
class JObject;
|
||||
|
||||
|
||||
/// Wrapper of a jmethodID. Provides a common base for JMethod specializations
|
||||
class JMethodBase {
|
||||
public:
|
||||
/// Verify that the method is valid
|
||||
explicit operator bool() const noexcept;
|
||||
|
||||
/// Access the wrapped id
|
||||
jmethodID getId() const noexcept;
|
||||
|
||||
protected:
|
||||
/// Create a wrapper of a method id
|
||||
explicit JMethodBase(jmethodID method_id = nullptr) noexcept;
|
||||
|
||||
private:
|
||||
jmethodID method_id_;
|
||||
};
|
||||
|
||||
|
||||
/// Representation of a jmethodID
|
||||
template<typename F>
|
||||
class JMethod;
|
||||
|
||||
/// @cond INTERNAL
|
||||
#pragma push_macro("DEFINE_PRIMITIVE_METHOD_CLASS")
|
||||
|
||||
#undef DEFINE_PRIMITIVE_METHOD_CLASS
|
||||
|
||||
// Defining JMethod specializations based on return value
|
||||
#define DEFINE_PRIMITIVE_METHOD_CLASS(TYPE) \
|
||||
template<typename... Args> \
|
||||
class JMethod<TYPE(Args...)> : public JMethodBase { \
|
||||
public: \
|
||||
static_assert(std::is_void<TYPE>::value || IsJniPrimitive<TYPE>(), \
|
||||
"TYPE must be primitive or void"); \
|
||||
\
|
||||
using JMethodBase::JMethodBase; \
|
||||
JMethod() noexcept {}; \
|
||||
JMethod(const JMethod& other) noexcept = default; \
|
||||
\
|
||||
TYPE operator()(alias_ref<jobject> self, Args... args); \
|
||||
\
|
||||
friend class JClass; \
|
||||
}
|
||||
|
||||
DEFINE_PRIMITIVE_METHOD_CLASS(void);
|
||||
DEFINE_PRIMITIVE_METHOD_CLASS(jboolean);
|
||||
DEFINE_PRIMITIVE_METHOD_CLASS(jbyte);
|
||||
DEFINE_PRIMITIVE_METHOD_CLASS(jchar);
|
||||
DEFINE_PRIMITIVE_METHOD_CLASS(jshort);
|
||||
DEFINE_PRIMITIVE_METHOD_CLASS(jint);
|
||||
DEFINE_PRIMITIVE_METHOD_CLASS(jlong);
|
||||
DEFINE_PRIMITIVE_METHOD_CLASS(jfloat);
|
||||
DEFINE_PRIMITIVE_METHOD_CLASS(jdouble);
|
||||
|
||||
#pragma pop_macro("DEFINE_PRIMITIVE_METHOD_CLASS")
|
||||
/// @endcond
|
||||
|
||||
|
||||
/// Convenience type representing constructors
|
||||
/// These should only be used with JClass::getConstructor and JClass::newObject.
|
||||
template<typename F>
|
||||
struct JConstructor : private JMethod<F> {
|
||||
using JMethod<F>::JMethod;
|
||||
private:
|
||||
JConstructor(const JMethod<F>& other) : JMethod<F>(other.getId()) {}
|
||||
friend class JClass;
|
||||
};
|
||||
|
||||
/// Representation of a jStaticMethodID
|
||||
template<typename F>
|
||||
class JStaticMethod;
|
||||
|
||||
/// @cond INTERNAL
|
||||
#pragma push_macro("DEFINE_PRIMITIVE_STATIC_METHOD_CLASS")
|
||||
|
||||
#undef DEFINE_PRIMITIVE_STATIC_METHOD_CLASS
|
||||
|
||||
// Defining JStaticMethod specializations based on return value
|
||||
#define DEFINE_PRIMITIVE_STATIC_METHOD_CLASS(TYPE) \
|
||||
template<typename... Args> \
|
||||
class JStaticMethod<TYPE(Args...)> : public JMethodBase { \
|
||||
static_assert(std::is_void<TYPE>::value || IsJniPrimitive<TYPE>(), \
|
||||
"T must be a JNI primitive or void"); \
|
||||
\
|
||||
public: \
|
||||
using JMethodBase::JMethodBase; \
|
||||
JStaticMethod() noexcept {}; \
|
||||
JStaticMethod(const JStaticMethod& other) noexcept = default; \
|
||||
\
|
||||
TYPE operator()(alias_ref<jclass> cls, Args... args); \
|
||||
\
|
||||
friend class JClass; \
|
||||
}
|
||||
|
||||
DEFINE_PRIMITIVE_STATIC_METHOD_CLASS(void);
|
||||
DEFINE_PRIMITIVE_STATIC_METHOD_CLASS(jboolean);
|
||||
DEFINE_PRIMITIVE_STATIC_METHOD_CLASS(jbyte);
|
||||
DEFINE_PRIMITIVE_STATIC_METHOD_CLASS(jchar);
|
||||
DEFINE_PRIMITIVE_STATIC_METHOD_CLASS(jshort);
|
||||
DEFINE_PRIMITIVE_STATIC_METHOD_CLASS(jint);
|
||||
DEFINE_PRIMITIVE_STATIC_METHOD_CLASS(jlong);
|
||||
DEFINE_PRIMITIVE_STATIC_METHOD_CLASS(jfloat);
|
||||
DEFINE_PRIMITIVE_STATIC_METHOD_CLASS(jdouble);
|
||||
|
||||
#pragma pop_macro("DEFINE_PRIMITIVE_STATIC_METHOD_CLASS")
|
||||
/// @endcond
|
||||
|
||||
|
||||
/// Representation of a jNonvirtualMethodID
|
||||
template<typename F>
|
||||
class JNonvirtualMethod;
|
||||
|
||||
/// @cond INTERNAL
|
||||
#pragma push_macro("DEFINE_PRIMITIVE_NON_VIRTUAL_METHOD_CLASS")
|
||||
|
||||
#undef DEFINE_PRIMITIVE_NON_VIRTUAL_METHOD_CLASS
|
||||
|
||||
// Defining JNonvirtualMethod specializations based on return value
|
||||
#define DEFINE_PRIMITIVE_NON_VIRTUAL_METHOD_CLASS(TYPE) \
|
||||
template<typename... Args> \
|
||||
class JNonvirtualMethod<TYPE(Args...)> : public JMethodBase { \
|
||||
static_assert(std::is_void<TYPE>::value || IsJniPrimitive<TYPE>(), \
|
||||
"T must be a JNI primitive or void"); \
|
||||
\
|
||||
public: \
|
||||
using JMethodBase::JMethodBase; \
|
||||
JNonvirtualMethod() noexcept {}; \
|
||||
JNonvirtualMethod(const JNonvirtualMethod& other) noexcept = default; \
|
||||
\
|
||||
TYPE operator()(alias_ref<jobject> self, alias_ref<jclass> cls, Args... args); \
|
||||
\
|
||||
friend class JClass; \
|
||||
}
|
||||
|
||||
DEFINE_PRIMITIVE_NON_VIRTUAL_METHOD_CLASS(void);
|
||||
DEFINE_PRIMITIVE_NON_VIRTUAL_METHOD_CLASS(jboolean);
|
||||
DEFINE_PRIMITIVE_NON_VIRTUAL_METHOD_CLASS(jbyte);
|
||||
DEFINE_PRIMITIVE_NON_VIRTUAL_METHOD_CLASS(jchar);
|
||||
DEFINE_PRIMITIVE_NON_VIRTUAL_METHOD_CLASS(jshort);
|
||||
DEFINE_PRIMITIVE_NON_VIRTUAL_METHOD_CLASS(jint);
|
||||
DEFINE_PRIMITIVE_NON_VIRTUAL_METHOD_CLASS(jlong);
|
||||
DEFINE_PRIMITIVE_NON_VIRTUAL_METHOD_CLASS(jfloat);
|
||||
DEFINE_PRIMITIVE_NON_VIRTUAL_METHOD_CLASS(jdouble);
|
||||
|
||||
#pragma pop_macro("DEFINE_PRIMITIVE_NON_VIRTUAL_METHOD_CLASS")
|
||||
/// @endcond
|
||||
|
||||
|
||||
/**
|
||||
* JField represents typed fields and simplifies their access. Note that object types return
|
||||
* raw pointers which generally should promptly get a wrap_local treatment.
|
||||
*/
|
||||
template<typename T>
|
||||
class JField {
|
||||
static_assert(IsJniScalar<T>(), "T must be a JNI scalar");
|
||||
|
||||
public:
|
||||
/// Wraps an existing field id
|
||||
explicit JField(jfieldID field = nullptr) noexcept;
|
||||
|
||||
/// Verify that the id is valid
|
||||
explicit operator bool() const noexcept;
|
||||
|
||||
/// Access the wrapped id
|
||||
jfieldID getId() const noexcept;
|
||||
|
||||
private:
|
||||
jfieldID field_id_;
|
||||
|
||||
/// Get field value
|
||||
/// @pre object != nullptr
|
||||
T get(jobject object) const noexcept;
|
||||
|
||||
/// Set field value
|
||||
/// @pre object != nullptr
|
||||
void set(jobject object, T value) noexcept;
|
||||
|
||||
friend class JObject;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* JStaticField represents typed fields and simplifies their access. Note that object types
|
||||
* return raw pointers which generally should promptly get a wrap_local treatment.
|
||||
*/
|
||||
template<typename T>
|
||||
class JStaticField {
|
||||
static_assert(IsJniScalar<T>(), "T must be a JNI scalar");
|
||||
|
||||
public:
|
||||
/// Wraps an existing field id
|
||||
explicit JStaticField(jfieldID field = nullptr) noexcept;
|
||||
|
||||
/// Verify that the id is valid
|
||||
explicit operator bool() const noexcept;
|
||||
|
||||
/// Access the wrapped id
|
||||
jfieldID getId() const noexcept;
|
||||
|
||||
private:
|
||||
jfieldID field_id_;
|
||||
|
||||
/// Get field value
|
||||
/// @pre object != nullptr
|
||||
T get(jclass jcls) const noexcept;
|
||||
|
||||
/// Set field value
|
||||
/// @pre object != nullptr
|
||||
void set(jclass jcls, T value) noexcept;
|
||||
|
||||
friend class JClass;
|
||||
friend class JObject;
|
||||
};
|
||||
|
||||
|
||||
/// Template magic to provide @ref jmethod_traits
|
||||
template<typename R, typename... Args>
|
||||
struct jmethod_traits<R(Args...)> {
|
||||
static std::string descriptor();
|
||||
static std::string constructor_descriptor();
|
||||
};
|
||||
|
||||
|
||||
// jtype_traits ////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template<typename T>
|
||||
struct jtype_traits {
|
||||
private:
|
||||
using Repr = ReprType<T>;
|
||||
public:
|
||||
// The jni type signature (described at
|
||||
// http://docs.oracle.com/javase/1.5.0/docs/guide/jni/spec/types.html).
|
||||
static std::string descriptor() {
|
||||
std::string descriptor;
|
||||
if (Repr::kJavaDescriptor == nullptr) {
|
||||
descriptor = Repr::get_instantiated_java_descriptor();
|
||||
} else {
|
||||
descriptor = Repr::kJavaDescriptor;
|
||||
}
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
// The signature used for class lookups. See
|
||||
// http://docs.oracle.com/javase/6/docs/api/java/lang/Class.html#getName().
|
||||
static std::string base_name() {
|
||||
if (Repr::kJavaDescriptor != nullptr) {
|
||||
std::string base_name = Repr::kJavaDescriptor;
|
||||
return base_name.substr(1, base_name.size() - 2);
|
||||
}
|
||||
return Repr::get_instantiated_base_name();
|
||||
}
|
||||
};
|
||||
|
||||
#pragma push_macro("DEFINE_FIELD_AND_ARRAY_TRAIT")
|
||||
#undef DEFINE_FIELD_AND_ARRAY_TRAIT
|
||||
|
||||
#define DEFINE_FIELD_AND_ARRAY_TRAIT(TYPE, DSC) \
|
||||
template<> \
|
||||
struct jtype_traits<TYPE> { \
|
||||
static std::string descriptor() { return std::string{#DSC}; } \
|
||||
static std::string base_name() { return descriptor(); } \
|
||||
using array_type = TYPE ## Array; \
|
||||
}; \
|
||||
template<> \
|
||||
struct jtype_traits<TYPE ## Array> { \
|
||||
static std::string descriptor() { return std::string{"[" #DSC}; } \
|
||||
static std::string base_name() { return descriptor(); } \
|
||||
using entry_type = TYPE; \
|
||||
};
|
||||
|
||||
// There is no voidArray, handle that without the macro.
|
||||
template<>
|
||||
struct jtype_traits<void> {
|
||||
static std::string descriptor() { return std::string{"V"}; };
|
||||
};
|
||||
|
||||
DEFINE_FIELD_AND_ARRAY_TRAIT(jboolean, Z)
|
||||
DEFINE_FIELD_AND_ARRAY_TRAIT(jbyte, B)
|
||||
DEFINE_FIELD_AND_ARRAY_TRAIT(jchar, C)
|
||||
DEFINE_FIELD_AND_ARRAY_TRAIT(jshort, S)
|
||||
DEFINE_FIELD_AND_ARRAY_TRAIT(jint, I)
|
||||
DEFINE_FIELD_AND_ARRAY_TRAIT(jlong, J)
|
||||
DEFINE_FIELD_AND_ARRAY_TRAIT(jfloat, F)
|
||||
DEFINE_FIELD_AND_ARRAY_TRAIT(jdouble, D)
|
||||
|
||||
#pragma pop_macro("DEFINE_FIELD_AND_ARRAY_TRAIT")
|
||||
|
||||
|
||||
template <typename T>
|
||||
struct jmethod_traits_from_cxx;
|
||||
|
||||
}}
|
||||
|
||||
#include "Meta-inl.h"
|
||||
@@ -1,141 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#include "Common.h"
|
||||
#include "References.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
namespace detail {
|
||||
|
||||
// In order to avoid potentially filling the jni locals table,
|
||||
// temporary objects (right now, this is just jstrings) need to be
|
||||
// released. This is done by returning a holder which autoconverts to
|
||||
// jstring.
|
||||
template <typename T>
|
||||
inline T callToJni(T&& t) {
|
||||
return t;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline JniType<T> callToJni(local_ref<T>&& sref) {
|
||||
return sref.get();
|
||||
}
|
||||
|
||||
// Normally, pass through types unmolested.
|
||||
template <typename T, typename Enabled = void>
|
||||
struct Convert {
|
||||
typedef T jniType;
|
||||
static jniType fromJni(jniType t) {
|
||||
return t;
|
||||
}
|
||||
static jniType toJniRet(jniType t) {
|
||||
return t;
|
||||
}
|
||||
static jniType toCall(jniType t) {
|
||||
return t;
|
||||
}
|
||||
};
|
||||
|
||||
// This is needed for return conversion
|
||||
template <>
|
||||
struct Convert<void> {
|
||||
typedef void jniType;
|
||||
};
|
||||
|
||||
// jboolean is an unsigned char, not a bool. Allow it to work either way.
|
||||
template<>
|
||||
struct Convert<bool> {
|
||||
typedef jboolean jniType;
|
||||
static bool fromJni(jniType t) {
|
||||
return t;
|
||||
}
|
||||
static jniType toJniRet(bool t) {
|
||||
return t;
|
||||
}
|
||||
static jniType toCall(bool t) {
|
||||
return t;
|
||||
}
|
||||
};
|
||||
|
||||
// convert to alias_ref<T> from T
|
||||
template <typename T>
|
||||
struct Convert<alias_ref<T>> {
|
||||
typedef JniType<T> jniType;
|
||||
static alias_ref<jniType> fromJni(jniType t) {
|
||||
return wrap_alias(t);
|
||||
}
|
||||
static jniType toJniRet(alias_ref<jniType> t) {
|
||||
return t.get();
|
||||
}
|
||||
static jniType toCall(alias_ref<jniType> t) {
|
||||
return t.get();
|
||||
}
|
||||
};
|
||||
|
||||
// convert return from local_ref<T>
|
||||
template <typename T>
|
||||
struct Convert<local_ref<T>> {
|
||||
typedef JniType<T> jniType;
|
||||
// No automatic synthesis of local_ref
|
||||
static jniType toJniRet(local_ref<jniType> t) {
|
||||
return t.release();
|
||||
}
|
||||
static jniType toCall(local_ref<jniType> t) {
|
||||
return t.get();
|
||||
}
|
||||
};
|
||||
|
||||
// convert return from global_ref<T>
|
||||
template <typename T>
|
||||
struct Convert<global_ref<T>> {
|
||||
typedef JniType<T> jniType;
|
||||
// No automatic synthesis of global_ref
|
||||
static jniType toJniRet(global_ref<jniType>&& t) {
|
||||
// If this gets called, ownership the global_ref was passed in here. (It's
|
||||
// probably a copy of a persistent global_ref made when a function was
|
||||
// declared to return a global_ref, but it could moved out or otherwise not
|
||||
// referenced elsewhere. Doesn't matter.) Either way, the only safe way
|
||||
// to return it is to make a local_ref, release it, and return the
|
||||
// underlying local jobject.
|
||||
auto ret = make_local(t);
|
||||
return ret.release();
|
||||
}
|
||||
static jniType toJniRet(const global_ref<jniType>& t) {
|
||||
// If this gets called, the function was declared to return const&. We
|
||||
// have a ref to a global_ref whose lifetime will exceed this call, so we
|
||||
// can just get the underlying jobject and return it to java without
|
||||
// needing to make a local_ref.
|
||||
return t.get();
|
||||
}
|
||||
static jniType toCall(global_ref<jniType> t) {
|
||||
return t.get();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T> struct jni_sig_from_cxx_t;
|
||||
template <typename R, typename... Args>
|
||||
struct jni_sig_from_cxx_t<R(Args...)> {
|
||||
using JniRet = typename Convert<typename std::decay<R>::type>::jniType;
|
||||
using JniSig = JniRet(typename Convert<typename std::decay<Args>::type>::jniType...);
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
using jni_sig_from_cxx = typename jni_sig_from_cxx_t<T>::JniSig;
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <typename R, typename... Args>
|
||||
struct jmethod_traits_from_cxx<R(Args...)> : jmethod_traits<detail::jni_sig_from_cxx<R(Args...)>> {
|
||||
};
|
||||
|
||||
}}
|
||||
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreClasses.h"
|
||||
#include "Hybrid.h"
|
||||
#include "Registration.h"
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
struct JRunnable : public JavaClass<JRunnable> {
|
||||
static auto constexpr kJavaDescriptor = "Ljava/lang/Runnable;";
|
||||
};
|
||||
|
||||
struct JNativeRunnable : public HybridClass<JNativeRunnable, JRunnable> {
|
||||
public:
|
||||
static auto constexpr kJavaDescriptor = "Lcom/facebook/jni/NativeRunnable;";
|
||||
|
||||
JNativeRunnable(std::function<void()>&& runnable) : runnable_(std::move(runnable)) {}
|
||||
|
||||
static void OnLoad() {
|
||||
registerHybrid({
|
||||
makeNativeMethod("run", JNativeRunnable::run),
|
||||
});
|
||||
}
|
||||
|
||||
void run() {
|
||||
runnable_();
|
||||
}
|
||||
|
||||
private:
|
||||
std::function<void()> runnable_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace jni
|
||||
} // namespace facebook
|
||||
@@ -1,23 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <fb/fbjni.h>
|
||||
#include <fb/fbjni/ByteBuffer.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
class JReadableByteChannel : public JavaClass<JReadableByteChannel> {
|
||||
public:
|
||||
static constexpr const char* kJavaDescriptor = "Ljava/nio/channels/ReadableByteChannel;";
|
||||
|
||||
int read(alias_ref<JByteBuffer> dest) const;
|
||||
};
|
||||
|
||||
}}
|
||||
@@ -1,128 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cassert>
|
||||
#include <new>
|
||||
#include <atomic>
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
/// @cond INTERNAL
|
||||
namespace internal {
|
||||
|
||||
// Statistics mostly provided for test (only updated if FBJNI_DEBUG_REFS is defined)
|
||||
struct ReferenceStats {
|
||||
std::atomic_uint locals_created, globals_created, weaks_created,
|
||||
locals_deleted, globals_deleted, weaks_deleted;
|
||||
|
||||
void reset() noexcept;
|
||||
};
|
||||
|
||||
extern ReferenceStats g_reference_stats;
|
||||
}
|
||||
/// @endcond
|
||||
|
||||
|
||||
// LocalReferenceAllocator /////////////////////////////////////////////////////////////////////////
|
||||
|
||||
inline jobject LocalReferenceAllocator::newReference(jobject original) const {
|
||||
internal::dbglog("Local new: %p", original);
|
||||
#ifdef FBJNI_DEBUG_REFS
|
||||
++internal::g_reference_stats.locals_created;
|
||||
#endif
|
||||
auto ref = internal::getEnv()->NewLocalRef(original);
|
||||
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
|
||||
return ref;
|
||||
}
|
||||
|
||||
inline void LocalReferenceAllocator::deleteReference(jobject reference) const noexcept {
|
||||
internal::dbglog("Local release: %p", reference);
|
||||
|
||||
if (reference) {
|
||||
#ifdef FBJNI_DEBUG_REFS
|
||||
++internal::g_reference_stats.locals_deleted;
|
||||
#endif
|
||||
assert(verifyReference(reference));
|
||||
internal::getEnv()->DeleteLocalRef(reference);
|
||||
}
|
||||
}
|
||||
|
||||
inline bool LocalReferenceAllocator::verifyReference(jobject reference) const noexcept {
|
||||
if (!reference || !internal::doesGetObjectRefTypeWork()) {
|
||||
return true;
|
||||
}
|
||||
return internal::getEnv()->GetObjectRefType(reference) == JNILocalRefType;
|
||||
}
|
||||
|
||||
|
||||
// GlobalReferenceAllocator ////////////////////////////////////////////////////////////////////////
|
||||
|
||||
inline jobject GlobalReferenceAllocator::newReference(jobject original) const {
|
||||
internal::dbglog("Global new: %p", original);
|
||||
#ifdef FBJNI_DEBUG_REFS
|
||||
++internal::g_reference_stats.globals_created;
|
||||
#endif
|
||||
auto ref = internal::getEnv()->NewGlobalRef(original);
|
||||
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
|
||||
return ref;
|
||||
}
|
||||
|
||||
inline void GlobalReferenceAllocator::deleteReference(jobject reference) const noexcept {
|
||||
internal::dbglog("Global release: %p", reference);
|
||||
|
||||
if (reference) {
|
||||
#ifdef FBJNI_DEBUG_REFS
|
||||
++internal::g_reference_stats.globals_deleted;
|
||||
#endif
|
||||
assert(verifyReference(reference));
|
||||
internal::getEnv()->DeleteGlobalRef(reference);
|
||||
}
|
||||
}
|
||||
|
||||
inline bool GlobalReferenceAllocator::verifyReference(jobject reference) const noexcept {
|
||||
if (!reference || !internal::doesGetObjectRefTypeWork()) {
|
||||
return true;
|
||||
}
|
||||
return internal::getEnv()->GetObjectRefType(reference) == JNIGlobalRefType;
|
||||
}
|
||||
|
||||
|
||||
// WeakGlobalReferenceAllocator ////////////////////////////////////////////////////////////////////
|
||||
|
||||
inline jobject WeakGlobalReferenceAllocator::newReference(jobject original) const {
|
||||
internal::dbglog("Weak global new: %p", original);
|
||||
#ifdef FBJNI_DEBUG_REFS
|
||||
++internal::g_reference_stats.weaks_created;
|
||||
#endif
|
||||
auto ref = internal::getEnv()->NewWeakGlobalRef(original);
|
||||
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
|
||||
return ref;
|
||||
}
|
||||
|
||||
inline void WeakGlobalReferenceAllocator::deleteReference(jobject reference) const noexcept {
|
||||
internal::dbglog("Weak Global release: %p", reference);
|
||||
|
||||
if (reference) {
|
||||
#ifdef FBJNI_DEBUG_REFS
|
||||
++internal::g_reference_stats.weaks_deleted;
|
||||
#endif
|
||||
assert(verifyReference(reference));
|
||||
internal::getEnv()->DeleteWeakGlobalRef(reference);
|
||||
}
|
||||
}
|
||||
|
||||
inline bool WeakGlobalReferenceAllocator::verifyReference(jobject reference) const noexcept {
|
||||
if (!reference || !internal::doesGetObjectRefTypeWork()) {
|
||||
return true;
|
||||
}
|
||||
return internal::getEnv()->GetObjectRefType(reference) == JNIWeakGlobalRefType;
|
||||
}
|
||||
|
||||
}}
|
||||
@@ -1,60 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file ReferenceAllocators.h
|
||||
*
|
||||
* Reference allocators are used to create and delete various classes of JNI references (local,
|
||||
* global, and weak global).
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <fb/visibility.h>
|
||||
|
||||
#include "Common.h"
|
||||
|
||||
namespace facebook { namespace jni {
|
||||
|
||||
/// Allocator that handles local references
|
||||
class FBEXPORT LocalReferenceAllocator {
|
||||
public:
|
||||
jobject newReference(jobject original) const;
|
||||
void deleteReference(jobject reference) const noexcept;
|
||||
bool verifyReference(jobject reference) const noexcept;
|
||||
};
|
||||
|
||||
/// Allocator that handles global references
|
||||
class FBEXPORT GlobalReferenceAllocator {
|
||||
public:
|
||||
jobject newReference(jobject original) const;
|
||||
void deleteReference(jobject reference) const noexcept;
|
||||
bool verifyReference(jobject reference) const noexcept;
|
||||
};
|
||||
|
||||
/// Allocator that handles weak global references
|
||||
class FBEXPORT WeakGlobalReferenceAllocator {
|
||||
public:
|
||||
jobject newReference(jobject original) const;
|
||||
void deleteReference(jobject reference) const noexcept;
|
||||
bool verifyReference(jobject reference) const noexcept;
|
||||
};
|
||||
|
||||
/// @cond INTERNAL
|
||||
namespace internal {
|
||||
|
||||
/**
|
||||
* @return true iff env->GetObjectRefType is expected to work properly.
|
||||
*/
|
||||
FBEXPORT bool doesGetObjectRefTypeWork();
|
||||
|
||||
}
|
||||
/// @endcond
|
||||
|
||||
}}
|
||||
|
||||
#include "ReferenceAllocators-inl.h"
|
||||
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ReferenceAllocators.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
template<typename T, typename Enable = void>
|
||||
class JObjectWrapper;
|
||||
|
||||
namespace detail {
|
||||
struct JObjectBase {
|
||||
jobject get() const noexcept;
|
||||
void set(jobject reference) noexcept;
|
||||
jobject this_;
|
||||
};
|
||||
|
||||
// RefReprType maps a type to the representation used by fbjni smart references.
|
||||
template <typename T, typename Enable = void>
|
||||
struct RefReprType;
|
||||
|
||||
template <typename T>
|
||||
struct JavaObjectType;
|
||||
|
||||
template <typename T>
|
||||
struct ReprAccess;
|
||||
}
|
||||
|
||||
// Given T, either a jobject-like type or a JavaClass-derived type, ReprType<T>
|
||||
// is the corresponding JavaClass-derived type and JniType<T> is the
|
||||
// jobject-like type.
|
||||
template <typename T>
|
||||
using ReprType = typename detail::RefReprType<T>::type;
|
||||
|
||||
template <typename T>
|
||||
using JniType = typename detail::JavaObjectType<T>::type;
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
class base_owned_ref;
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
class basic_strong_ref;
|
||||
|
||||
template<typename T>
|
||||
class weak_ref;
|
||||
|
||||
template<typename T>
|
||||
class alias_ref;
|
||||
|
||||
/// A smart unique reference owning a local JNI reference
|
||||
template<typename T>
|
||||
using local_ref = basic_strong_ref<T, LocalReferenceAllocator>;
|
||||
|
||||
/// A smart unique reference owning a global JNI reference
|
||||
template<typename T>
|
||||
using global_ref = basic_strong_ref<T, GlobalReferenceAllocator>;
|
||||
|
||||
}} // namespace facebook::jni
|
||||
@@ -1,511 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <new>
|
||||
#include "CoreClasses.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
template<typename T>
|
||||
inline enable_if_t<IsPlainJniReference<T>(), T> getPlainJniReference(T ref) {
|
||||
return ref;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline JniType<T> getPlainJniReference(alias_ref<T> ref) {
|
||||
return ref.get();
|
||||
}
|
||||
|
||||
template<typename T, typename A>
|
||||
inline JniType<T> getPlainJniReference(const base_owned_ref<T, A>& ref) {
|
||||
return ref.get();
|
||||
}
|
||||
|
||||
|
||||
namespace detail {
|
||||
template <typename Repr>
|
||||
struct ReprAccess {
|
||||
using javaobject = JniType<Repr>;
|
||||
static void set(Repr& repr, javaobject obj) noexcept {
|
||||
repr.JObjectBase::set(obj);
|
||||
}
|
||||
static javaobject get(const Repr& repr) {
|
||||
return static_cast<javaobject>(repr.JObject::get());
|
||||
}
|
||||
};
|
||||
|
||||
namespace {
|
||||
template <typename Repr>
|
||||
void StaticAssertValidRepr() noexcept {
|
||||
static_assert(std::is_base_of<JObject, Repr>::value,
|
||||
"A smart ref representation must be derived from JObject.");
|
||||
static_assert(IsPlainJniReference<JniType<Repr>>(), "T must be a JNI reference");
|
||||
static_assert(sizeof(Repr) == sizeof(JObjectBase), "");
|
||||
static_assert(alignof(Repr) == alignof(JObjectBase), "");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Repr>
|
||||
ReprStorage<Repr>::ReprStorage(JniType<Repr> obj) noexcept {
|
||||
StaticAssertValidRepr<Repr>();
|
||||
set(obj);
|
||||
}
|
||||
|
||||
template <typename Repr>
|
||||
void ReprStorage<Repr>::set(JniType<Repr> obj) noexcept {
|
||||
new (&storage_) Repr;
|
||||
ReprAccess<Repr>::set(get(), obj);
|
||||
}
|
||||
|
||||
template <typename Repr>
|
||||
Repr& ReprStorage<Repr>::get() noexcept {
|
||||
return *reinterpret_cast<Repr*>(&storage_);
|
||||
}
|
||||
|
||||
template <typename Repr>
|
||||
const Repr& ReprStorage<Repr>::get() const noexcept {
|
||||
return *reinterpret_cast<const Repr*>(&storage_);
|
||||
}
|
||||
|
||||
template <typename Repr>
|
||||
JniType<Repr> ReprStorage<Repr>::jobj() const noexcept {
|
||||
ReprAccess<Repr>::get(get());
|
||||
return ReprAccess<Repr>::get(get());
|
||||
}
|
||||
|
||||
template <typename Repr>
|
||||
void ReprStorage<Repr>::swap(ReprStorage& other) noexcept {
|
||||
StaticAssertValidRepr<Repr>();
|
||||
using std::swap;
|
||||
swap(get(), other.get());
|
||||
}
|
||||
|
||||
inline void JObjectBase::set(jobject reference) noexcept {
|
||||
this_ = reference;
|
||||
}
|
||||
|
||||
inline jobject JObjectBase::get() const noexcept {
|
||||
return this_;
|
||||
}
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
enable_if_t<IsNonWeakReference<T>(), plain_jni_reference_t<T>> make_ref(const T& reference) {
|
||||
auto old_reference = getPlainJniReference(reference);
|
||||
if (!old_reference) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto ref = Alloc{}.newReference(old_reference);
|
||||
if (!ref) {
|
||||
// Note that we end up here if we pass a weak ref that refers to a collected object.
|
||||
// Thus, it's hard to come up with a reason why this function should be used with
|
||||
// weak references.
|
||||
throw std::bad_alloc{};
|
||||
}
|
||||
|
||||
return static_cast<plain_jni_reference_t<T>>(ref);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template<typename T>
|
||||
inline local_ref<T> adopt_local(T ref) noexcept {
|
||||
static_assert(IsPlainJniReference<T>(), "T must be a plain jni reference");
|
||||
return local_ref<T>{ref};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline global_ref<T> adopt_global(T ref) noexcept {
|
||||
static_assert(IsPlainJniReference<T>(), "T must be a plain jni reference");
|
||||
return global_ref<T>{ref};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline weak_ref<T> adopt_weak_global(T ref) noexcept {
|
||||
static_assert(IsPlainJniReference<T>(), "T must be a plain jni reference");
|
||||
return weak_ref<T>{ref};
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
inline enable_if_t<IsPlainJniReference<T>(), alias_ref<T>> wrap_alias(T ref) noexcept {
|
||||
return alias_ref<T>(ref);
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
enable_if_t<IsPlainJniReference<T>(), alias_ref<T>> wrap_alias(T ref) noexcept;
|
||||
|
||||
|
||||
template<typename T>
|
||||
enable_if_t<IsNonWeakReference<T>(), local_ref<plain_jni_reference_t<T>>>
|
||||
make_local(const T& ref) {
|
||||
return adopt_local(detail::make_ref<T, LocalReferenceAllocator>(ref));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
enable_if_t<IsNonWeakReference<T>(), global_ref<plain_jni_reference_t<T>>>
|
||||
make_global(const T& ref) {
|
||||
return adopt_global(detail::make_ref<T, GlobalReferenceAllocator>(ref));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
enable_if_t<IsNonWeakReference<T>(), weak_ref<plain_jni_reference_t<T>>>
|
||||
make_weak(const T& ref) {
|
||||
return adopt_weak_global(detail::make_ref<T, WeakGlobalReferenceAllocator>(ref));
|
||||
}
|
||||
|
||||
template<typename T1, typename T2>
|
||||
inline enable_if_t<IsNonWeakReference<T1>() && IsNonWeakReference<T2>(), bool>
|
||||
operator==(const T1& a, const T2& b) {
|
||||
return isSameObject(getPlainJniReference(a), getPlainJniReference(b));
|
||||
}
|
||||
|
||||
template<typename T1, typename T2>
|
||||
inline enable_if_t<IsNonWeakReference<T1>() && IsNonWeakReference<T2>(), bool>
|
||||
operator!=(const T1& a, const T2& b) {
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
|
||||
// base_owned_ref ///////////////////////////////////////////////////////////////////////
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
inline base_owned_ref<T, Alloc>::base_owned_ref() noexcept
|
||||
: base_owned_ref(nullptr)
|
||||
{}
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
inline base_owned_ref<T, Alloc>::base_owned_ref(std::nullptr_t t) noexcept
|
||||
: base_owned_ref(static_cast<javaobject>(nullptr))
|
||||
{}
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
inline base_owned_ref<T, Alloc>::base_owned_ref(const base_owned_ref& other)
|
||||
: storage_{static_cast<javaobject>(Alloc{}.newReference(other.get()))}
|
||||
{}
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
template<typename U>
|
||||
inline base_owned_ref<T, Alloc>::base_owned_ref(const base_owned_ref<U, Alloc>& other)
|
||||
: storage_{static_cast<javaobject>(Alloc{}.newReference(other.get()))}
|
||||
{
|
||||
static_assert(std::is_convertible<JniType<U>, javaobject>::value, "");
|
||||
}
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
inline facebook::jni::base_owned_ref<T, Alloc>::base_owned_ref(
|
||||
javaobject reference) noexcept
|
||||
: storage_(reference) {
|
||||
assert(Alloc{}.verifyReference(reference));
|
||||
internal::dbglog("New wrapped ref=%p this=%p", get(), this);
|
||||
}
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
inline base_owned_ref<T, Alloc>::base_owned_ref(
|
||||
base_owned_ref<T, Alloc>&& other) noexcept
|
||||
: storage_(other.get()) {
|
||||
internal::dbglog("New move from ref=%p other=%p", other.get(), &other);
|
||||
internal::dbglog("New move to ref=%p this=%p", get(), this);
|
||||
// JObject is a simple type and does not support move semantics so we explicitly
|
||||
// clear other
|
||||
other.set(nullptr);
|
||||
}
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
template<typename U>
|
||||
base_owned_ref<T, Alloc>::base_owned_ref(base_owned_ref<U, Alloc>&& other) noexcept
|
||||
: storage_(other.get()) {
|
||||
internal::dbglog("New move from ref=%p other=%p", other.get(), &other);
|
||||
internal::dbglog("New move to ref=%p this=%p", get(), this);
|
||||
// JObject is a simple type and does not support move semantics so we explicitly
|
||||
// clear other
|
||||
other.set(nullptr);
|
||||
}
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
inline base_owned_ref<T, Alloc>::~base_owned_ref() noexcept {
|
||||
reset();
|
||||
internal::dbglog("Ref destruct ref=%p this=%p", get(), this);
|
||||
}
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
inline auto base_owned_ref<T, Alloc>::release() noexcept -> javaobject {
|
||||
auto value = get();
|
||||
internal::dbglog("Ref release ref=%p this=%p", value, this);
|
||||
set(nullptr);
|
||||
return value;
|
||||
}
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
inline void base_owned_ref<T,Alloc>::reset() noexcept {
|
||||
reset(nullptr);
|
||||
}
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
inline void base_owned_ref<T,Alloc>::reset(javaobject reference) noexcept {
|
||||
if (get()) {
|
||||
assert(Alloc{}.verifyReference(reference));
|
||||
Alloc{}.deleteReference(get());
|
||||
}
|
||||
set(reference);
|
||||
}
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
inline auto base_owned_ref<T, Alloc>::get() const noexcept -> javaobject {
|
||||
return storage_.jobj();
|
||||
}
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
inline void base_owned_ref<T, Alloc>::set(javaobject ref) noexcept {
|
||||
storage_.set(ref);
|
||||
}
|
||||
|
||||
|
||||
// weak_ref ///////////////////////////////////////////////////////////////////////
|
||||
|
||||
template<typename T>
|
||||
inline weak_ref<T>& weak_ref<T>::operator=(
|
||||
const weak_ref& other) {
|
||||
auto otherCopy = other;
|
||||
swap(*this, otherCopy);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline weak_ref<T>& weak_ref<T>::operator=(
|
||||
weak_ref<T>&& other) noexcept {
|
||||
internal::dbglog("Op= move ref=%p this=%p oref=%p other=%p",
|
||||
get(), this, other.get(), &other);
|
||||
reset(other.release());
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
local_ref<T> weak_ref<T>::lockLocal() const {
|
||||
return adopt_local(
|
||||
static_cast<javaobject>(LocalReferenceAllocator{}.newReference(get())));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
global_ref<T> weak_ref<T>::lockGlobal() const {
|
||||
return adopt_global(
|
||||
static_cast<javaobject>(GlobalReferenceAllocator{}.newReference(get())));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void swap(
|
||||
weak_ref<T>& a,
|
||||
weak_ref<T>& b) noexcept {
|
||||
internal::dbglog("Ref swap a.ref=%p a=%p b.ref=%p b=%p",
|
||||
a.get(), &a, b.get(), &b);
|
||||
a.storage_.swap(b.storage_);
|
||||
}
|
||||
|
||||
|
||||
// basic_strong_ref ////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
inline basic_strong_ref<T, Alloc>& basic_strong_ref<T, Alloc>::operator=(
|
||||
const basic_strong_ref& other) {
|
||||
auto otherCopy = other;
|
||||
swap(*this, otherCopy);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
inline basic_strong_ref<T, Alloc>& basic_strong_ref<T, Alloc>::operator=(
|
||||
basic_strong_ref<T, Alloc>&& other) noexcept {
|
||||
internal::dbglog("Op= move ref=%p this=%p oref=%p other=%p",
|
||||
get(), this, other.get(), &other);
|
||||
reset(other.release());
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
inline alias_ref<T> basic_strong_ref<T, Alloc>::releaseAlias() noexcept {
|
||||
return wrap_alias(release());
|
||||
}
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
inline basic_strong_ref<T, Alloc>::operator bool() const noexcept {
|
||||
return get() != nullptr;
|
||||
}
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
inline auto basic_strong_ref<T, Alloc>::operator->() noexcept -> Repr* {
|
||||
return &storage_.get();
|
||||
}
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
inline auto basic_strong_ref<T, Alloc>::operator->() const noexcept -> const Repr* {
|
||||
return &storage_.get();
|
||||
}
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
inline auto basic_strong_ref<T, Alloc>::operator*() noexcept -> Repr& {
|
||||
return storage_.get();
|
||||
}
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
inline auto basic_strong_ref<T, Alloc>::operator*() const noexcept -> const Repr& {
|
||||
return storage_.get();
|
||||
}
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
inline void swap(
|
||||
basic_strong_ref<T, Alloc>& a,
|
||||
basic_strong_ref<T, Alloc>& b) noexcept {
|
||||
internal::dbglog("Ref swap a.ref=%p a=%p b.ref=%p b=%p",
|
||||
a.get(), &a, b.get(), &b);
|
||||
using std::swap;
|
||||
a.storage_.swap(b.storage_);
|
||||
}
|
||||
|
||||
|
||||
// alias_ref //////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template<typename T>
|
||||
inline alias_ref<T>::alias_ref() noexcept
|
||||
: storage_{nullptr}
|
||||
{}
|
||||
|
||||
template<typename T>
|
||||
inline alias_ref<T>::alias_ref(std::nullptr_t) noexcept
|
||||
: storage_{nullptr}
|
||||
{}
|
||||
|
||||
template<typename T>
|
||||
inline alias_ref<T>::alias_ref(const alias_ref& other) noexcept
|
||||
: storage_{other.get()}
|
||||
{}
|
||||
|
||||
template<typename T>
|
||||
inline alias_ref<T>::alias_ref(javaobject ref) noexcept
|
||||
: storage_(ref) {
|
||||
assert(
|
||||
LocalReferenceAllocator{}.verifyReference(ref) ||
|
||||
GlobalReferenceAllocator{}.verifyReference(ref));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
template<typename TOther, typename /* for SFINAE */>
|
||||
inline alias_ref<T>::alias_ref(alias_ref<TOther> other) noexcept
|
||||
: storage_{other.get()}
|
||||
{}
|
||||
|
||||
template<typename T>
|
||||
template<typename TOther, typename AOther, typename /* for SFINAE */>
|
||||
inline alias_ref<T>::alias_ref(const basic_strong_ref<TOther, AOther>& other) noexcept
|
||||
: storage_{other.get()}
|
||||
{}
|
||||
|
||||
template<typename T>
|
||||
inline alias_ref<T>& alias_ref<T>::operator=(alias_ref other) noexcept {
|
||||
swap(*this, other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline alias_ref<T>::operator bool() const noexcept {
|
||||
return get() != nullptr;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline auto facebook::jni::alias_ref<T>::get() const noexcept -> javaobject {
|
||||
return storage_.jobj();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline auto alias_ref<T>::operator->() noexcept -> Repr* {
|
||||
return &(**this);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline auto alias_ref<T>::operator->() const noexcept -> const Repr* {
|
||||
return &(**this);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline auto alias_ref<T>::operator*() noexcept -> Repr& {
|
||||
return storage_.get();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline auto alias_ref<T>::operator*() const noexcept -> const Repr& {
|
||||
return storage_.get();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void alias_ref<T>::set(javaobject ref) noexcept {
|
||||
storage_.set(ref);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void swap(alias_ref<T>& a, alias_ref<T>& b) noexcept {
|
||||
a.storage_.swap(b.storage_);
|
||||
}
|
||||
|
||||
// Could reduce code duplication by using a pointer-to-function
|
||||
// template argument. I'm not sure whether that would make the code
|
||||
// more maintainable (DRY), or less (too clever/confusing.).
|
||||
template<typename T, typename U>
|
||||
enable_if_t<IsPlainJniReference<T>(), local_ref<T>>
|
||||
static_ref_cast(const local_ref<U>& ref) noexcept
|
||||
{
|
||||
T p = static_cast<T>(ref.get());
|
||||
return make_local(p);
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
enable_if_t<IsPlainJniReference<T>(), global_ref<T>>
|
||||
static_ref_cast(const global_ref<U>& ref) noexcept
|
||||
{
|
||||
T p = static_cast<T>(ref.get());
|
||||
return make_global(p);
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
enable_if_t<IsPlainJniReference<T>(), alias_ref<T>>
|
||||
static_ref_cast(const alias_ref<U>& ref) noexcept
|
||||
{
|
||||
T p = static_cast<T>(ref.get());
|
||||
return wrap_alias(p);
|
||||
}
|
||||
|
||||
template<typename T, typename RefType>
|
||||
auto dynamic_ref_cast(const RefType& ref) ->
|
||||
enable_if_t<IsPlainJniReference<T>(), decltype(static_ref_cast<T>(ref))>
|
||||
{
|
||||
if (!ref) {
|
||||
return decltype(static_ref_cast<T>(ref))();
|
||||
}
|
||||
|
||||
static alias_ref<jclass> target_class = findClassStatic(jtype_traits<T>::base_name().c_str());
|
||||
if (!target_class) {
|
||||
throwNewJavaException("java/lang/ClassCastException",
|
||||
"Could not find class %s.",
|
||||
jtype_traits<T>::base_name().c_str());
|
||||
|
||||
}
|
||||
|
||||
local_ref<jclass> source_class = ref->getClass();
|
||||
|
||||
if (!target_class->isAssignableFrom(source_class)) {
|
||||
throwNewJavaException("java/lang/ClassCastException",
|
||||
"Tried to cast from %s to %s.",
|
||||
source_class->toString().c_str(),
|
||||
jtype_traits<T>::base_name().c_str());
|
||||
}
|
||||
|
||||
return static_ref_cast<T>(ref);
|
||||
}
|
||||
|
||||
}}
|
||||
@@ -1,586 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
|
||||
/** @file References.h
|
||||
*
|
||||
* Functionality similar to smart pointers, but for references into the VM. Four main reference
|
||||
* types are provided: local_ref, global_ref, weak_ref, and alias_ref. All are generic
|
||||
* templates that and refer to objects in the jobject hierarchy. The type of the referred objects
|
||||
* are specified using the template parameter. All reference types except alias_ref own their
|
||||
* underlying reference, just as a std smart pointer owns the underlying raw pointer. In the context
|
||||
* of std smart pointers, these references behave like unique_ptr, and have basically the same
|
||||
* interface. Thus, when the reference is destructed, the plain JNI reference, i.e. the underlying
|
||||
* JNI reference (like the parameters passed directly to JNI functions), is released. The alias
|
||||
* references provides no ownership and is a simple wrapper for plain JNI references.
|
||||
*
|
||||
* All but the weak references provides access to the underlying object using dereferencing, and a
|
||||
* get() method. It is also possible to convert these references to booleans to test for nullity.
|
||||
* To access the underlying object of a weak reference, the reference must either be released, or
|
||||
* the weak reference can be used to create a local or global reference.
|
||||
*
|
||||
* An owning reference is created either by moving the reference from an existing owned reference,
|
||||
* by copying an existing owned reference (which creates a new underlying reference), by using the
|
||||
* default constructor which initialize the reference to nullptr, or by using a helper function. The
|
||||
* helper function exist in two flavors: make_XXX or adopt_XXX.
|
||||
*
|
||||
* Adopting takes a plain JNI reference and wrap it in an owned reference. It takes ownership of the
|
||||
* plain JNI reference so be sure that no one else owns the reference when you adopt it, and make
|
||||
* sure that you know what kind of reference it is.
|
||||
*
|
||||
* New owned references can be created from existing plain JNI references, alias references, local
|
||||
* references, and global references (i.e. non-weak references) using the make_local, make_global,
|
||||
* and make_weak functions.
|
||||
*
|
||||
* Alias references can be implicitly initialized using global, local and plain JNI references using
|
||||
* the wrap_alias function. Here, we don't assume ownership of the passed-in reference, but rather
|
||||
* create a separate reference that we do own, leaving the passed-in reference to its fate.
|
||||
*
|
||||
* Similar rules apply for assignment. An owned reference can be copy or move assigned using a smart
|
||||
* reference of the same type. In the case of copy assignment a new reference is created. Alias
|
||||
* reference can also be assigned new values, but since they are simple wrappers of plain JNI
|
||||
* references there is no move semantics involved.
|
||||
*
|
||||
* Alias references are special in that they do not own the object and can therefore safely be
|
||||
* converted to and from its corresponding plain JNI reference. They are useful as parameters of
|
||||
* functions that do not affect the lifetime of a reference. Usage can be compared with using plain
|
||||
* JNI pointers as parameters where a function does not take ownership of the underlying object.
|
||||
*
|
||||
* The local, global, and alias references makes it possible to access methods in the underlying
|
||||
* objects. A core set of classes are implemented in CoreClasses.h, and user defined wrappers are
|
||||
* supported (see example below). The wrappers also supports inheritance so a wrapper can inherit
|
||||
* from another wrapper to gain access to its functionality. As an example the jstring wrapper
|
||||
* inherits from the jobject wrapper, so does the jclass wrapper. That means that you can for
|
||||
* example call the toString() method using the jclass wrapper, or any other class that inherits
|
||||
* from the jobject wrapper.
|
||||
*
|
||||
* Note that the wrappers are parameterized on the static type of your (jobject) pointer, thus if
|
||||
* you have a jobject that refers to a Java String you will need to cast it to jstring to get the
|
||||
* jstring wrapper. This also mean that if you make a down cast that is invalid there will be no one
|
||||
* stopping you and the wrappers currently does not detect this which can cause crashes. Thus, cast
|
||||
* wisely.
|
||||
*
|
||||
* @include WrapperSample.cpp
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#include <fb/visibility.h>
|
||||
|
||||
#include "ReferenceAllocators.h"
|
||||
#include "TypeTraits.h"
|
||||
#include "References-forward.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
/// Convenience function to wrap an existing local reference
|
||||
template<typename T>
|
||||
local_ref<T> adopt_local(T ref) noexcept;
|
||||
|
||||
/// Convenience function to wrap an existing global reference
|
||||
template<typename T>
|
||||
global_ref<T> adopt_global(T ref) noexcept;
|
||||
|
||||
/// Convenience function to wrap an existing weak reference
|
||||
template<typename T>
|
||||
weak_ref<T> adopt_weak_global(T ref) noexcept;
|
||||
|
||||
|
||||
/// Swaps two owning references of the same type
|
||||
template<typename T>
|
||||
void swap(weak_ref<T>& a, weak_ref<T>& b) noexcept;
|
||||
|
||||
/// Swaps two owning references of the same type
|
||||
template<typename T, typename Alloc>
|
||||
void swap(basic_strong_ref<T, Alloc>& a, basic_strong_ref<T, Alloc>& b) noexcept;
|
||||
|
||||
/**
|
||||
* Retrieve the plain reference from a plain reference.
|
||||
*/
|
||||
template<typename T>
|
||||
enable_if_t<IsPlainJniReference<T>(), T> getPlainJniReference(T ref);
|
||||
|
||||
/**
|
||||
* Retrieve the plain reference from an alias reference.
|
||||
*/
|
||||
template<typename T>
|
||||
JniType<T> getPlainJniReference(alias_ref<T> ref);
|
||||
|
||||
/**
|
||||
* Retrieve the plain JNI reference from any reference owned reference.
|
||||
*/
|
||||
template<typename T, typename Alloc>
|
||||
JniType<T> getPlainJniReference(const base_owned_ref<T, Alloc>& ref);
|
||||
|
||||
class JObject;
|
||||
class JClass;
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename T, typename Enable = void>
|
||||
struct HasJniRefRepr : std::false_type {};
|
||||
|
||||
template <typename T>
|
||||
struct HasJniRefRepr<T, typename std::enable_if<!std::is_same<typename T::JniRefRepr, void>::value, void>::type> : std::true_type {
|
||||
using type = typename T::JniRefRepr;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct RefReprType<T*> {
|
||||
using type = typename std::conditional<HasJniRefRepr<T>::value, typename HasJniRefRepr<T>::type, JObjectWrapper<T*>>::type;
|
||||
static_assert(std::is_base_of<JObject, type>::value,
|
||||
"Repr type missing JObject base.");
|
||||
static_assert(std::is_same<type, typename RefReprType<type>::type>::value,
|
||||
"RefReprType<T> not idempotent");
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct RefReprType<T, typename std::enable_if<std::is_base_of<JObject, T>::value, void>::type> {
|
||||
using type = T;
|
||||
static_assert(std::is_base_of<JObject, type>::value,
|
||||
"Repr type missing JObject base.");
|
||||
static_assert(std::is_same<type, typename RefReprType<type>::type>::value,
|
||||
"RefReprType<T> not idempotent");
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct JavaObjectType {
|
||||
using type = typename RefReprType<T>::type::javaobject;
|
||||
static_assert(IsPlainJniReference<type>(),
|
||||
"JavaObjectType<T> not a plain jni reference");
|
||||
static_assert(std::is_same<type, typename JavaObjectType<type>::type>::value,
|
||||
"JavaObjectType<T> not idempotent");
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct JavaObjectType<JObjectWrapper<T>> {
|
||||
using type = T;
|
||||
static_assert(IsPlainJniReference<type>(),
|
||||
"JavaObjectType<T> not a plain jni reference");
|
||||
static_assert(std::is_same<type, typename JavaObjectType<type>::type>::value,
|
||||
"JavaObjectType<T> not idempotent");
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct JavaObjectType<T*> {
|
||||
using type = T*;
|
||||
static_assert(IsPlainJniReference<type>(),
|
||||
"JavaObjectType<T> not a plain jni reference");
|
||||
static_assert(std::is_same<type, typename JavaObjectType<type>::type>::value,
|
||||
"JavaObjectType<T> not idempotent");
|
||||
};
|
||||
|
||||
template <typename Repr>
|
||||
struct ReprStorage {
|
||||
explicit ReprStorage(JniType<Repr> obj) noexcept;
|
||||
|
||||
void set(JniType<Repr> obj) noexcept;
|
||||
|
||||
Repr& get() noexcept;
|
||||
const Repr& get() const noexcept;
|
||||
JniType<Repr> jobj() const noexcept;
|
||||
|
||||
void swap(ReprStorage& other) noexcept;
|
||||
private:
|
||||
ReprStorage() = delete;
|
||||
ReprStorage(const ReprStorage&) = delete;
|
||||
ReprStorage(ReprStorage&&) = delete;
|
||||
ReprStorage& operator=(const ReprStorage&) = delete;
|
||||
ReprStorage& operator=(ReprStorage&&) = delete;
|
||||
|
||||
using Storage = typename std::aligned_storage<sizeof(JObjectBase), alignof(JObjectBase)>::type;
|
||||
Storage storage_;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* Create a new local reference from an existing reference
|
||||
*
|
||||
* @param ref a plain JNI, alias, or strong reference
|
||||
* @return an owned local reference (referring to null if the input does)
|
||||
* @throws std::bad_alloc if the JNI reference could not be created
|
||||
*/
|
||||
template<typename T>
|
||||
enable_if_t<IsNonWeakReference<T>(), local_ref<plain_jni_reference_t<T>>>
|
||||
make_local(const T& r);
|
||||
|
||||
/**
|
||||
* Create a new global reference from an existing reference
|
||||
*
|
||||
* @param ref a plain JNI, alias, or strong reference
|
||||
* @return an owned global reference (referring to null if the input does)
|
||||
* @throws std::bad_alloc if the JNI reference could not be created
|
||||
*/
|
||||
template<typename T>
|
||||
enable_if_t<IsNonWeakReference<T>(), global_ref<plain_jni_reference_t<T>>>
|
||||
make_global(const T& r);
|
||||
|
||||
/**
|
||||
* Create a new weak global reference from an existing reference
|
||||
*
|
||||
* @param ref a plain JNI, alias, or strong reference
|
||||
* @return an owned weak global reference (referring to null if the input does)
|
||||
* @throws std::bad_alloc if the returned reference is null
|
||||
*/
|
||||
template<typename T>
|
||||
enable_if_t<IsNonWeakReference<T>(), weak_ref<plain_jni_reference_t<T>>>
|
||||
make_weak(const T& r);
|
||||
|
||||
/**
|
||||
* Compare two references to see if they refer to the same object
|
||||
*/
|
||||
template<typename T1, typename T2>
|
||||
enable_if_t<IsNonWeakReference<T1>() && IsNonWeakReference<T2>(), bool>
|
||||
operator==(const T1& a, const T2& b);
|
||||
|
||||
/**
|
||||
* Compare two references to see if they don't refer to the same object
|
||||
*/
|
||||
template<typename T1, typename T2>
|
||||
enable_if_t<IsNonWeakReference<T1>() && IsNonWeakReference<T2>(), bool>
|
||||
operator!=(const T1& a, const T2& b);
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
class base_owned_ref {
|
||||
public:
|
||||
using javaobject = JniType<T>;
|
||||
|
||||
/**
|
||||
* Release the ownership and set the reference to null. Thus no deleter is invoked.
|
||||
* @return Returns the reference
|
||||
*/
|
||||
javaobject release() noexcept;
|
||||
|
||||
/**
|
||||
* Reset the reference to refer to nullptr.
|
||||
*/
|
||||
void reset() noexcept;
|
||||
|
||||
protected:
|
||||
using Repr = ReprType<T>;
|
||||
detail::ReprStorage<Repr> storage_;
|
||||
|
||||
javaobject get() const noexcept;
|
||||
void set(javaobject ref) noexcept;
|
||||
|
||||
/*
|
||||
* Wrap an existing reference and transfers its ownership to the newly created unique reference.
|
||||
* NB! Does not create a new reference
|
||||
*/
|
||||
explicit base_owned_ref(javaobject reference) noexcept;
|
||||
|
||||
/// Create a null reference
|
||||
base_owned_ref() noexcept;
|
||||
|
||||
/// Create a null reference
|
||||
explicit base_owned_ref(std::nullptr_t) noexcept;
|
||||
|
||||
/// Copy constructor (note creates a new reference)
|
||||
base_owned_ref(const base_owned_ref& other);
|
||||
template<typename U>
|
||||
base_owned_ref(const base_owned_ref<U, Alloc>& other);
|
||||
|
||||
/// Transfers ownership of an underlying reference from one unique reference to another
|
||||
base_owned_ref(base_owned_ref&& other) noexcept;
|
||||
template<typename U>
|
||||
base_owned_ref(base_owned_ref<U, Alloc>&& other) noexcept;
|
||||
|
||||
/// The delete the underlying reference if applicable
|
||||
~base_owned_ref() noexcept;
|
||||
|
||||
|
||||
/// Assignment operator (note creates a new reference)
|
||||
base_owned_ref& operator=(const base_owned_ref& other);
|
||||
|
||||
/// Assignment by moving a reference thus not creating a new reference
|
||||
base_owned_ref& operator=(base_owned_ref&& rhs) noexcept;
|
||||
|
||||
void reset(javaobject reference) noexcept;
|
||||
|
||||
friend javaobject jni::getPlainJniReference<>(const base_owned_ref& ref);
|
||||
|
||||
template<typename U, typename UAlloc>
|
||||
friend class base_owned_ref;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A smart reference that owns its underlying JNI reference. The class provides basic
|
||||
* functionality to handle a reference but gives no access to it unless the reference is
|
||||
* released, thus no longer owned. The API is stolen with pride from unique_ptr and the
|
||||
* semantics should be basically the same. This class should not be used directly, instead use
|
||||
* @ref weak_ref
|
||||
*/
|
||||
template<typename T>
|
||||
class weak_ref : public base_owned_ref<T, WeakGlobalReferenceAllocator> {
|
||||
public:
|
||||
using javaobject = JniType<T>;
|
||||
|
||||
using Allocator = WeakGlobalReferenceAllocator;
|
||||
|
||||
// This inherits non-default, non-copy, non-move ctors.
|
||||
using base_owned_ref<T, Allocator>::base_owned_ref;
|
||||
|
||||
/// Create a null reference
|
||||
weak_ref() noexcept
|
||||
: base_owned_ref<T, Allocator>{} {}
|
||||
|
||||
/// Create a null reference
|
||||
/* implicit */ weak_ref(std::nullptr_t) noexcept
|
||||
: base_owned_ref<T, Allocator>{nullptr} {}
|
||||
|
||||
/// Copy constructor (note creates a new reference)
|
||||
weak_ref(const weak_ref& other)
|
||||
: base_owned_ref<T, Allocator>{other} {}
|
||||
|
||||
// This needs to be explicit to change its visibility.
|
||||
template<typename U>
|
||||
weak_ref(const weak_ref<U>& other)
|
||||
: base_owned_ref<T, Allocator>{other} {}
|
||||
|
||||
/// Transfers ownership of an underlying reference from one unique reference to another
|
||||
weak_ref(weak_ref&& other) noexcept
|
||||
: base_owned_ref<T, Allocator>{std::move(other)} {}
|
||||
|
||||
|
||||
/// Assignment operator (note creates a new reference)
|
||||
weak_ref& operator=(const weak_ref& other);
|
||||
|
||||
/// Assignment by moving a reference thus not creating a new reference
|
||||
weak_ref& operator=(weak_ref&& rhs) noexcept;
|
||||
|
||||
// Creates an owned local reference to the referred object or to null if the object is reclaimed
|
||||
local_ref<T> lockLocal() const;
|
||||
|
||||
// Creates an owned global reference to the referred object or to null if the object is reclaimed
|
||||
global_ref<T> lockGlobal() const;
|
||||
|
||||
private:
|
||||
// get/release/reset on weak_ref are not exposed to users.
|
||||
using base_owned_ref<T, Allocator>::get;
|
||||
using base_owned_ref<T, Allocator>::release;
|
||||
using base_owned_ref<T, Allocator>::reset;
|
||||
/*
|
||||
* Wrap an existing reference and transfers its ownership to the newly created unique reference.
|
||||
* NB! Does not create a new reference
|
||||
*/
|
||||
explicit weak_ref(javaobject reference) noexcept
|
||||
: base_owned_ref<T, Allocator>{reference} {}
|
||||
|
||||
template<typename T2> friend class weak_ref;
|
||||
friend weak_ref<javaobject> adopt_weak_global<javaobject>(javaobject ref) noexcept;
|
||||
friend void swap<T>(weak_ref& a, weak_ref& b) noexcept;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A class representing owned strong references to Java objects. This class
|
||||
* should not be used directly, instead use @ref local_ref, or @ref global_ref.
|
||||
*/
|
||||
template<typename T, typename Alloc>
|
||||
class basic_strong_ref : public base_owned_ref<T, Alloc> {
|
||||
using typename base_owned_ref<T, Alloc>::Repr;
|
||||
public:
|
||||
using javaobject = JniType<T>;
|
||||
|
||||
using Allocator = Alloc;
|
||||
|
||||
// This inherits non-default, non-copy, non-move ctors.
|
||||
using base_owned_ref<T, Alloc>::base_owned_ref;
|
||||
using base_owned_ref<T, Alloc>::release;
|
||||
using base_owned_ref<T, Alloc>::reset;
|
||||
|
||||
/// Create a null reference
|
||||
basic_strong_ref() noexcept
|
||||
: base_owned_ref<T, Alloc>{} {}
|
||||
|
||||
/// Create a null reference
|
||||
/* implicit */ basic_strong_ref(std::nullptr_t) noexcept
|
||||
: base_owned_ref<T, Alloc>{nullptr} {}
|
||||
|
||||
/// Copy constructor (note creates a new reference)
|
||||
basic_strong_ref(const basic_strong_ref& other)
|
||||
: base_owned_ref<T, Alloc>{other} {}
|
||||
|
||||
// This needs to be explicit to change its visibility.
|
||||
template<typename U>
|
||||
basic_strong_ref(const basic_strong_ref<U, Alloc>& other)
|
||||
: base_owned_ref<T, Alloc>{other} {}
|
||||
|
||||
/// Transfers ownership of an underlying reference from one unique reference to another
|
||||
basic_strong_ref(basic_strong_ref&& other) noexcept
|
||||
: base_owned_ref<T, Alloc>{std::move(other)} {}
|
||||
|
||||
/// Assignment operator (note creates a new reference)
|
||||
basic_strong_ref& operator=(const basic_strong_ref& other);
|
||||
|
||||
/// Assignment by moving a reference thus not creating a new reference
|
||||
basic_strong_ref& operator=(basic_strong_ref&& rhs) noexcept;
|
||||
|
||||
/// Get the plain JNI reference
|
||||
using base_owned_ref<T, Allocator>::get;
|
||||
|
||||
/// Release the ownership of the reference and return the wrapped reference in an alias
|
||||
alias_ref<T> releaseAlias() noexcept;
|
||||
|
||||
/// Checks if the reference points to a non-null object
|
||||
explicit operator bool() const noexcept;
|
||||
|
||||
/// Access the functionality provided by the object wrappers
|
||||
Repr* operator->() noexcept;
|
||||
|
||||
/// Access the functionality provided by the object wrappers
|
||||
const Repr* operator->() const noexcept;
|
||||
|
||||
/// Provide a reference to the underlying wrapper (be sure that it is non-null before invoking)
|
||||
Repr& operator*() noexcept;
|
||||
|
||||
/// Provide a const reference to the underlying wrapper (be sure that it is non-null
|
||||
/// before invoking)
|
||||
const Repr& operator*() const noexcept;
|
||||
|
||||
private:
|
||||
|
||||
using base_owned_ref<T, Alloc>::storage_;
|
||||
|
||||
/*
|
||||
* Wrap an existing reference and transfers its ownership to the newly created unique reference.
|
||||
* NB! Does not create a new reference
|
||||
*/
|
||||
explicit basic_strong_ref(javaobject reference) noexcept
|
||||
: base_owned_ref<T, Alloc>{reference} {}
|
||||
|
||||
|
||||
friend local_ref<T> adopt_local<T>(T ref) noexcept;
|
||||
friend global_ref<T> adopt_global<T>(T ref) noexcept;
|
||||
friend void swap<T, Alloc>(basic_strong_ref& a, basic_strong_ref& b) noexcept;
|
||||
};
|
||||
|
||||
|
||||
template<typename T>
|
||||
enable_if_t<IsPlainJniReference<T>(), alias_ref<T>> wrap_alias(T ref) noexcept;
|
||||
|
||||
/// Swaps to alias reference of the same type
|
||||
template<typename T>
|
||||
void swap(alias_ref<T>& a, alias_ref<T>& b) noexcept;
|
||||
|
||||
/**
|
||||
* A non-owning variant of the smart references (a dumb reference). These references still provide
|
||||
* access to the functionality of the @ref JObjectWrapper specializations including exception
|
||||
* handling and ease of use. Use this representation when you don't want to claim ownership of the
|
||||
* underlying reference (compare to using raw pointers instead of smart pointers.) For symmetry use
|
||||
* @ref alias_ref instead of this class.
|
||||
*/
|
||||
template<typename T>
|
||||
class alias_ref {
|
||||
using Repr = ReprType<T>;
|
||||
|
||||
public:
|
||||
using javaobject = JniType<T>;
|
||||
|
||||
/// Create a null reference
|
||||
alias_ref() noexcept;
|
||||
|
||||
/// Create a null reference
|
||||
/* implicit */ alias_ref(std::nullptr_t) noexcept;
|
||||
|
||||
/// Copy constructor
|
||||
alias_ref(const alias_ref& other) noexcept;
|
||||
|
||||
/// Wrap an existing plain JNI reference
|
||||
/* implicit */ alias_ref(javaobject ref) noexcept;
|
||||
|
||||
/// Wrap an existing smart reference of any type convertible to T
|
||||
template<
|
||||
typename TOther,
|
||||
typename = enable_if_t<
|
||||
IsConvertible<JniType<TOther>, javaobject>(), T>
|
||||
>
|
||||
alias_ref(alias_ref<TOther> other) noexcept;
|
||||
|
||||
/// Wrap an existing alias reference of a type convertible to T
|
||||
template<
|
||||
typename TOther,
|
||||
typename AOther,
|
||||
typename = enable_if_t<
|
||||
IsConvertible<JniType<TOther>, javaobject>(), T>
|
||||
>
|
||||
alias_ref(const basic_strong_ref<TOther, AOther>& other) noexcept;
|
||||
|
||||
/// Assignment operator
|
||||
alias_ref& operator=(alias_ref other) noexcept;
|
||||
|
||||
/// Checks if the reference points to a non-null object
|
||||
explicit operator bool() const noexcept;
|
||||
|
||||
/// Converts back to a plain JNI reference
|
||||
javaobject get() const noexcept;
|
||||
|
||||
/// Access the functionality provided by the object wrappers
|
||||
Repr* operator->() noexcept;
|
||||
|
||||
/// Access the functionality provided by the object wrappers
|
||||
const Repr* operator->() const noexcept;
|
||||
|
||||
/// Provide a guaranteed non-null reference (be sure that it is non-null before invoking)
|
||||
Repr& operator*() noexcept;
|
||||
|
||||
/// Provide a guaranteed non-null reference (be sure that it is non-null before invoking)
|
||||
const Repr& operator*() const noexcept;
|
||||
|
||||
private:
|
||||
void set(javaobject ref) noexcept;
|
||||
|
||||
detail::ReprStorage<Repr> storage_;
|
||||
|
||||
friend void swap<T>(alias_ref& a, alias_ref& b) noexcept;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* RAII object to create a local JNI frame, using PushLocalFrame/PopLocalFrame.
|
||||
*
|
||||
* This is useful when you have a call which is initiated from C++-land, and therefore
|
||||
* doesn't automatically get a local JNI frame managed for you by the JNI framework.
|
||||
*/
|
||||
class FBEXPORT JniLocalScope {
|
||||
public:
|
||||
JniLocalScope(JNIEnv* p_env, jint capacity);
|
||||
~JniLocalScope();
|
||||
|
||||
private:
|
||||
JNIEnv* env_;
|
||||
bool hasFrame_;
|
||||
};
|
||||
|
||||
template<typename T, typename U>
|
||||
enable_if_t<IsPlainJniReference<T>(), local_ref<T>>
|
||||
static_ref_cast(const local_ref<U>& ref) noexcept;
|
||||
|
||||
template<typename T, typename U>
|
||||
enable_if_t<IsPlainJniReference<T>(), global_ref<T>>
|
||||
static_ref_cast(const global_ref<U>& ref) noexcept;
|
||||
|
||||
template<typename T, typename U>
|
||||
enable_if_t<IsPlainJniReference<T>(), alias_ref<T>>
|
||||
static_ref_cast(const alias_ref<U>& ref) noexcept;
|
||||
|
||||
template<typename T, typename RefType>
|
||||
auto dynamic_ref_cast(const RefType& ref) ->
|
||||
enable_if_t<IsPlainJniReference<T>(), decltype(static_ref_cast<T>(ref))> ;
|
||||
|
||||
}}
|
||||
|
||||
#include "References-inl.h"
|
||||
@@ -1,166 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Exceptions.h"
|
||||
#include "Hybrid.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
namespace detail {
|
||||
|
||||
#ifdef __i386__
|
||||
// X86 ABI forces 16 byte stack alignment on calls. Unfortunately
|
||||
// sometimes Dalvik chooses not to obey the ABI:
|
||||
// - https://code.google.com/p/android/issues/detail?id=61012
|
||||
// - https://android.googlesource.com/platform/ndk/+/81696d2%5E!/
|
||||
// Therefore, we tell the compiler to re-align the stack on entry
|
||||
// to our JNI functions.
|
||||
#define JNI_ENTRY_POINT __attribute__((force_align_arg_pointer))
|
||||
#else
|
||||
#define JNI_ENTRY_POINT
|
||||
#endif
|
||||
|
||||
template <typename R>
|
||||
struct CreateDefault {
|
||||
static R create() {
|
||||
return R{};
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct CreateDefault<void> {
|
||||
static void create() {}
|
||||
};
|
||||
|
||||
template <typename R>
|
||||
using Converter = Convert<typename std::decay<R>::type>;
|
||||
|
||||
template <typename F, F func, typename R, typename... Args>
|
||||
struct WrapForVoidReturn {
|
||||
static typename Converter<R>::jniType call(Args&&... args) {
|
||||
return Converter<R>::toJniRet(func(std::forward<Args>(args)...));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename F, F func, typename... Args>
|
||||
struct WrapForVoidReturn<F, func, void, Args...> {
|
||||
static void call(Args&&... args) {
|
||||
func(std::forward<Args>(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
// registration wrapper for legacy JNI-style functions
|
||||
template<typename F, F func, typename C, typename R, typename... Args>
|
||||
struct BareJniWrapper {
|
||||
JNI_ENTRY_POINT static R call(JNIEnv* env, jobject obj, Args... args) {
|
||||
ThreadScope ts(env, internal::CacheEnvTag{});
|
||||
try {
|
||||
return (*func)(env, static_cast<JniType<C>>(obj), args...);
|
||||
} catch (...) {
|
||||
translatePendingCppExceptionToJavaException();
|
||||
return CreateDefault<R>::create();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// registration wrappers for functions, with autoconversion of arguments.
|
||||
template<typename F, F func, typename C, typename R, typename... Args>
|
||||
struct FunctionWrapper {
|
||||
using jniRet = typename Converter<R>::jniType;
|
||||
JNI_ENTRY_POINT static jniRet call(JNIEnv* env, jobject obj, typename Converter<Args>::jniType... args) {
|
||||
ThreadScope ts(env, internal::CacheEnvTag{});
|
||||
try {
|
||||
return WrapForVoidReturn<F, func, R, JniType<C>, Args...>::call(
|
||||
static_cast<JniType<C>>(obj), Converter<Args>::fromJni(args)...);
|
||||
} catch (...) {
|
||||
translatePendingCppExceptionToJavaException();
|
||||
return CreateDefault<jniRet>::create();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// registration wrappers for non-static methods, with autoconversion of arguments.
|
||||
template<typename M, M method, typename C, typename R, typename... Args>
|
||||
struct MethodWrapper {
|
||||
using jhybrid = typename C::jhybridobject;
|
||||
static R dispatch(alias_ref<jhybrid> ref, Args&&... args) {
|
||||
try {
|
||||
// This is usually a noop, but if the hybrid object is a
|
||||
// base class of other classes which register JNI methods,
|
||||
// this will get the right type for the registered method.
|
||||
auto cobj = static_cast<C*>(ref->cthis());
|
||||
return (cobj->*method)(std::forward<Args>(args)...);
|
||||
} catch (const std::exception& ex) {
|
||||
C::mapException(ex);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
JNI_ENTRY_POINT static typename Converter<R>::jniType call(
|
||||
JNIEnv* env, jobject obj, typename Converter<Args>::jniType... args) {
|
||||
return FunctionWrapper<R(*)(alias_ref<jhybrid>, Args&&...), dispatch, jhybrid, R, Args...>::call(env, obj, args...);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename F, F func, typename C, typename R, typename... Args>
|
||||
inline NativeMethodWrapper* exceptionWrapJNIMethod(R (*)(JNIEnv*, C, Args... args)) {
|
||||
// This intentionally erases the real type; JNI will do it anyway
|
||||
return reinterpret_cast<NativeMethodWrapper*>(&(BareJniWrapper<F, func, C, R, Args...>::call));
|
||||
}
|
||||
|
||||
template<typename F, F func, typename C, typename R, typename... Args>
|
||||
inline NativeMethodWrapper* exceptionWrapJNIMethod(R (*)(alias_ref<C>, Args... args)) {
|
||||
// This intentionally erases the real type; JNI will do it anyway
|
||||
return reinterpret_cast<NativeMethodWrapper*>(&(FunctionWrapper<F, func, C, R, Args...>::call));
|
||||
}
|
||||
|
||||
template<typename M, M method, typename C, typename R, typename... Args>
|
||||
inline NativeMethodWrapper* exceptionWrapJNIMethod(R (C::*method0)(Args... args)) {
|
||||
// This intentionally erases the real type; JNI will do it anyway
|
||||
return reinterpret_cast<NativeMethodWrapper*>(&(MethodWrapper<M, method, C, R, Args...>::call));
|
||||
}
|
||||
|
||||
template<typename R, typename C, typename... Args>
|
||||
inline std::string makeDescriptor(R (*)(JNIEnv*, C, Args... args)) {
|
||||
return jmethod_traits<R(Args...)>::descriptor();
|
||||
}
|
||||
|
||||
template<typename R, typename C, typename... Args>
|
||||
inline std::string makeDescriptor(R (*)(alias_ref<C>, Args... args)) {
|
||||
return jmethod_traits_from_cxx<R(Args...)>::descriptor();
|
||||
}
|
||||
|
||||
template<typename R, typename C, typename... Args>
|
||||
inline std::string makeDescriptor(R (C::*)(Args... args)) {
|
||||
return jmethod_traits_from_cxx<R(Args...)>::descriptor();
|
||||
}
|
||||
|
||||
template<typename R, typename ...Args>
|
||||
template<R(*func)(Args...)>
|
||||
JNI_ENTRY_POINT R CriticalMethod<R(*)(Args...)>::call(alias_ref<jclass>, Args... args) {
|
||||
static_assert(
|
||||
IsJniPrimitive<R>() || std::is_void<R>(),
|
||||
"Critical Native Methods may only return primitive JNI types, or void.");
|
||||
static_assert(
|
||||
AreJniPrimitives<Args...>(),
|
||||
"Critical Native Methods may only use primitive JNI types as parameters");
|
||||
|
||||
return func(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename R, typename ...Args>
|
||||
template<R(*func)(Args...)>
|
||||
inline std::string CriticalMethod<R(*)(Args...)>::desc() {
|
||||
return makeDescriptor(call<func>);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}}
|
||||
@@ -1,168 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <jni.h>
|
||||
#include "References.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
namespace detail {
|
||||
|
||||
// This uses the real JNI function as a non-type template parameter to
|
||||
// cause a (static member) function to exist with the same signature,
|
||||
// but with try/catch exception translation.
|
||||
template<typename F, F func, typename C, typename R, typename... Args>
|
||||
NativeMethodWrapper* exceptionWrapJNIMethod(R (*func0)(JNIEnv*, jobject, Args... args));
|
||||
|
||||
// Automatically wrap object argument, and don't take env explicitly.
|
||||
template<typename F, F func, typename C, typename R, typename... Args>
|
||||
NativeMethodWrapper* exceptionWrapJNIMethod(R (*func0)(alias_ref<C>, Args... args));
|
||||
|
||||
// Extract C++ instance from object, and invoke given method on it,
|
||||
template<typename M, M method, typename C, typename R, typename... Args>
|
||||
NativeMethodWrapper* exceptionWrapJNIMethod(R (C::*method0)(Args... args));
|
||||
|
||||
// This uses deduction to figure out the descriptor name if the types
|
||||
// are primitive or have JObjectWrapper specializations.
|
||||
template<typename R, typename C, typename... Args>
|
||||
std::string makeDescriptor(R (*func)(JNIEnv*, C, Args... args));
|
||||
|
||||
// This uses deduction to figure out the descriptor name if the types
|
||||
// are primitive or have JObjectWrapper specializations.
|
||||
template<typename R, typename C, typename... Args>
|
||||
std::string makeDescriptor(R (*func)(alias_ref<C>, Args... args));
|
||||
|
||||
// This uses deduction to figure out the descriptor name if the types
|
||||
// are primitive or have JObjectWrapper specializations.
|
||||
template<typename R, typename C, typename... Args>
|
||||
std::string makeDescriptor(R (C::*method0)(Args... args));
|
||||
|
||||
template<typename F>
|
||||
struct CriticalMethod;
|
||||
|
||||
template<typename R, typename ...Args>
|
||||
struct CriticalMethod<R(*)(Args...)> {
|
||||
template<R(*func)(Args...)>
|
||||
static R call(alias_ref<jclass>, Args... args);
|
||||
|
||||
template<R(*func)(Args...)>
|
||||
inline static std::string desc();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
// We have to use macros here, because the func needs to be used
|
||||
// as both a decltype expression argument and as a non-type template
|
||||
// parameter, since C++ provides no way for translateException
|
||||
// to deduce the type of its non-type template parameter.
|
||||
// The empty string in the macros below ensures that name
|
||||
// is always a string literal (because that syntax is only
|
||||
// valid when name is a string literal).
|
||||
#define makeNativeMethod2(name, func) \
|
||||
{ name "", ::facebook::jni::detail::makeDescriptor(&func), \
|
||||
::facebook::jni::detail::exceptionWrapJNIMethod<decltype(&func), &func>(&func) }
|
||||
|
||||
#define makeNativeMethod3(name, desc, func) \
|
||||
{ name "", desc, \
|
||||
::facebook::jni::detail::exceptionWrapJNIMethod<decltype(&func), &func>(&func) }
|
||||
|
||||
// Variadic template hacks to get macros with different numbers of
|
||||
// arguments. Usage instructions are in CoreClasses.h.
|
||||
#define makeNativeMethodN(a, b, c, count, ...) makeNativeMethod ## count
|
||||
#define makeNativeMethod(...) makeNativeMethodN(__VA_ARGS__, 3, 2)(__VA_ARGS__)
|
||||
|
||||
|
||||
// FAST CALLS / CRITICAL CALLS
|
||||
// Android up to and including v7 supports "fast calls" by prefixing the method
|
||||
// signature with an exclamation mark.
|
||||
// Android v8+ supports fast calls by annotating methods:
|
||||
// https://source.android.com/devices/tech/dalvik/improvements#faster-native-methods
|
||||
//
|
||||
// YOU ALMOST CERTAINLY DO NOT NEED THIS AND IT IS DANGEROUS.
|
||||
// YOU ALMOST CERTAINLY DO NOT NEED THIS AND IT IS DANGEROUS.
|
||||
// YOU ALMOST CERTAINLY DO NOT NEED THIS AND IT IS DANGEROUS.
|
||||
// YOU ALMOST CERTAINLY DO NOT NEED THIS AND IT IS DANGEROUS.
|
||||
// YOU ALMOST CERTAINLY DO NOT NEED THIS AND IT IS DANGEROUS.
|
||||
//
|
||||
// "Fast" calls are only on the order of a few dozen NANO-seconds faster than
|
||||
// regular JNI calls. If your method does almost aaanything of consequence - if
|
||||
// you loop, if you write to a log, if you call another method, if you even
|
||||
// simply allocate or deallocate - then the method body will significantly
|
||||
// outweigh the method overhead.
|
||||
//
|
||||
// The difference between a regular JNI method and a "FastJNI" method (as
|
||||
// they're called inside the runtime) is that a FastJNI method doesn't mark the
|
||||
// thread as executing native code, and by skipping that avoids the locking and
|
||||
// thread state check overhead of interacting with the Garbage Collector.
|
||||
//
|
||||
// To understand why this is dangerous, you need to understand a bit about the
|
||||
// GC. In order to perform its work the GC needs to have at least one (usually
|
||||
// two in modern implementations) "stop the world" checkpoints where it can
|
||||
// guarantee that all managed-code execution is paused. The VM performs these
|
||||
// checkpoints at allocations, method boundaries, and each backward branch (ie
|
||||
// anytime you loop). When the GC wants to run, it will signal to all managed
|
||||
// threads that they should pause at the next checkpoint, and then it will wait
|
||||
// for every thread in the system to transition from the "runnable" state into a
|
||||
// "waiting" state. Once every thread has stopped, the GC thread can perform the
|
||||
// work it needs to and then it will trigger the execution threads to resume.
|
||||
//
|
||||
// JNI methods fit neatly into the above paradigm: They're still methods, so
|
||||
// they perform GC checkpoints at method entry and method exit. JNI methods also
|
||||
// perform checkpoints at any JNI boundary crossing - ie, any time you call
|
||||
// GetObjectField etc. Because access to managed objects from native code is
|
||||
// tightly controlled, the VM is able to mark threads executing native methods
|
||||
// into a special "native" state which the GC is able to ignore: It knows they
|
||||
// can't touch managed objects (without hitting a checkpoint) so it doesn't care
|
||||
// about them.
|
||||
//
|
||||
// JNI critical methods don't perform that "runnable" -> "native" thread state
|
||||
// transition. Skipping that transition allows them to shave about 20ns off
|
||||
// their total execution time, but it means that the GC has to wait for them to
|
||||
// complete before it can move forward. If a critical method begins blocking,
|
||||
// say on a long loop, or an I/O operation, or on perhaps a mutex, then the GC
|
||||
// will also block, and because the GC is blocking the entire rest of the VM
|
||||
// (which is waiting on the GC) will block. If the critical method is blocking
|
||||
// on a mutex that's already held by the GC - for example, the VM's internal
|
||||
// weak_globals_lock_ which guards modifications to the weak global reference
|
||||
// table (and is required in order to create or free a weak_ref<>) - then you
|
||||
// have a system-wide deadlock.
|
||||
|
||||
// prefixes a JNI method signature as android "fast call".
|
||||
#if defined(__ANDROID__) && defined(FBJNI_WITH_FAST_CALLS)
|
||||
#define FBJNI_PREFIX_FAST_CALL(desc) (std::string{"!"} + desc)
|
||||
#else
|
||||
#define FBJNI_PREFIX_FAST_CALL(desc) (desc)
|
||||
#endif
|
||||
|
||||
#define makeCriticalNativeMethod3(name, desc, func) \
|
||||
makeNativeMethod3( \
|
||||
name, \
|
||||
FBJNI_PREFIX_FAST_CALL(desc), \
|
||||
::facebook::jni::detail::CriticalMethod<decltype(&func)>::call<&func>)
|
||||
|
||||
#define makeCriticalNativeMethod2(name, func) \
|
||||
makeCriticalNativeMethod3( \
|
||||
name, \
|
||||
::facebook::jni::detail::CriticalMethod<decltype(&func)>::desc<&func>(), \
|
||||
func)
|
||||
|
||||
#define makeCriticalNativeMethodN(a, b, c, count, ...) makeCriticalNativeMethod ## count
|
||||
|
||||
// YOU ALMOST CERTAINLY DO NOT NEED THIS AND IT IS DANGEROUS.
|
||||
// YOU ALMOST CERTAINLY DO NOT NEED THIS AND IT IS DANGEROUS.
|
||||
// YOU ALMOST CERTAINLY DO NOT NEED THIS AND IT IS DANGEROUS.
|
||||
// YOU ALMOST CERTAINLY DO NOT NEED THIS AND IT IS DANGEROUS.
|
||||
// YOU ALMOST CERTAINLY DO NOT NEED THIS AND IT IS DANGEROUS.
|
||||
// See above for an explanation.
|
||||
#define makeCriticalNativeMethod_DO_NOT_USE_OR_YOU_WILL_BE_FIRED(...) makeCriticalNativeMethodN(__VA_ARGS__, 3, 2)(__VA_ARGS__)
|
||||
|
||||
}}
|
||||
|
||||
#include "Registration-inl.h"
|
||||
@@ -1,177 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include "References-forward.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
/// Generic std::enable_if helper
|
||||
template<bool B, typename T>
|
||||
using enable_if_t = typename std::enable_if<B, T>::type;
|
||||
|
||||
/// Generic std::is_convertible helper
|
||||
template<typename From, typename To>
|
||||
constexpr bool IsConvertible() {
|
||||
return std::is_convertible<From, To>::value;
|
||||
}
|
||||
|
||||
template<template<typename...> class TT, typename T>
|
||||
struct is_instantiation_of : std::false_type {};
|
||||
|
||||
template<template<typename...> class TT, typename... Ts>
|
||||
struct is_instantiation_of<TT, TT<Ts...>> : std::true_type {};
|
||||
|
||||
template<template<typename...> class TT, typename... Ts>
|
||||
constexpr bool IsInstantiationOf() {
|
||||
return is_instantiation_of<TT, Ts...>::value;
|
||||
}
|
||||
|
||||
/// Metafunction to determine whether a type is a JNI reference or not
|
||||
template<typename T>
|
||||
struct is_plain_jni_reference :
|
||||
std::integral_constant<bool,
|
||||
std::is_pointer<T>::value &&
|
||||
std::is_base_of<
|
||||
typename std::remove_pointer<jobject>::type,
|
||||
typename std::remove_pointer<T>::type>::value> {};
|
||||
|
||||
/// Helper to simplify use of is_plain_jni_reference
|
||||
template<typename T>
|
||||
constexpr bool IsPlainJniReference() {
|
||||
return is_plain_jni_reference<T>::value;
|
||||
}
|
||||
|
||||
/// Metafunction to determine whether a type is a primitive JNI type or not
|
||||
template<typename T>
|
||||
struct is_jni_primitive :
|
||||
std::integral_constant<bool,
|
||||
std::is_same<jboolean, T>::value ||
|
||||
std::is_same<jbyte, T>::value ||
|
||||
std::is_same<jchar, T>::value ||
|
||||
std::is_same<jshort, T>::value ||
|
||||
std::is_same<jint, T>::value ||
|
||||
std::is_same<jlong, T>::value ||
|
||||
std::is_same<jfloat, T>::value ||
|
||||
std::is_same<jdouble, T>::value> {};
|
||||
|
||||
/// Helper to simplify use of is_jni_primitive
|
||||
template<typename T>
|
||||
constexpr bool IsJniPrimitive() {
|
||||
return is_jni_primitive<T>::value;
|
||||
}
|
||||
|
||||
/// Metafunction to determine whether a series of types are all primitive JNI types.
|
||||
template<typename ...Ts>
|
||||
struct are_jni_primitives;
|
||||
|
||||
template<typename T, typename ...Ts>
|
||||
struct are_jni_primitives<T, Ts...> :
|
||||
std::integral_constant<bool,
|
||||
is_jni_primitive<T>::value && are_jni_primitives<Ts...>::value> {};
|
||||
|
||||
template<>
|
||||
struct are_jni_primitives<> : std::integral_constant<bool, true> {};
|
||||
|
||||
/// Helper to simplify use of are_jni_primitives
|
||||
template<typename ...Ts>
|
||||
constexpr bool AreJniPrimitives() {
|
||||
return are_jni_primitives<Ts...>::value;
|
||||
}
|
||||
|
||||
|
||||
/// Metafunction to determine whether a type is a JNI array of primitives or not
|
||||
template <typename T>
|
||||
struct is_jni_primitive_array :
|
||||
std::integral_constant<bool,
|
||||
std::is_same<jbooleanArray, T>::value ||
|
||||
std::is_same<jbyteArray, T>::value ||
|
||||
std::is_same<jcharArray, T>::value ||
|
||||
std::is_same<jshortArray, T>::value ||
|
||||
std::is_same<jintArray, T>::value ||
|
||||
std::is_same<jlongArray, T>::value ||
|
||||
std::is_same<jfloatArray, T>::value ||
|
||||
std::is_same<jdoubleArray, T>::value> {};
|
||||
|
||||
/// Helper to simplify use of is_jni_primitive_array
|
||||
template <typename T>
|
||||
constexpr bool IsJniPrimitiveArray() {
|
||||
return is_jni_primitive_array<T>::value;
|
||||
}
|
||||
|
||||
/// Metafunction to determine if a type is a scalar (primitive or reference) JNI type
|
||||
template<typename T>
|
||||
struct is_jni_scalar :
|
||||
std::integral_constant<bool,
|
||||
is_plain_jni_reference<T>::value ||
|
||||
is_jni_primitive<T>::value> {};
|
||||
|
||||
/// Helper to simplify use of is_jni_scalar
|
||||
template<typename T>
|
||||
constexpr bool IsJniScalar() {
|
||||
return is_jni_scalar<T>::value;
|
||||
}
|
||||
|
||||
// Metafunction to determine if a type is a JNI type
|
||||
template<typename T>
|
||||
struct is_jni_type :
|
||||
std::integral_constant<bool,
|
||||
is_jni_scalar<T>::value ||
|
||||
std::is_void<T>::value> {};
|
||||
|
||||
/// Helper to simplify use of is_jni_type
|
||||
template<typename T>
|
||||
constexpr bool IsJniType() {
|
||||
return is_jni_type<T>::value;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
struct is_non_weak_reference :
|
||||
std::integral_constant<bool,
|
||||
IsPlainJniReference<T>() ||
|
||||
IsInstantiationOf<basic_strong_ref, T>() ||
|
||||
IsInstantiationOf<alias_ref, T>()> {};
|
||||
|
||||
template<typename T>
|
||||
constexpr bool IsNonWeakReference() {
|
||||
return is_non_weak_reference<T>::value;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
struct is_any_reference :
|
||||
std::integral_constant<bool,
|
||||
IsPlainJniReference<T>() ||
|
||||
IsInstantiationOf<weak_ref, T>() ||
|
||||
IsInstantiationOf<basic_strong_ref, T>() ||
|
||||
IsInstantiationOf<alias_ref, T>()> {};
|
||||
|
||||
template<typename T>
|
||||
constexpr bool IsAnyReference() {
|
||||
return is_any_reference<T>::value;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
struct reference_traits {
|
||||
using plain_jni_reference_t = JniType<T>;
|
||||
static_assert(IsPlainJniReference<plain_jni_reference_t>(), "Need a plain JNI reference");
|
||||
};
|
||||
|
||||
template<template <typename...> class R, typename T, typename... A>
|
||||
struct reference_traits<R<T, A...>> {
|
||||
using plain_jni_reference_t = JniType<T>;
|
||||
static_assert(IsPlainJniReference<plain_jni_reference_t>(), "Need a plain JNI reference");
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
using plain_jni_reference_t = typename reference_traits<T>::plain_jni_reference_t;
|
||||
|
||||
} // namespace jni
|
||||
} // namespace facebook
|
||||
@@ -1,173 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <fb/visibility.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace lyra {
|
||||
|
||||
constexpr size_t kDefaultLimit = 64;
|
||||
|
||||
using InstructionPointer = const void*;
|
||||
|
||||
class FBEXPORT StackTraceElement {
|
||||
public:
|
||||
StackTraceElement(InstructionPointer absoluteProgramCounter,
|
||||
InstructionPointer libraryBase,
|
||||
InstructionPointer functionAddress, std::string libraryName,
|
||||
std::string functionName)
|
||||
: absoluteProgramCounter_{absoluteProgramCounter},
|
||||
libraryBase_{libraryBase},
|
||||
functionAddress_{functionAddress},
|
||||
libraryName_{std::move(libraryName)},
|
||||
functionName_{std::move(functionName)} {}
|
||||
|
||||
InstructionPointer libraryBase() const noexcept { return libraryBase_; }
|
||||
|
||||
InstructionPointer functionAddress() const noexcept {
|
||||
return functionAddress_;
|
||||
}
|
||||
|
||||
InstructionPointer absoluteProgramCounter() const noexcept {
|
||||
return absoluteProgramCounter_;
|
||||
}
|
||||
|
||||
const std::string& libraryName() const noexcept { return libraryName_; }
|
||||
|
||||
const std::string& functionName() const noexcept { return functionName_; }
|
||||
|
||||
/**
|
||||
* The offset of the program counter to the base of the library (i.e. the
|
||||
* address that addr2line takes as input>
|
||||
*/
|
||||
std::ptrdiff_t libraryOffset() const noexcept {
|
||||
auto absoluteLibrary = static_cast<const char*>(libraryBase_);
|
||||
auto absoluteabsoluteProgramCounter =
|
||||
static_cast<const char*>(absoluteProgramCounter_);
|
||||
return absoluteabsoluteProgramCounter - absoluteLibrary;
|
||||
}
|
||||
|
||||
/**
|
||||
* The offset within the current function
|
||||
*/
|
||||
int functionOffset() const noexcept {
|
||||
auto absoluteSymbol = static_cast<const char*>(functionAddress_);
|
||||
auto absoluteabsoluteProgramCounter =
|
||||
static_cast<const char*>(absoluteProgramCounter_);
|
||||
return absoluteabsoluteProgramCounter - absoluteSymbol;
|
||||
}
|
||||
|
||||
private:
|
||||
const InstructionPointer absoluteProgramCounter_;
|
||||
const InstructionPointer libraryBase_;
|
||||
const InstructionPointer functionAddress_;
|
||||
const std::string libraryName_;
|
||||
const std::string functionName_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Populate the vector with the current stack trace
|
||||
*
|
||||
* Note that this trace needs to be symbolicated to get the library offset even
|
||||
* if it is to be symbolicated off-line.
|
||||
*
|
||||
* Beware of a bug on some platforms, which makes the trace loop until the
|
||||
* buffer is full when it reaches a noexpr function. It seems to be fixed in
|
||||
* newer versions of gcc. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56846
|
||||
*
|
||||
* @param stackTrace The vector that will receive the stack trace. Before
|
||||
* filling the vector it will be cleared. The vector will never grow so the
|
||||
* number of frames captured is limited by the capacity of it.
|
||||
*
|
||||
* @param skip The number of frames to skip before capturing the trace
|
||||
*/
|
||||
FBEXPORT void getStackTrace(std::vector<InstructionPointer>& stackTrace,
|
||||
size_t skip = 0);
|
||||
|
||||
/**
|
||||
* Creates a vector and populates it with the current stack trace
|
||||
*
|
||||
* Note that this trace needs to be symbolicated to get the library offset even
|
||||
* if it is to be symbolicated off-line.
|
||||
*
|
||||
* Beware of a bug on some platforms, which makes the trace loop until the
|
||||
* buffer is full when it reaches a noexpr function. It seems to be fixed in
|
||||
* newer versions of gcc. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56846
|
||||
*
|
||||
* @param skip The number of frames to skip before capturing the trace
|
||||
*
|
||||
* @limit The maximum number of frames captured
|
||||
*/
|
||||
FBEXPORT inline std::vector<InstructionPointer> getStackTrace(
|
||||
size_t skip = 0,
|
||||
size_t limit = kDefaultLimit) {
|
||||
auto stackTrace = std::vector<InstructionPointer>{};
|
||||
stackTrace.reserve(limit);
|
||||
getStackTrace(stackTrace, skip + 1);
|
||||
return stackTrace;
|
||||
}
|
||||
|
||||
/**
|
||||
* Symbolicates a stack trace into a given vector
|
||||
*
|
||||
* @param symbols The vector to receive the output. The vector is cleared and
|
||||
* enough room to keep the frames are reserved.
|
||||
*
|
||||
* @param stackTrace The input stack trace
|
||||
*/
|
||||
FBEXPORT void getStackTraceSymbols(std::vector<StackTraceElement>& symbols,
|
||||
const std::vector<InstructionPointer>& trace);
|
||||
|
||||
/**
|
||||
* Symbolicates a stack trace into a new vector
|
||||
*
|
||||
* @param stackTrace The input stack trace
|
||||
*/
|
||||
FBEXPORT inline std::vector<StackTraceElement> getStackTraceSymbols(
|
||||
const std::vector<InstructionPointer>& trace) {
|
||||
auto symbols = std::vector<StackTraceElement>{};
|
||||
getStackTraceSymbols(symbols, trace);
|
||||
return symbols;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Captures and symbolicates a stack trace
|
||||
*
|
||||
* Beware of a bug on some platforms, which makes the trace loop until the
|
||||
* buffer is full when it reaches a noexpr function. It seems to be fixed in
|
||||
* newer versions of gcc. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56846
|
||||
*
|
||||
* @param skip The number of frames before capturing the trace
|
||||
*
|
||||
* @param limit The maximum number of frames captured
|
||||
*/
|
||||
FBEXPORT inline std::vector<StackTraceElement> getStackTraceSymbols(
|
||||
size_t skip = 0,
|
||||
size_t limit = kDefaultLimit) {
|
||||
return getStackTraceSymbols(getStackTrace(skip + 1, limit));
|
||||
}
|
||||
|
||||
/**
|
||||
* Formatting a stack trace element
|
||||
*/
|
||||
FBEXPORT std::ostream& operator<<(std::ostream& out, const StackTraceElement& elm);
|
||||
|
||||
/**
|
||||
* Formatting a stack trace
|
||||
*/
|
||||
FBEXPORT std::ostream& operator<<(std::ostream& out,
|
||||
const std::vector<StackTraceElement>& trace);
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <fb/fbjni.h>
|
||||
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#include <fb/Countable.h>
|
||||
#include <fb/RefPtr.h>
|
||||
#include <fb/visibility.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
FBEXPORT const RefPtr<Countable>& countableFromJava(JNIEnv* env, jobject obj);
|
||||
|
||||
template <typename T> RefPtr<T> extractRefPtr(JNIEnv* env, jobject obj) {
|
||||
return static_cast<RefPtr<T>>(countableFromJava(env, obj));
|
||||
}
|
||||
|
||||
template <typename T> RefPtr<T> extractPossiblyNullRefPtr(JNIEnv* env, jobject obj) {
|
||||
return obj ? extractRefPtr<T>(env, obj) : nullptr;
|
||||
}
|
||||
|
||||
FBEXPORT void setCountableForJava(JNIEnv* env, jobject obj, RefPtr<Countable>&& countable);
|
||||
|
||||
void CountableOnLoad(JNIEnv* env);
|
||||
|
||||
} }
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#include <fb/Environment.h>
|
||||
|
||||
namespace facebook { namespace jni {
|
||||
|
||||
template<typename T>
|
||||
class GlobalReference {
|
||||
static_assert(std::is_convertible<T, jobject>::value,
|
||||
"GlobalReference<T> instantiated with type that is not "
|
||||
"convertible to jobject");
|
||||
|
||||
public:
|
||||
explicit GlobalReference(T globalReference) :
|
||||
reference_(globalReference? Environment::current()->NewGlobalRef(globalReference) : nullptr) {
|
||||
}
|
||||
|
||||
~GlobalReference() {
|
||||
reset();
|
||||
}
|
||||
|
||||
GlobalReference() :
|
||||
reference_(nullptr) {
|
||||
}
|
||||
|
||||
// enable move constructor and assignment
|
||||
GlobalReference(GlobalReference&& rhs) :
|
||||
reference_(std::move(rhs.reference_)) {
|
||||
rhs.reference_ = nullptr;
|
||||
}
|
||||
|
||||
GlobalReference& operator=(GlobalReference&& rhs) {
|
||||
if (this != &rhs) {
|
||||
reset();
|
||||
reference_ = std::move(rhs.reference_);
|
||||
rhs.reference_ = nullptr;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
GlobalReference(const GlobalReference<T>& rhs) :
|
||||
reference_{} {
|
||||
reset(rhs.get());
|
||||
}
|
||||
|
||||
GlobalReference& operator=(const GlobalReference<T>& rhs) {
|
||||
if (this == &rhs) {
|
||||
return *this;
|
||||
}
|
||||
reset(rhs.get());
|
||||
return *this;
|
||||
}
|
||||
|
||||
explicit operator bool() const {
|
||||
return (reference_ != nullptr);
|
||||
}
|
||||
|
||||
T get() const {
|
||||
return reinterpret_cast<T>(reference_);
|
||||
}
|
||||
|
||||
void reset(T globalReference = nullptr) {
|
||||
if (reference_) {
|
||||
Environment::current()->DeleteGlobalRef(reference_);
|
||||
}
|
||||
if (globalReference) {
|
||||
reference_ = Environment::current()->NewGlobalRef(globalReference);
|
||||
} else {
|
||||
reference_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
jobject reference_;
|
||||
};
|
||||
|
||||
}}
|
||||
@@ -1,16 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <fb/visibility.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
void FBEXPORT installTerminateHandler();
|
||||
}};
|
||||
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#include <fb/Environment.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
template<class T>
|
||||
struct LocalReferenceDeleter {
|
||||
static_assert(std::is_convertible<T, jobject>::value,
|
||||
"LocalReferenceDeleter<T> instantiated with type that is not convertible to jobject");
|
||||
void operator()(T localReference) {
|
||||
if (localReference != nullptr) {
|
||||
Environment::current()->DeleteLocalRef(localReference);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<class T>
|
||||
using LocalReference =
|
||||
std::unique_ptr<typename std::remove_pointer<T>::type, LocalReferenceDeleter<T>>;
|
||||
|
||||
} }
|
||||
@@ -1,99 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#include <fb/visibility.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
namespace detail {
|
||||
|
||||
void utf8ToModifiedUTF8(const uint8_t* bytes, size_t len, uint8_t* modified, size_t modifiedLength);
|
||||
size_t modifiedLength(const std::string& str);
|
||||
size_t modifiedLength(const uint8_t* str, size_t* length);
|
||||
std::string modifiedUTF8ToUTF8(const uint8_t* modified, size_t len) noexcept;
|
||||
std::string utf16toUTF8(const uint16_t* utf16Bytes, size_t len) noexcept;
|
||||
|
||||
}
|
||||
|
||||
// JNI represents strings encoded with modified version of UTF-8. The difference between UTF-8 and
|
||||
// Modified UTF-8 is that the latter support only 1-byte, 2-byte, and 3-byte formats. Supplementary
|
||||
// character (4 bytes in unicode) needs to be represented in the form of surrogate pairs. To create
|
||||
// a Modified UTF-8 surrogate pair that Dalvik would understand we take 4-byte unicode character,
|
||||
// encode it with UTF-16 which gives us two 2 byte chars (surrogate pair) and then we encode each
|
||||
// pair as UTF-8. This result in 2 x 3 byte characters. To convert modified UTF-8 to standard
|
||||
// UTF-8, this mus tbe reversed.
|
||||
//
|
||||
// The second difference is that Modified UTF-8 is encoding NUL byte in 2-byte format.
|
||||
//
|
||||
// In order to avoid complex error handling, only a minimum of validity checking is done to avoid
|
||||
// crashing. If the input is invalid, the output may be invalid as well.
|
||||
//
|
||||
// Relevant links:
|
||||
// - http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/functions.html
|
||||
// - https://docs.oracle.com/javase/6/docs/api/java/io/DataInput.html#modified-utf-8
|
||||
|
||||
class FBEXPORT LocalString {
|
||||
public:
|
||||
// Assumes UTF8 encoding and make a required conversion to modified UTF-8 when the string
|
||||
// contains unicode supplementary characters.
|
||||
explicit LocalString(const std::string& str);
|
||||
explicit LocalString(const char* str);
|
||||
jstring string() const {
|
||||
return m_string;
|
||||
}
|
||||
~LocalString();
|
||||
private:
|
||||
jstring m_string;
|
||||
};
|
||||
|
||||
// JString to UTF16 extractor using RAII idiom
|
||||
class JStringUtf16Extractor {
|
||||
public:
|
||||
JStringUtf16Extractor(JNIEnv* env, jstring javaString)
|
||||
: env_(env)
|
||||
, javaString_(javaString)
|
||||
, length_(0)
|
||||
, utf16String_(nullptr) {
|
||||
if (env_ && javaString_) {
|
||||
length_ = env_->GetStringLength(javaString_);
|
||||
utf16String_ = env_->GetStringCritical(javaString_, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
~JStringUtf16Extractor() {
|
||||
if (utf16String_) {
|
||||
env_->ReleaseStringCritical(javaString_, utf16String_);
|
||||
}
|
||||
}
|
||||
|
||||
const jsize length() const {
|
||||
return length_;
|
||||
}
|
||||
|
||||
const jchar* chars() const {
|
||||
return utf16String_;
|
||||
}
|
||||
|
||||
private:
|
||||
JNIEnv* env_;
|
||||
jstring javaString_;
|
||||
jsize length_;
|
||||
const jchar* utf16String_;
|
||||
};
|
||||
|
||||
// The string from JNI is converted to standard UTF-8 if the string contains supplementary
|
||||
// characters.
|
||||
FBEXPORT std::string fromJString(JNIEnv* env, jstring str);
|
||||
|
||||
} }
|
||||
@@ -1,25 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <jni.h>
|
||||
#include <initializer_list>
|
||||
#include <fb/assert.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
static inline void registerNatives(JNIEnv* env, jclass cls, std::initializer_list<JNINativeMethod> methods) {
|
||||
auto result = env->RegisterNatives(cls, methods.begin(), methods.size());
|
||||
FBASSERT(result == 0);
|
||||
}
|
||||
|
||||
static inline void registerNatives(JNIEnv* env, const char* cls, std::initializer_list<JNINativeMethod> list) {
|
||||
registerNatives(env, env->FindClass(cls), list);
|
||||
}
|
||||
|
||||
} }
|
||||
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <jni.h>
|
||||
#include <fb/noncopyable.h>
|
||||
#include <fb/Countable.h>
|
||||
#include <fb/visibility.h>
|
||||
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
class FBEXPORT WeakReference : public Countable {
|
||||
public:
|
||||
typedef RefPtr<WeakReference> Ptr;
|
||||
WeakReference(jobject strongRef);
|
||||
~WeakReference();
|
||||
jweak weakRef() {
|
||||
return m_weakReference;
|
||||
}
|
||||
|
||||
private:
|
||||
jweak m_weakReference;
|
||||
};
|
||||
|
||||
// This class is intended to take a weak reference and turn it into a strong
|
||||
// local reference. Consequently, it should only be allocated on the stack.
|
||||
class FBEXPORT ResolvedWeakReference : public noncopyable {
|
||||
public:
|
||||
ResolvedWeakReference(jobject weakRef);
|
||||
ResolvedWeakReference(const RefPtr<WeakReference>& weakRef);
|
||||
~ResolvedWeakReference();
|
||||
|
||||
operator jobject () {
|
||||
return m_strongReference;
|
||||
}
|
||||
|
||||
explicit operator bool () {
|
||||
return m_strongReference != nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
jobject m_strongReference;
|
||||
};
|
||||
|
||||
} }
|
||||
|
||||
@@ -1,137 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#include <fb/visibility.h>
|
||||
|
||||
namespace facebook {
|
||||
|
||||
/**
|
||||
* Instructs the JNI environment to throw an exception.
|
||||
*
|
||||
* @param pEnv JNI environment
|
||||
* @param szClassName class name to throw
|
||||
* @param szFmt sprintf-style format string
|
||||
* @param ... sprintf-style args
|
||||
* @return 0 on success; a negative value on failure
|
||||
*/
|
||||
FBEXPORT jint throwException(JNIEnv* pEnv, const char* szClassName, const char* szFmt, va_list va_args);
|
||||
|
||||
/**
|
||||
* Instructs the JNI environment to throw a NoClassDefFoundError.
|
||||
*
|
||||
* @param pEnv JNI environment
|
||||
* @param szFmt sprintf-style format string
|
||||
* @param ... sprintf-style args
|
||||
* @return 0 on success; a negative value on failure
|
||||
*/
|
||||
FBEXPORT jint throwNoClassDefError(JNIEnv* pEnv, const char* szFmt, ...);
|
||||
|
||||
/**
|
||||
* Instructs the JNI environment to throw a RuntimeException.
|
||||
*
|
||||
* @param pEnv JNI environment
|
||||
* @param szFmt sprintf-style format string
|
||||
* @param ... sprintf-style args
|
||||
* @return 0 on success; a negative value on failure
|
||||
*/
|
||||
FBEXPORT jint throwRuntimeException(JNIEnv* pEnv, const char* szFmt, ...);
|
||||
|
||||
/**
|
||||
* Instructs the JNI environment to throw a IllegalArgumentException.
|
||||
*
|
||||
* @param pEnv JNI environment
|
||||
* @param szFmt sprintf-style format string
|
||||
* @param ... sprintf-style args
|
||||
* @return 0 on success; a negative value on failure
|
||||
*/
|
||||
FBEXPORT jint throwIllegalArgumentException(JNIEnv* pEnv, const char* szFmt, ...);
|
||||
|
||||
/**
|
||||
* Instructs the JNI environment to throw a IllegalStateException.
|
||||
*
|
||||
* @param pEnv JNI environment
|
||||
* @param szFmt sprintf-style format string
|
||||
* @param ... sprintf-style args
|
||||
* @return 0 on success; a negative value on failure
|
||||
*/
|
||||
FBEXPORT jint throwIllegalStateException(JNIEnv* pEnv, const char* szFmt, ...);
|
||||
|
||||
/**
|
||||
* Instructs the JNI environment to throw an IOException.
|
||||
*
|
||||
* @param pEnv JNI environment
|
||||
* @param szFmt sprintf-style format string
|
||||
* @param ... sprintf-style args
|
||||
* @return 0 on success; a negative value on failure
|
||||
*/
|
||||
FBEXPORT jint throwIOException(JNIEnv* pEnv, const char* szFmt, ...);
|
||||
|
||||
/**
|
||||
* Instructs the JNI environment to throw an AssertionError.
|
||||
*
|
||||
* @param pEnv JNI environment
|
||||
* @param szFmt sprintf-style format string
|
||||
* @param ... sprintf-style args
|
||||
* @return 0 on success; a negative value on failure
|
||||
*/
|
||||
FBEXPORT jint throwAssertionError(JNIEnv* pEnv, const char* szFmt, ...);
|
||||
|
||||
/**
|
||||
* Instructs the JNI environment to throw an OutOfMemoryError.
|
||||
*
|
||||
* @param pEnv JNI environment
|
||||
* @param szFmt sprintf-style format string
|
||||
* @param ... sprintf-style args
|
||||
* @return 0 on success; a negative value on failure
|
||||
*/
|
||||
FBEXPORT jint throwOutOfMemoryError(JNIEnv* pEnv, const char* szFmt, ...);
|
||||
|
||||
/**
|
||||
* Finds the specified class. If it's not found, instructs the JNI environment to throw an
|
||||
* exception.
|
||||
*
|
||||
* @param pEnv JNI environment
|
||||
* @param szClassName the classname to find in JNI format (e.g. "java/lang/String")
|
||||
* @return the class or NULL if not found (in which case a pending exception will be queued). This
|
||||
* returns a global reference (JNIEnv::NewGlobalRef).
|
||||
*/
|
||||
FBEXPORT jclass findClassOrThrow(JNIEnv *pEnv, const char* szClassName);
|
||||
|
||||
/**
|
||||
* Finds the specified field of the specified class. If it's not found, instructs the JNI
|
||||
* environment to throw an exception.
|
||||
*
|
||||
* @param pEnv JNI environment
|
||||
* @param clazz the class to lookup the field in
|
||||
* @param szFieldName the name of the field to find
|
||||
* @param szSig the signature of the field
|
||||
* @return the field or NULL if not found (in which case a pending exception will be queued)
|
||||
*/
|
||||
FBEXPORT jfieldID getFieldIdOrThrow(JNIEnv* pEnv, jclass clazz, const char* szFieldName, const char* szSig);
|
||||
|
||||
/**
|
||||
* Finds the specified method of the specified class. If it's not found, instructs the JNI
|
||||
* environment to throw an exception.
|
||||
*
|
||||
* @param pEnv JNI environment
|
||||
* @param clazz the class to lookup the method in
|
||||
* @param szMethodName the name of the method to find
|
||||
* @param szSig the signature of the method
|
||||
* @return the method or NULL if not found (in which case a pending exception will be queued)
|
||||
*/
|
||||
FBEXPORT jmethodID getMethodIdOrThrow(
|
||||
JNIEnv* pEnv,
|
||||
jclass clazz,
|
||||
const char* szMethodName,
|
||||
const char* szSig);
|
||||
|
||||
} // namespace facebook
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#include <fb/fbjni/ByteBuffer.h>
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include <fb/fbjni/References.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
namespace {
|
||||
local_ref<JByteBuffer> createEmpty() {
|
||||
static auto cls = JByteBuffer::javaClassStatic();
|
||||
static auto meth = cls->getStaticMethod<JByteBuffer::javaobject(int)>("allocateDirect");
|
||||
return meth(cls, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void JBuffer::rewind() const {
|
||||
static auto meth = javaClassStatic()->getMethod<alias_ref<JBuffer>()>("rewind");
|
||||
meth(self());
|
||||
}
|
||||
|
||||
local_ref<JByteBuffer> JByteBuffer::wrapBytes(uint8_t* data, size_t size) {
|
||||
// env->NewDirectByteBuffer requires that size is positive. Android's
|
||||
// dalvik returns an invalid result and Android's art aborts if size == 0.
|
||||
// Workaround this by using a slow path through Java in that case.
|
||||
if (!size) {
|
||||
return createEmpty();
|
||||
}
|
||||
auto res = adopt_local(static_cast<javaobject>(Environment::current()->NewDirectByteBuffer(data, size)));
|
||||
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
|
||||
if (!res) {
|
||||
throw std::runtime_error("Direct byte buffers are unsupported.");
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
uint8_t* JByteBuffer::getDirectBytes() const {
|
||||
if (!self()) {
|
||||
throwNewJavaException("java/lang/NullPointerException", "java.lang.NullPointerException");
|
||||
}
|
||||
void* bytes = Environment::current()->GetDirectBufferAddress(self());
|
||||
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
|
||||
if (!bytes) {
|
||||
throw std::runtime_error(
|
||||
isDirect() ?
|
||||
"Attempt to get direct bytes of non-direct byte buffer." :
|
||||
"Error getting direct bytes of byte buffer.");
|
||||
}
|
||||
return static_cast<uint8_t*>(bytes);
|
||||
}
|
||||
|
||||
size_t JByteBuffer::getDirectSize() const {
|
||||
if (!self()) {
|
||||
throwNewJavaException("java/lang/NullPointerException", "java.lang.NullPointerException");
|
||||
}
|
||||
int size = Environment::current()->GetDirectBufferCapacity(self());
|
||||
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
|
||||
if (size < 0) {
|
||||
throw std::runtime_error(
|
||||
isDirect() ?
|
||||
"Attempt to get direct size of non-direct byte buffer." :
|
||||
"Error getting direct size of byte buffer.");
|
||||
}
|
||||
return static_cast<size_t>(size);
|
||||
}
|
||||
|
||||
bool JByteBuffer::isDirect() const {
|
||||
static auto meth = javaClassStatic()->getMethod<jboolean()>("isDirect");
|
||||
return meth(self());
|
||||
}
|
||||
|
||||
}}
|
||||
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#include <cstdint>
|
||||
#include <jni/Countable.h>
|
||||
#include <fb/Environment.h>
|
||||
#include <jni/Registration.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
static jfieldID gCountableNativePtr;
|
||||
|
||||
static RefPtr<Countable>* rawCountableFromJava(JNIEnv* env, jobject obj) {
|
||||
FBASSERT(obj);
|
||||
return reinterpret_cast<RefPtr<Countable>*>(env->GetLongField(obj, gCountableNativePtr));
|
||||
}
|
||||
|
||||
const RefPtr<Countable>& countableFromJava(JNIEnv* env, jobject obj) {
|
||||
FBASSERT(obj);
|
||||
return *rawCountableFromJava(env, obj);
|
||||
}
|
||||
|
||||
void setCountableForJava(JNIEnv* env, jobject obj, RefPtr<Countable>&& countable) {
|
||||
int oldValue = env->GetLongField(obj, gCountableNativePtr);
|
||||
FBASSERTMSGF(oldValue == 0, "Cannot reinitialize object; expected nullptr, got %x", oldValue);
|
||||
|
||||
FBASSERT(countable);
|
||||
uintptr_t fieldValue = (uintptr_t) new RefPtr<Countable>(std::move(countable));
|
||||
env->SetLongField(obj, gCountableNativePtr, fieldValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* NB: THREAD SAFETY (this comment also exists at Countable.java)
|
||||
*
|
||||
* This method deletes the corresponding native object on whatever thread the method is called
|
||||
* on. In the common case when this is called by Countable#finalize(), this will be called on the
|
||||
* system finalizer thread. If you manually call dispose on the Java object, the native object
|
||||
* will be deleted synchronously on that thread.
|
||||
*/
|
||||
void dispose(JNIEnv* env, jobject obj) {
|
||||
// Grab the pointer
|
||||
RefPtr<Countable>* countable = rawCountableFromJava(env, obj);
|
||||
if (!countable) {
|
||||
// That was easy.
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear out the old value to avoid double-frees
|
||||
env->SetLongField(obj, gCountableNativePtr, 0);
|
||||
|
||||
delete countable;
|
||||
}
|
||||
|
||||
void CountableOnLoad(JNIEnv* env) {
|
||||
jclass countable = env->FindClass("com/facebook/jni/Countable");
|
||||
gCountableNativePtr = env->GetFieldID(countable, "mInstance", "J");
|
||||
registerNatives(env, countable, {
|
||||
{ "dispose", "()V", (void*) dispose },
|
||||
});
|
||||
}
|
||||
|
||||
} }
|
||||
@@ -1,196 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#include <fb/log.h>
|
||||
#include <fb/ThreadLocal.h>
|
||||
#include <fb/Environment.h>
|
||||
#include <fb/fbjni/CoreClasses.h>
|
||||
#include <fb/fbjni/NativeRunnable.h>
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
namespace {
|
||||
|
||||
ThreadLocal<ThreadScope>& scopeStorage() {
|
||||
// We don't want the ThreadLocal to delete the ThreadScopes.
|
||||
static ThreadLocal<ThreadScope> scope([] (void*) {});
|
||||
return scope;
|
||||
}
|
||||
|
||||
ThreadScope* currentScope() {
|
||||
return scopeStorage().get();
|
||||
}
|
||||
|
||||
JavaVM* g_vm = nullptr;
|
||||
|
||||
struct EnvironmentInitializer {
|
||||
EnvironmentInitializer(JavaVM* vm) {
|
||||
FBASSERT(!g_vm);
|
||||
FBASSERT(vm);
|
||||
g_vm = vm;
|
||||
}
|
||||
};
|
||||
|
||||
int getEnv(JNIEnv** env) {
|
||||
FBASSERT(g_vm);
|
||||
// g_vm->GetEnv() might not clear the env* in failure cases.
|
||||
*env = nullptr;
|
||||
return g_vm->GetEnv((void**)env, JNI_VERSION_1_6);
|
||||
}
|
||||
|
||||
JNIEnv* attachCurrentThread() {
|
||||
JavaVMAttachArgs args{JNI_VERSION_1_6, nullptr, nullptr};
|
||||
JNIEnv* env = nullptr;
|
||||
auto result = g_vm->AttachCurrentThread(&env, &args);
|
||||
FBASSERT(result == JNI_OK);
|
||||
return env;
|
||||
}
|
||||
}
|
||||
|
||||
/* static */
|
||||
void Environment::initialize(JavaVM* vm) {
|
||||
static EnvironmentInitializer init(vm);
|
||||
}
|
||||
|
||||
/* static */
|
||||
JNIEnv* Environment::current() {
|
||||
auto scope = currentScope();
|
||||
if (scope && scope->env_) {
|
||||
return scope->env_;
|
||||
}
|
||||
|
||||
JNIEnv* env;
|
||||
if (getEnv(&env) != JNI_OK) {
|
||||
// If there's a ThreadScope in the stack, we should be attached and able to
|
||||
// retrieve a JNIEnv*.
|
||||
FBASSERT(!scope);
|
||||
|
||||
// TODO(cjhopman): this should probably be a hard failure, too.
|
||||
FBLOGE("Unable to retrieve jni environment. Is the thread attached?");
|
||||
}
|
||||
return env;
|
||||
}
|
||||
|
||||
/* static */
|
||||
void Environment::detachCurrentThread() {
|
||||
FBASSERT(g_vm);
|
||||
// The thread shouldn't be detached while a ThreadScope is in the stack.
|
||||
FBASSERT(!currentScope());
|
||||
g_vm->DetachCurrentThread();
|
||||
}
|
||||
|
||||
/* static */
|
||||
JNIEnv* Environment::ensureCurrentThreadIsAttached() {
|
||||
auto scope = currentScope();
|
||||
if (scope && scope->env_) {
|
||||
return scope->env_;
|
||||
}
|
||||
|
||||
JNIEnv* env;
|
||||
// We should be able to just get the JNIEnv* by just calling
|
||||
// AttachCurrentThread, but the spec is unclear (and using getEnv is probably
|
||||
// generally more reliable).
|
||||
auto result = getEnv(&env);
|
||||
// We don't know how to deal with anything other than JNI_OK or JNI_DETACHED.
|
||||
FBASSERT(result == JNI_OK || result == JNI_EDETACHED);
|
||||
if (result == JNI_EDETACHED) {
|
||||
// The thread should not be detached while a ThreadScope is in the stack.
|
||||
FBASSERT(!scope);
|
||||
env = attachCurrentThread();
|
||||
}
|
||||
FBASSERT(env);
|
||||
return env;
|
||||
}
|
||||
|
||||
ThreadScope::ThreadScope() : ThreadScope(nullptr, internal::CacheEnvTag{}) {}
|
||||
|
||||
ThreadScope::ThreadScope(JNIEnv* env, internal::CacheEnvTag)
|
||||
: previous_(nullptr), env_(nullptr), attachedWithThisScope_(false) {
|
||||
auto& storage = scopeStorage();
|
||||
previous_ = storage.get();
|
||||
storage.reset(this);
|
||||
|
||||
if (previous_ && previous_->env_) {
|
||||
FBASSERT(!env || env == previous_->env_);
|
||||
env = previous_->env_;
|
||||
}
|
||||
|
||||
env_ = env;
|
||||
if (env_) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the thread is attached by someone else.
|
||||
auto result = getEnv(&env);
|
||||
if (result == JNI_OK) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We don't know how to deal with anything other than JNI_OK or JNI_DETACHED.
|
||||
FBASSERT(result == JNI_EDETACHED);
|
||||
|
||||
// If there's already a ThreadScope on the stack, then the thread should be attached.
|
||||
FBASSERT(!previous_);
|
||||
attachCurrentThread();
|
||||
attachedWithThisScope_ = true;
|
||||
}
|
||||
|
||||
ThreadScope::~ThreadScope() {
|
||||
auto& storage = scopeStorage();
|
||||
// ThreadScopes should be destroyed in the reverse order they are created
|
||||
// (that is, just put them on the stack).
|
||||
FBASSERT(this == storage.get());
|
||||
storage.reset(previous_);
|
||||
if (attachedWithThisScope_) {
|
||||
Environment::detachCurrentThread();
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
struct JThreadScopeSupport : JavaClass<JThreadScopeSupport> {
|
||||
static auto constexpr kJavaDescriptor = "Lcom/facebook/jni/ThreadScopeSupport;";
|
||||
|
||||
// These reinterpret_casts are a totally dangerous pattern. Don't use them. Use HybridData instead.
|
||||
static void runStdFunction(std::function<void()>&& func) {
|
||||
static auto method = javaClassStatic()->getStaticMethod<void(jlong)>("runStdFunction");
|
||||
method(javaClassStatic(), reinterpret_cast<jlong>(&func));
|
||||
}
|
||||
|
||||
static void runStdFunctionImpl(alias_ref<JClass>, jlong ptr) {
|
||||
(*reinterpret_cast<std::function<void()>*>(ptr))();
|
||||
}
|
||||
|
||||
static void OnLoad() {
|
||||
// We need the javaClassStatic so that the class lookup is cached and that
|
||||
// runStdFunction can be called from a ThreadScope-attached thread.
|
||||
javaClassStatic()->registerNatives({
|
||||
makeNativeMethod("runStdFunctionImpl", runStdFunctionImpl),
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/* static */
|
||||
void ThreadScope::OnLoad() {
|
||||
// These classes are required for ScopeWithClassLoader. Ensure they are looked up when loading.
|
||||
JThreadScopeSupport::OnLoad();
|
||||
}
|
||||
|
||||
/* static */
|
||||
void ThreadScope::WithClassLoader(std::function<void()>&& runnable) {
|
||||
// TODO(cjhopman): If the classloader is already available in this scope, we
|
||||
// shouldn't have to jump through java. It should be enough to check if the
|
||||
// attach state env* is set.
|
||||
ThreadScope ts;
|
||||
JThreadScopeSupport::runStdFunction(std::move(runnable));
|
||||
}
|
||||
|
||||
} }
|
||||
|
||||
@@ -1,394 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#include <fb/fbjni/CoreClasses.h>
|
||||
|
||||
#include <fb/assert.h>
|
||||
#include <fb/log.h>
|
||||
|
||||
#ifdef USE_LYRA
|
||||
#include <fb/lyra.h>
|
||||
#include <fb/lyra_exceptions.h>
|
||||
#endif
|
||||
|
||||
#include <alloca.h>
|
||||
#include <cstdlib>
|
||||
#include <ios>
|
||||
#include <stdexcept>
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
#include <system_error>
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
namespace {
|
||||
class JRuntimeException : public JavaClass<JRuntimeException, JThrowable> {
|
||||
public:
|
||||
static auto constexpr kJavaDescriptor = "Ljava/lang/RuntimeException;";
|
||||
|
||||
static local_ref<JRuntimeException> create(const char* str) {
|
||||
return newInstance(make_jstring(str));
|
||||
}
|
||||
|
||||
static local_ref<JRuntimeException> create() {
|
||||
return newInstance();
|
||||
}
|
||||
};
|
||||
|
||||
class JIOException : public JavaClass<JIOException, JThrowable> {
|
||||
public:
|
||||
static auto constexpr kJavaDescriptor = "Ljava/io/IOException;";
|
||||
|
||||
static local_ref<JIOException> create(const char* str) {
|
||||
return newInstance(make_jstring(str));
|
||||
}
|
||||
};
|
||||
|
||||
class JOutOfMemoryError : public JavaClass<JOutOfMemoryError, JThrowable> {
|
||||
public:
|
||||
static auto constexpr kJavaDescriptor = "Ljava/lang/OutOfMemoryError;";
|
||||
|
||||
static local_ref<JOutOfMemoryError> create(const char* str) {
|
||||
return newInstance(make_jstring(str));
|
||||
}
|
||||
};
|
||||
|
||||
class JArrayIndexOutOfBoundsException : public JavaClass<JArrayIndexOutOfBoundsException, JThrowable> {
|
||||
public:
|
||||
static auto constexpr kJavaDescriptor = "Ljava/lang/ArrayIndexOutOfBoundsException;";
|
||||
|
||||
static local_ref<JArrayIndexOutOfBoundsException> create(const char* str) {
|
||||
return newInstance(make_jstring(str));
|
||||
}
|
||||
};
|
||||
|
||||
class JUnknownCppException : public JavaClass<JUnknownCppException, JThrowable> {
|
||||
public:
|
||||
static auto constexpr kJavaDescriptor = "Lcom/facebook/jni/UnknownCppException;";
|
||||
|
||||
static local_ref<JUnknownCppException> create() {
|
||||
return newInstance();
|
||||
}
|
||||
|
||||
static local_ref<JUnknownCppException> create(const char* str) {
|
||||
return newInstance(make_jstring(str));
|
||||
}
|
||||
};
|
||||
|
||||
class JCppSystemErrorException : public JavaClass<JCppSystemErrorException, JThrowable> {
|
||||
public:
|
||||
static auto constexpr kJavaDescriptor = "Lcom/facebook/jni/CppSystemErrorException;";
|
||||
|
||||
static local_ref<JCppSystemErrorException> create(const std::system_error& e) {
|
||||
return newInstance(make_jstring(e.what()), e.code().value());
|
||||
}
|
||||
};
|
||||
|
||||
// Exception throwing & translating functions //////////////////////////////////////////////////////
|
||||
|
||||
// Functions that throw Java exceptions
|
||||
|
||||
void setJavaExceptionAndAbortOnFailure(alias_ref<JThrowable> throwable) {
|
||||
auto env = Environment::current();
|
||||
if (throwable) {
|
||||
env->Throw(throwable.get());
|
||||
}
|
||||
if (env->ExceptionCheck() != JNI_TRUE) {
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Functions that throw C++ exceptions
|
||||
|
||||
// TODO(T6618159) Inject the c++ stack into the exception's stack trace. One
|
||||
// issue: when a java exception is created, it captures the full java stack
|
||||
// across jni boundaries. lyra will only capture the c++ stack to the jni
|
||||
// boundary. So, as we pass the java exception up to c++, we need to capture
|
||||
// the c++ stack and then insert it into the correct place in the java stack
|
||||
// trace. Then, as the exception propagates across the boundaries, we will
|
||||
// slowly fill in the c++ parts of the trace.
|
||||
void throwPendingJniExceptionAsCppException() {
|
||||
JNIEnv* env = Environment::current();
|
||||
if (env->ExceptionCheck() == JNI_FALSE) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto throwable = env->ExceptionOccurred();
|
||||
if (!throwable) {
|
||||
throw std::runtime_error("Unable to get pending JNI exception.");
|
||||
}
|
||||
env->ExceptionClear();
|
||||
|
||||
throw JniException(adopt_local(throwable));
|
||||
}
|
||||
|
||||
void throwCppExceptionIf(bool condition) {
|
||||
if (!condition) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto env = Environment::current();
|
||||
if (env->ExceptionCheck() == JNI_TRUE) {
|
||||
throwPendingJniExceptionAsCppException();
|
||||
return;
|
||||
}
|
||||
|
||||
throw JniException();
|
||||
}
|
||||
|
||||
void throwNewJavaException(jthrowable throwable) {
|
||||
throw JniException(wrap_alias(throwable));
|
||||
}
|
||||
|
||||
void throwNewJavaException(const char* throwableName, const char* msg) {
|
||||
// If anything of the fbjni calls fail, an exception of a suitable
|
||||
// form will be thrown, which is what we want.
|
||||
auto throwableClass = findClassLocal(throwableName);
|
||||
auto throwable = throwableClass->newObject(
|
||||
throwableClass->getConstructor<jthrowable(jstring)>(),
|
||||
make_jstring(msg).release());
|
||||
throwNewJavaException(throwable.get());
|
||||
}
|
||||
|
||||
// jthrowable //////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
local_ref<JThrowable> JThrowable::initCause(alias_ref<JThrowable> cause) {
|
||||
static auto meth = javaClassStatic()->getMethod<javaobject(alias_ref<javaobject>)>("initCause");
|
||||
return meth(self(), cause);
|
||||
}
|
||||
|
||||
auto JThrowable::getStackTrace() -> local_ref<JStackTrace> {
|
||||
static auto meth = javaClassStatic()->getMethod<JStackTrace::javaobject()>("getStackTrace");
|
||||
return meth(self());
|
||||
}
|
||||
|
||||
void JThrowable::setStackTrace(alias_ref<JStackTrace> stack) {
|
||||
static auto meth = javaClassStatic()->getMethod<void(alias_ref<JStackTrace>)>("setStackTrace");
|
||||
return meth(self(), stack);
|
||||
}
|
||||
|
||||
auto JStackTraceElement::create(
|
||||
const std::string& declaringClass, const std::string& methodName, const std::string& file, int line)
|
||||
-> local_ref<javaobject> {
|
||||
return newInstance(declaringClass, methodName, file, line);
|
||||
}
|
||||
|
||||
std::string JStackTraceElement::getClassName() const {
|
||||
static auto meth = javaClassStatic()->getMethod<local_ref<JString>()>("getClassName");
|
||||
return meth(self())->toStdString();
|
||||
}
|
||||
|
||||
std::string JStackTraceElement::getMethodName() const {
|
||||
static auto meth = javaClassStatic()->getMethod<local_ref<JString>()>("getMethodName");
|
||||
return meth(self())->toStdString();
|
||||
}
|
||||
|
||||
std::string JStackTraceElement::getFileName() const {
|
||||
static auto meth = javaClassStatic()->getMethod<local_ref<JString>()>("getFileName");
|
||||
return meth(self())->toStdString();
|
||||
}
|
||||
|
||||
int JStackTraceElement::getLineNumber() const {
|
||||
static auto meth = javaClassStatic()->getMethod<jint()>("getLineNumber");
|
||||
return meth(self());
|
||||
}
|
||||
|
||||
// Translate C++ to Java Exception
|
||||
|
||||
namespace {
|
||||
|
||||
// For each exception in the chain of the exception_ptr argument, func
|
||||
// will be called with that exception (in reverse order, i.e. innermost first).
|
||||
#ifndef FBJNI_NO_EXCEPTION_PTR
|
||||
void denest(const std::function<void(std::exception_ptr)>& func, std::exception_ptr ptr) {
|
||||
FBASSERT(ptr);
|
||||
try {
|
||||
std::rethrow_exception(ptr);
|
||||
} catch (const std::nested_exception& e) {
|
||||
denest(func, e.nested_ptr());
|
||||
} catch (...) {
|
||||
// ignored.
|
||||
}
|
||||
func(ptr);
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
#ifdef USE_LYRA
|
||||
local_ref<JStackTraceElement> createJStackTraceElement(const lyra::StackTraceElement& cpp) {
|
||||
return JStackTraceElement::create(
|
||||
"|lyra|{" + cpp.libraryName() + "}", cpp.functionName(), cpp.buildId(), cpp.libraryOffset());
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef FBJNI_NO_EXCEPTION_PTR
|
||||
void addCppStacktraceToJavaException(alias_ref<JThrowable> java, std::exception_ptr cpp) {
|
||||
#ifdef USE_LYRA
|
||||
auto cppStack = lyra::getStackTraceSymbols(
|
||||
(cpp == nullptr) ?
|
||||
lyra::getStackTrace()
|
||||
: lyra::getExceptionTrace(cpp));
|
||||
|
||||
auto javaStack = java->getStackTrace();
|
||||
auto newStack = JThrowable::JStackTrace::newArray(javaStack->size() + cppStack.size());
|
||||
size_t i = 0;
|
||||
for (size_t j = 0; j < cppStack.size(); j++, i++) {
|
||||
(*newStack)[i] = createJStackTraceElement(cppStack[j]);
|
||||
}
|
||||
for (size_t j = 0; j < javaStack->size(); j++, i++) {
|
||||
(*newStack)[i] = (*javaStack)[j];
|
||||
}
|
||||
java->setStackTrace(newStack);
|
||||
#endif
|
||||
}
|
||||
|
||||
local_ref<JThrowable> convertCppExceptionToJavaException(std::exception_ptr ptr) {
|
||||
FBASSERT(ptr);
|
||||
local_ref<JThrowable> current;
|
||||
bool addCppStack = true;
|
||||
try {
|
||||
std::rethrow_exception(ptr);
|
||||
addCppStack = false;
|
||||
} catch (const JniException& ex) {
|
||||
current = ex.getThrowable();
|
||||
} catch (const std::ios_base::failure& ex) {
|
||||
current = JIOException::create(ex.what());
|
||||
} catch (const std::bad_alloc& ex) {
|
||||
current = JOutOfMemoryError::create(ex.what());
|
||||
} catch (const std::out_of_range& ex) {
|
||||
current = JArrayIndexOutOfBoundsException::create(ex.what());
|
||||
} catch (const std::system_error& ex) {
|
||||
current = JCppSystemErrorException::create(ex);
|
||||
} catch (const std::runtime_error& ex) {
|
||||
current = JRuntimeException::create(ex.what());
|
||||
} catch (const std::exception& ex) {
|
||||
current = JCppException::create(ex.what());
|
||||
} catch (const char* msg) {
|
||||
current = JUnknownCppException::create(msg);
|
||||
} catch (...) {
|
||||
current = JUnknownCppException::create();
|
||||
}
|
||||
|
||||
if (addCppStack) {
|
||||
addCppStacktraceToJavaException(current, ptr);
|
||||
}
|
||||
return current;
|
||||
}
|
||||
#endif
|
||||
|
||||
local_ref<JThrowable> getJavaExceptionForCppBackTrace() {
|
||||
return getJavaExceptionForCppBackTrace(nullptr);
|
||||
}
|
||||
|
||||
local_ref<JThrowable> getJavaExceptionForCppBackTrace(const char* msg) {
|
||||
local_ref<JThrowable> current =
|
||||
msg ? JUnknownCppException::create(msg) : JUnknownCppException::create();
|
||||
#ifndef FBJNI_NO_EXCEPTION_PTR
|
||||
addCppStacktraceToJavaException(current, nullptr);
|
||||
#endif
|
||||
return current;
|
||||
}
|
||||
|
||||
|
||||
#ifndef FBJNI_NO_EXCEPTION_PTR
|
||||
local_ref<JThrowable> getJavaExceptionForCppException(std::exception_ptr ptr) {
|
||||
FBASSERT(ptr);
|
||||
local_ref<JThrowable> previous;
|
||||
auto func = [&previous] (std::exception_ptr ptr) {
|
||||
auto current = convertCppExceptionToJavaException(ptr);
|
||||
if (previous) {
|
||||
current->initCause(previous);
|
||||
}
|
||||
previous = current;
|
||||
};
|
||||
denest(func, ptr);
|
||||
return previous;
|
||||
}
|
||||
#endif
|
||||
|
||||
void translatePendingCppExceptionToJavaException() {
|
||||
try {
|
||||
#ifndef FBJNI_NO_EXCEPTION_PTR
|
||||
auto exc = getJavaExceptionForCppException(std::current_exception());
|
||||
#else
|
||||
auto exc = JUnknownCppException::create();
|
||||
#endif
|
||||
setJavaExceptionAndAbortOnFailure(exc);
|
||||
} catch (...) {
|
||||
#ifdef USE_LYRA
|
||||
FBLOGE("Unexpected error in translatePendingCppExceptionToJavaException(): %s",
|
||||
lyra::toString(std::current_exception()).c_str());
|
||||
#endif
|
||||
std::terminate();
|
||||
}
|
||||
}
|
||||
|
||||
// JniException ////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
const std::string JniException::kExceptionMessageFailure_ = "Unable to get exception message.";
|
||||
|
||||
JniException::JniException() : JniException(JRuntimeException::create()) { }
|
||||
|
||||
JniException::JniException(alias_ref<jthrowable> throwable) : isMessageExtracted_(false) {
|
||||
throwable_ = make_global(throwable);
|
||||
}
|
||||
|
||||
JniException::JniException(JniException &&rhs)
|
||||
: throwable_(std::move(rhs.throwable_)),
|
||||
what_(std::move(rhs.what_)),
|
||||
isMessageExtracted_(rhs.isMessageExtracted_) {
|
||||
}
|
||||
|
||||
JniException::JniException(const JniException &rhs)
|
||||
: what_(rhs.what_), isMessageExtracted_(rhs.isMessageExtracted_) {
|
||||
throwable_ = make_global(rhs.throwable_);
|
||||
}
|
||||
|
||||
JniException::~JniException() {
|
||||
try {
|
||||
ThreadScope ts;
|
||||
throwable_.reset();
|
||||
} catch (...) {
|
||||
FBLOGE("Exception in ~JniException()");
|
||||
std::terminate();
|
||||
}
|
||||
}
|
||||
|
||||
local_ref<JThrowable> JniException::getThrowable() const noexcept {
|
||||
return make_local(throwable_);
|
||||
}
|
||||
|
||||
// TODO 6900503: consider making this thread-safe.
|
||||
void JniException::populateWhat() const noexcept {
|
||||
try {
|
||||
ThreadScope ts;
|
||||
what_ = throwable_->toString();
|
||||
isMessageExtracted_ = true;
|
||||
} catch(...) {
|
||||
what_ = kExceptionMessageFailure_;
|
||||
}
|
||||
}
|
||||
|
||||
const char* JniException::what() const noexcept {
|
||||
if (!isMessageExtracted_) {
|
||||
populateWhat();
|
||||
}
|
||||
return what_.c_str();
|
||||
}
|
||||
|
||||
void JniException::setJavaException() const noexcept {
|
||||
setJavaExceptionAndAbortOnFailure(throwable_);
|
||||
}
|
||||
|
||||
}}
|
||||
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#include "fb/fbjni.h"
|
||||
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
namespace detail {
|
||||
|
||||
local_ref<HybridData> HybridData::create() {
|
||||
return newInstance();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace {
|
||||
void deleteNative(alias_ref<jclass>, jlong ptr) {
|
||||
delete reinterpret_cast<detail::BaseHybridClass*>(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
void HybridDataOnLoad() {
|
||||
registerNatives("com/facebook/jni/HybridData$Destructor", {
|
||||
makeNativeMethod("deleteNative", deleteNative),
|
||||
});
|
||||
}
|
||||
|
||||
}}
|
||||
@@ -1,311 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#include <jni/LocalString.h>
|
||||
#include <fb/Environment.h>
|
||||
#include <fb/assert.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
namespace {
|
||||
|
||||
const uint16_t kUtf8OneByteBoundary = 0x80;
|
||||
const uint16_t kUtf8TwoBytesBoundary = 0x800;
|
||||
const uint16_t kUtf16HighSubLowBoundary = 0xD800;
|
||||
const uint16_t kUtf16HighSubHighBoundary = 0xDC00;
|
||||
const uint16_t kUtf16LowSubHighBoundary = 0xE000;
|
||||
|
||||
inline void encode3ByteUTF8(char32_t code, uint8_t* out) {
|
||||
FBASSERTMSGF((code & 0xffff0000) == 0, "3 byte utf-8 encodings only valid for up to 16 bits");
|
||||
|
||||
out[0] = 0xE0 | (code >> 12);
|
||||
out[1] = 0x80 | ((code >> 6) & 0x3F);
|
||||
out[2] = 0x80 | (code & 0x3F);
|
||||
}
|
||||
|
||||
inline char32_t decode3ByteUTF8(const uint8_t* in) {
|
||||
return (((in[0] & 0x0f) << 12) |
|
||||
((in[1] & 0x3f) << 6) |
|
||||
( in[2] & 0x3f));
|
||||
}
|
||||
|
||||
inline void encode4ByteUTF8(char32_t code, std::string& out, size_t offset) {
|
||||
FBASSERTMSGF((code & 0xfff80000) == 0, "4 byte utf-8 encodings only valid for up to 21 bits");
|
||||
|
||||
out[offset] = (char) (0xF0 | (code >> 18));
|
||||
out[offset + 1] = (char) (0x80 | ((code >> 12) & 0x3F));
|
||||
out[offset + 2] = (char) (0x80 | ((code >> 6) & 0x3F));
|
||||
out[offset + 3] = (char) (0x80 | (code & 0x3F));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline bool isFourByteUTF8Encoding(const T* utf8) {
|
||||
return ((*utf8 & 0xF8) == 0xF0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
|
||||
size_t modifiedLength(const std::string& str) {
|
||||
// Scan for supplementary characters
|
||||
size_t j = 0;
|
||||
for (size_t i = 0; i < str.size(); ) {
|
||||
if (str[i] == 0) {
|
||||
i += 1;
|
||||
j += 2;
|
||||
} else if (i + 4 > str.size() ||
|
||||
!isFourByteUTF8Encoding(&(str[i]))) {
|
||||
// See the code in utf8ToModifiedUTF8 for what's happening here.
|
||||
i += 1;
|
||||
j += 1;
|
||||
} else {
|
||||
i += 4;
|
||||
j += 6;
|
||||
}
|
||||
}
|
||||
|
||||
return j;
|
||||
}
|
||||
|
||||
// returns modified utf8 length; *length is set to strlen(str)
|
||||
size_t modifiedLength(const uint8_t* str, size_t* length) {
|
||||
// NUL-terminated: Scan for length and supplementary characters
|
||||
size_t i = 0;
|
||||
size_t j = 0;
|
||||
if (str != nullptr) {
|
||||
while (str[i] != 0) {
|
||||
if (str[i + 1] == 0 ||
|
||||
str[i + 2] == 0 ||
|
||||
str[i + 3] == 0 ||
|
||||
!isFourByteUTF8Encoding(&(str[i]))) {
|
||||
i += 1;
|
||||
j += 1;
|
||||
} else {
|
||||
i += 4;
|
||||
j += 6;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*length = i;
|
||||
return j;
|
||||
}
|
||||
|
||||
void utf8ToModifiedUTF8(const uint8_t* utf8, size_t len, uint8_t* modified, size_t modifiedBufLen)
|
||||
{
|
||||
size_t j = 0;
|
||||
for (size_t i = 0; i < len; ) {
|
||||
FBASSERTMSGF(j < modifiedBufLen, "output buffer is too short");
|
||||
if (utf8[i] == 0) {
|
||||
FBASSERTMSGF(j + 1 < modifiedBufLen, "output buffer is too short");
|
||||
modified[j] = 0xc0;
|
||||
modified[j + 1] = 0x80;
|
||||
i += 1;
|
||||
j += 2;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i + 4 > len ||
|
||||
!isFourByteUTF8Encoding(utf8 + i)) {
|
||||
// If the input is too short for this to be a four-byte
|
||||
// encoding, or it isn't one for real, just copy it on through.
|
||||
modified[j] = utf8[i];
|
||||
i++;
|
||||
j++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Convert 4 bytes of input to 2 * 3 bytes of output
|
||||
char32_t code = (((utf8[i] & 0x07) << 18) |
|
||||
((utf8[i + 1] & 0x3f) << 12) |
|
||||
((utf8[i + 2] & 0x3f) << 6) |
|
||||
( utf8[i + 3] & 0x3f));
|
||||
char32_t first;
|
||||
char32_t second;
|
||||
|
||||
if (code > 0x10ffff) {
|
||||
// These could be valid utf-8, but cannot be represented as modified UTF-8, due to the 20-bit
|
||||
// limit on that representation. Encode two replacement characters, so the expected output
|
||||
// length lines up.
|
||||
const char32_t kUnicodeReplacementChar = 0xfffd;
|
||||
first = kUnicodeReplacementChar;
|
||||
second = kUnicodeReplacementChar;
|
||||
} else {
|
||||
// split into surrogate pair
|
||||
first = ((code - 0x010000) >> 10) | 0xd800;
|
||||
second = ((code - 0x010000) & 0x3ff) | 0xdc00;
|
||||
}
|
||||
|
||||
// encode each as a 3 byte surrogate value
|
||||
FBASSERTMSGF(j + 5 < modifiedBufLen, "output buffer is too short");
|
||||
encode3ByteUTF8(first, modified + j);
|
||||
encode3ByteUTF8(second, modified + j + 3);
|
||||
i += 4;
|
||||
j += 6;
|
||||
}
|
||||
|
||||
FBASSERTMSGF(j < modifiedBufLen, "output buffer is too short");
|
||||
modified[j++] = '\0';
|
||||
}
|
||||
|
||||
std::string modifiedUTF8ToUTF8(const uint8_t* modified, size_t len) noexcept {
|
||||
// Converting from modified utf8 to utf8 will always shrink, so this will always be sufficient
|
||||
std::string utf8(len, 0);
|
||||
size_t j = 0;
|
||||
for (size_t i = 0; i < len; ) {
|
||||
// surrogate pair: 1101 10xx xxxx xxxx 1101 11xx xxxx xxxx
|
||||
// encoded pair: 1110 1101 1010 xxxx 10xx xxxx 1110 1101 1011 xxxx 10xx xxxx
|
||||
|
||||
if (len >= i + 6 &&
|
||||
modified[i] == 0xed &&
|
||||
(modified[i + 1] & 0xf0) == 0xa0 &&
|
||||
modified[i + 3] == 0xed &&
|
||||
(modified[i + 4] & 0xf0) == 0xb0) {
|
||||
// Valid surrogate pair
|
||||
char32_t pair1 = decode3ByteUTF8(modified + i);
|
||||
char32_t pair2 = decode3ByteUTF8(modified + i + 3);
|
||||
char32_t ch = 0x10000 + (((pair1 & 0x3ff) << 10) |
|
||||
( pair2 & 0x3ff));
|
||||
encode4ByteUTF8(ch, utf8, j);
|
||||
i += 6;
|
||||
j += 4;
|
||||
continue;
|
||||
} else if (len >= i + 2 &&
|
||||
modified[i] == 0xc0 &&
|
||||
modified[i + 1] == 0x80) {
|
||||
utf8[j] = 0;
|
||||
i += 2;
|
||||
j += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
// copy one byte. This might be a one, two, or three-byte encoding. It might be an invalid
|
||||
// encoding of some sort, but garbage in garbage out is ok.
|
||||
|
||||
utf8[j] = (char) modified[i];
|
||||
i++;
|
||||
j++;
|
||||
}
|
||||
|
||||
utf8.resize(j);
|
||||
|
||||
return utf8;
|
||||
}
|
||||
|
||||
// Calculate how many bytes are needed to convert an UTF16 string into UTF8
|
||||
// UTF16 string
|
||||
size_t utf16toUTF8Length(const uint16_t* utf16String, size_t utf16StringLen) {
|
||||
if (!utf16String || utf16StringLen == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t utf8StringLen = 0;
|
||||
auto utf16StringEnd = utf16String + utf16StringLen;
|
||||
auto idx16 = utf16String;
|
||||
while (idx16 < utf16StringEnd) {
|
||||
auto ch = *idx16++;
|
||||
if (ch < kUtf8OneByteBoundary) {
|
||||
utf8StringLen++;
|
||||
} else if (ch < kUtf8TwoBytesBoundary) {
|
||||
utf8StringLen += 2;
|
||||
} else if (
|
||||
(ch >= kUtf16HighSubLowBoundary) && (ch < kUtf16HighSubHighBoundary) &&
|
||||
(idx16 < utf16StringEnd) &&
|
||||
(*idx16 >= kUtf16HighSubHighBoundary) && (*idx16 < kUtf16LowSubHighBoundary)) {
|
||||
utf8StringLen += 4;
|
||||
idx16++;
|
||||
} else {
|
||||
utf8StringLen += 3;
|
||||
}
|
||||
}
|
||||
|
||||
return utf8StringLen;
|
||||
}
|
||||
|
||||
std::string utf16toUTF8(const uint16_t* utf16String, size_t utf16StringLen) noexcept {
|
||||
if (!utf16String || utf16StringLen <= 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string utf8String(utf16toUTF8Length(utf16String, utf16StringLen), '\0');
|
||||
auto idx8 = utf8String.begin();
|
||||
auto idx16 = utf16String;
|
||||
auto utf16StringEnd = utf16String + utf16StringLen;
|
||||
while (idx16 < utf16StringEnd) {
|
||||
auto ch = *idx16++;
|
||||
if (ch < kUtf8OneByteBoundary) {
|
||||
*idx8++ = (ch & 0x7F);
|
||||
} else if (ch < kUtf8TwoBytesBoundary) {
|
||||
*idx8++ = 0b11000000 | (ch >> 6);
|
||||
*idx8++ = 0b10000000 | (ch & 0x3F);
|
||||
} else if (
|
||||
(ch >= kUtf16HighSubLowBoundary) && (ch < kUtf16HighSubHighBoundary) &&
|
||||
(idx16 < utf16StringEnd) &&
|
||||
(*idx16 >= kUtf16HighSubHighBoundary) && (*idx16 < kUtf16LowSubHighBoundary)) {
|
||||
auto ch2 = *idx16++;
|
||||
uint8_t trunc_byte = (((ch >> 6) & 0x0F) + 1);
|
||||
*idx8++ = 0b11110000 | (trunc_byte >> 2);
|
||||
*idx8++ = 0b10000000 | ((trunc_byte & 0x03) << 4) | ((ch >> 2) & 0x0F);
|
||||
*idx8++ = 0b10000000 | ((ch & 0x03) << 4) | ((ch2 >> 6) & 0x0F);
|
||||
*idx8++ = 0b10000000 | (ch2 & 0x3F);
|
||||
} else {
|
||||
*idx8++ = 0b11100000 | (ch >> 12);
|
||||
*idx8++ = 0b10000000 | ((ch >> 6) & 0x3F);
|
||||
*idx8++ = 0b10000000 | (ch & 0x3F);
|
||||
}
|
||||
}
|
||||
|
||||
return utf8String;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
LocalString::LocalString(const std::string& str)
|
||||
{
|
||||
size_t modlen = detail::modifiedLength(str);
|
||||
if (modlen == str.size()) {
|
||||
// no supplementary characters, build jstring from input buffer
|
||||
m_string = Environment::current()->NewStringUTF(str.data());
|
||||
return;
|
||||
}
|
||||
auto modified = std::vector<char>(modlen + 1); // allocate extra byte for \0
|
||||
detail::utf8ToModifiedUTF8(
|
||||
reinterpret_cast<const uint8_t*>(str.data()), str.size(),
|
||||
reinterpret_cast<uint8_t*>(modified.data()), modified.size());
|
||||
m_string = Environment::current()->NewStringUTF(modified.data());
|
||||
}
|
||||
|
||||
LocalString::LocalString(const char* str)
|
||||
{
|
||||
size_t len;
|
||||
size_t modlen = detail::modifiedLength(reinterpret_cast<const uint8_t*>(str), &len);
|
||||
if (modlen == len) {
|
||||
// no supplementary characters, build jstring from input buffer
|
||||
m_string = Environment::current()->NewStringUTF(str);
|
||||
return;
|
||||
}
|
||||
auto modified = std::vector<char>(modlen + 1); // allocate extra byte for \0
|
||||
detail::utf8ToModifiedUTF8(
|
||||
reinterpret_cast<const uint8_t*>(str), len,
|
||||
reinterpret_cast<uint8_t*>(modified.data()), modified.size());
|
||||
m_string = Environment::current()->NewStringUTF(modified.data());
|
||||
}
|
||||
|
||||
LocalString::~LocalString() {
|
||||
Environment::current()->DeleteLocalRef(m_string);
|
||||
}
|
||||
|
||||
std::string fromJString(JNIEnv* env, jstring str) {
|
||||
auto utf16String = JStringUtf16Extractor(env, str);
|
||||
return detail::utf16toUTF8(utf16String.chars(), utf16String.length());
|
||||
}
|
||||
|
||||
} }
|
||||
@@ -1,20 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#include <jni/Countable.h>
|
||||
#include <fb/Environment.h>
|
||||
#include <fb/fbjni.h>
|
||||
#include <fb/fbjni/NativeRunnable.h>
|
||||
|
||||
using namespace facebook::jni;
|
||||
|
||||
void initialize_fbjni() {
|
||||
CountableOnLoad(Environment::current());
|
||||
HybridDataOnLoad();
|
||||
JNativeRunnable::OnLoad();
|
||||
ThreadScope::OnLoad();
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#include <fb/fbjni/ReadableByteChannel.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
int JReadableByteChannel::read(alias_ref<JByteBuffer> dest) const {
|
||||
if (!self()) {
|
||||
throwNewJavaException("java/lang/NullPointerException", "java.lang.NullPointerException");
|
||||
}
|
||||
static auto method = javaClassStatic()->getMethod<jint(alias_ref<JByteBuffer>)>("read");
|
||||
return method(self(), dest);
|
||||
}
|
||||
|
||||
}}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user