adding module deps + fixing gradle

This commit is contained in:
Nabil Hachicha 2015-12-01 22:27:48 +00:00 committed by Ari Lazier
parent d7ca354a27
commit 5b06c41163
63 changed files with 7551 additions and 133 deletions

View File

@ -6,7 +6,7 @@
#include "js_object.hpp"
#include "js_util.hpp"
#include "object_accessor.hpp"
laksdlasd
using RJSAccessor = realm::NativeAccessor<JSValueRef, JSContextRef>;
using namespace realm;

View File

@ -1,79 +1,271 @@
apply plugin: "com.android.application"
// Copyright 2015-present Facebook. All Rights Reserved.
/**
* The react.gradle file registers two tasks: bundleDebugJsAndAssets and bundleReleaseJsAndAssets.
* These basically call `react-native bundle` with the correct arguments during the Android build
* cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the
* bundle directly from the development server. Below you can see all the possible configurations
* and their defaults. If you decide to add a configuration block, make sure to add it before the
* `apply from: "react.gradle"` line.
*
* project.ext.react = [
* // the name of the generated asset file containing your JS bundle
* bundleAssetName: "index.android.bundle",
*
* // the entry file for bundle generation
* entryFile: "index.android.js",
*
* // whether to bundle JS and assets in debug mode
* bundleInDebug: false,
*
* // whether to bundle JS and assets in release mode
* bundleInRelease: true,
*
* // the root of your project, i.e. where "package.json" lives
* root: "../../",
*
* // where to put the JS bundle asset in debug mode
* jsBundleDirDebug: "$buildDir/intermediates/assets/debug",
*
* // where to put the JS bundle asset in release mode
* jsBundleDirRelease: "$buildDir/intermediates/assets/release",
*
* // where to put drawable resources / React Native assets, e.g. the ones you use via
* // require('./image.png')), in debug mode
* resourcesDirDebug: "$buildDir/intermediates/res/merged/debug",
*
* // where to put drawable resources / React Native assets, e.g. the ones you use via
* // require('./image.png')), in release mode
* resourcesDirRelease: "$buildDir/intermediates/res/merged/release",
*
* // by default the gradle tasks are skipped if none of the JS files or assets change; this means
* // that we don't look at files in android/ or ios/ to determine whether the tasks are up to
* // date; if you have any other folders that you want to ignore for performance reasons (gradle
* // indexes the entire tree), add them here. Alternatively, if you have JS files in android/
* // for example, you might want to remove it from here.
* inputExcludes: ["android/**", "ios/**"]
* ]
*/
apply plugin: 'com.android.library'
apply plugin: 'maven'
apply from: "react.gradle"
apply plugin: 'de.undercouch.download'
import de.undercouch.gradle.tasks.download.Download
import org.apache.tools.ant.taskdefs.condition.Os
import org.apache.tools.ant.filters.ReplaceTokens
// We download various C++ open-source dependencies into downloads.
// We then copy both the downloaded code and our custom makefiles and headers into third-party-ndk.
// After that we build native code from src/main/jni with module path pointing at third-party-ndk.
def downloadsDir = new File("$buildDir/downloads")
def thirdPartyNdkDir = new File("$buildDir/third-party-ndk")
task createNativeDepsDirectories {
downloadsDir.mkdirs()
thirdPartyNdkDir.mkdirs()
}
task downloadBoost(dependsOn: createNativeDepsDirectories, type: Download) {
// Use ZIP version as it's faster this way to selectively extract some parts of the archive
src 'https://downloads.sourceforge.net/project/boost/boost/1.57.0/boost_1_57_0.zip'
onlyIfNewer true
overwrite false
dest new File(downloadsDir, 'boost_1_57_0.zip')
}
task prepareBoost(dependsOn: downloadBoost) {
inputs.files downloadBoost.dest, 'src/main/jni/third-party/boost/Android.mk'
outputs.dir "$thirdPartyNdkDir/boost"
doLast {
copy {
from { zipTree(downloadBoost.dest) }
from 'src/main/jni/third-party/boost/Android.mk'
include 'boost_1_57_0/boost/**/*.hpp', 'Android.mk'
into "$thirdPartyNdkDir/boost"
}
}
}
task downloadDoubleConversion(dependsOn: createNativeDepsDirectories, type: Download) {
src 'https://github.com/google/double-conversion/archive/v1.1.1.tar.gz'
onlyIfNewer true
overwrite false
dest new File(downloadsDir, 'double-conversion-1.1.1.tar.gz')
}
task prepareDoubleConversion(dependsOn: downloadDoubleConversion, type: Copy) {
from tarTree(downloadDoubleConversion.dest)
from 'src/main/jni/third-party/double-conversion/Android.mk'
include 'double-conversion-1.1.1/src/**/*', 'Android.mk'
filesMatching('*/src/**/*', {fname -> fname.path = "double-conversion/${fname.name}"})
includeEmptyDirs = false
into "$thirdPartyNdkDir/double-conversion"
}
task downloadFolly(dependsOn: createNativeDepsDirectories, type: Download) {
src 'https://github.com/facebook/folly/archive/v0.50.0.tar.gz'
onlyIfNewer true
overwrite false
dest new File(downloadsDir, 'folly-0.50.0.tar.gz');
}
task prepareFolly(dependsOn: downloadFolly, type: Copy) {
from tarTree(downloadFolly.dest)
from 'src/main/jni/third-party/folly/Android.mk'
include 'folly-0.50.0/folly/**/*', 'Android.mk'
eachFile {fname -> fname.path = (fname.path - "folly-0.50.0/")}
includeEmptyDirs = false
into "$thirdPartyNdkDir/folly"
}
task downloadGlog(dependsOn: createNativeDepsDirectories, type: Download) {
src 'https://github.com/google/glog/archive/v0.3.3.tar.gz'
onlyIfNewer true
overwrite false
dest new File(downloadsDir, 'glog-0.3.3.tar.gz')
}
// Prepare glog sources to be compiled, this task will perform steps that normally shoudl've been
// executed by automake. This way we can avoid dependencies on make/automake
task prepareGlog(dependsOn: downloadGlog, type: Copy) {
from tarTree(downloadGlog.dest)
from 'src/main/jni/third-party/glog/'
include 'glog-0.3.3/src/**/*', 'Android.mk', 'config.h'
includeEmptyDirs = false
filesMatching('**/*.h.in') {
filter(ReplaceTokens, tokens: [
ac_cv_have_unistd_h: '1',
ac_cv_have_stdint_h: '1',
ac_cv_have_systypes_h: '1',
ac_cv_have_inttypes_h: '1',
ac_cv_have_libgflags: '0',
ac_google_start_namespace: 'namespace google {',
ac_cv_have_uint16_t: '1',
ac_cv_have_u_int16_t: '1',
ac_cv_have___uint16: '0',
ac_google_end_namespace: '}',
ac_cv_have___builtin_expect: '1',
ac_google_namespace: 'google',
ac_cv___attribute___noinline: '__attribute__ ((noinline))',
ac_cv___attribute___noreturn: '__attribute__ ((noreturn))',
ac_cv___attribute___printf_4_5: '__attribute__((__format__ (__printf__, 4, 5)))'
])
it.path = (it.name - '.in')
}
into "$thirdPartyNdkDir/glog"
}
task downloadJSCHeaders(type: Download) {
def jscAPIBaseURL = 'https://svn.webkit.org/repository/webkit/!svn/bc/174650/trunk/Source/JavaScriptCore/API/'
def jscHeaderFiles = ['JSBase.h', 'JSContextRef.h', 'JSObjectRef.h', 'JSRetainPtr.h', 'JSStringRef.h', 'JSValueRef.h', 'WebKitAvailability.h', 'JavaScriptCore.h', 'JavaScript.h', 'JSStringRefCF.h']
def output = new File(downloadsDir, 'jsc')
output.mkdirs()
src(jscHeaderFiles.collect { headerName -> "$jscAPIBaseURL$headerName" })
onlyIfNewer true
overwrite false
dest output
}
// Create Android.mk library module based on so files from mvn + include headers fetched from webkit.org
task prepareJSC(dependsOn: downloadJSCHeaders) << {
copy {
from zipTree(configurations.compile.fileCollection { dep -> dep.name == 'android-jsc' }.singleFile)
from {downloadJSCHeaders.dest}
from 'src/main/jni/third-party/jsc/Android.mk'
include 'jni/**/*.so', '*.h', 'Android.mk'
filesMatching('*.h', { fname -> fname.path = "JavaScriptCore/${fname.path}"})
into "$thirdPartyNdkDir/jsc";
}
}
def getNdkBuildName() {
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
return "ndk-build.cmd"
} else {
return "ndk-build"
}
}
def findNdkBuildFullPath() {
// we allow to provide full path to ndk-build tool
if (hasProperty('ndk.command')) {
return property('ndk.command')
}
// or just a path to the containing directory
if (hasProperty('ndk.path')) {
def ndkDir = property('ndk.path')
return new File(ndkDir, getNdkBuildName()).getAbsolutePath()
}
if (System.getenv('ANDROID_NDK') != null) {
def ndkDir = System.getenv('ANDROID_NDK')
return new File(ndkDir, getNdkBuildName()).getAbsolutePath()
}
def ndkDir = android.hasProperty('plugin') ? android.plugin.ndkFolder :
plugins.getPlugin('com.android.library').sdkHandler.getNdkFolder()
if (ndkDir) {
return new File(ndkDir, getNdkBuildName()).getAbsolutePath()
}
return null
}
def getNdkBuildFullPath() {
def ndkBuildFullPath = findNdkBuildFullPath()
if (ndkBuildFullPath == null) {
throw new GradleScriptException(
"ndk-build binary cannot be found, check if you've set " +
"\$ANDROID_NDK environment variable correctly or if ndk.dir is " +
"setup in local.properties",
null)
}
if (!new File(ndkBuildFullPath).canExecute()) {
throw new GradleScriptException(
"ndk-build binary " + ndkBuildFullPath + " doesn't exist or isn't executable.\n" +
"Check that the \$ANDROID_NDK environment variable, or ndk.dir in local.proerties, is set correctly.\n" +
"(On Windows, make sure you escape backslashes in local.properties or use forward slashes, e.g. C:\\\\ndk or C:/ndk rather than C:\\ndk)",
null)
}
return ndkBuildFullPath
}
task buildReactNdkLib(dependsOn: [prepareJSC, prepareBoost, prepareDoubleConversion, prepareFolly, prepareGlog], type: Exec) {
inputs.file('src/main/jni')
outputs.dir("$buildDir/react-ndk/all")
commandLine getNdkBuildFullPath(),
'NDK_PROJECT_PATH=null',
"NDK_APPLICATION_MK=$projectDir/src/main/jni/Application.mk",
'NDK_OUT=' + temporaryDir,
"NDK_LIBS_OUT=$buildDir/react-ndk/all",
"THIRD_PARTY_NDK_DIR=$buildDir/third-party-ndk",
'-C', file('src/main/jni').absolutePath,
'-B',
'NDK_LOG=1',
'--jobs', Runtime.runtime.availableProcessors()
}
task cleanReactNdkLib(type: Exec) {
commandLine getNdkBuildFullPath(),
'-C', file('src/main/jni').absolutePath,
'clean'
}
task packageReactNdkLibs(dependsOn: buildReactNdkLib, type: Copy) {
from "$buildDir/react-ndk/all"
exclude '**/libjsc.so'
into "$buildDir/react-ndk/exported"
}
android {
compileSdkVersion 23
buildToolsVersion "23.0.1"
defaultConfig {
applicationId "com.reacttests"
minSdkVersion 16
targetSdkVersion 22
versionCode 1
versionName "1.0"
ndk {
moduleName = "realm-react-android"
abiFilters "armeabi-v7a", "x86"
moduleName "reactnativejni"
}
buildConfigField 'boolean', 'IS_INTERNAL_BUILD', 'false'
}
buildTypes {
release {
minifyEnabled false // Set this to true to enable Proguard
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
sourceSets.main {
jni.srcDirs = []
jniLibs.srcDir "$buildDir/react-ndk/exported"
res.srcDirs = ['src/main/res/devsupport', 'src/main/res/shell']
}
tasks.withType(JavaCompile) {
compileTask -> compileTask.dependsOn packageReactNdkLibs
}
clean.dependsOn cleanReactNdkLib
lintOptions {
abortOnError false
}
}
dependencies {
compile fileTree(dir: "libs", include: ["*.jar"])
compile "com.android.support:appcompat-v7:23.0.1"
compile "com.facebook.react:react-native:0.14.+"
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:23.0.1'
compile 'com.android.support:recyclerview-v7:23.0.1'
compile 'com.facebook.fresco:fresco:0.8.1'
compile 'com.facebook.fresco:imagepipeline-okhttp:0.8.1'
compile 'com.facebook.stetho:stetho:1.2.0'
compile 'com.facebook.stetho:stetho-okhttp:1.2.0'
compile 'com.fasterxml.jackson.core:jackson-core:2.2.3'
compile 'com.google.code.findbugs:jsr305:3.0.0'
compile 'com.squareup.okhttp:okhttp:2.5.0'
compile 'com.squareup.okhttp:okhttp-ws:2.5.0'
compile 'com.squareup.okio:okio:1.6.0'
compile 'org.webkit:android-jsc:r174650'
testCompile "junit:junit:${JUNIT_VERSION}"
testCompile "org.powermock:powermock-api-mockito:${POWERMOCK_VERSION}"
testCompile "org.powermock:powermock-module-junit4-rule:${POWERMOCK_VERSION}"
testCompile "org.powermock:powermock-classloading-xstream:${POWERMOCK_VERSION}"
testCompile "org.mockito:mockito-core:${MOCKITO_CORE_VERSION}"
testCompile "org.easytesting:fest-assert-core:${FEST_ASSERT_CORE_VERSION}"
testCompile("org.robolectric:robolectric:${ROBOLECTRIC_VERSION}")
}
apply from: 'release.gradle'

View File

@ -0,0 +1,14 @@
VERSION_NAME=0.12.0-SNAPSHOT
GROUP=com.facebook.react
POM_NAME=ReactNative
POM_ARTIFACT_ID=react-native
POM_PACKAGING=aar
android.useDeprecatedNdk=true
MOCKITO_CORE_VERSION=1.+
POWERMOCK_VERSION=1.6.2
ROBOLECTRIC_VERSION=3.0
JUNIT_VERSION=4.12
FEST_ASSERT_CORE_VERSION=2.0M10

View File

@ -0,0 +1,128 @@
// Copyright 2015-present Facebook. All Rights Reserved.
apply plugin: 'maven'
apply plugin: 'signing'
// Gradle tasks for publishing to maven
// 1) To install in local maven repo use :installArchives task
// 2) To upload artifact to maven central use: :uploadArchives (you'd need to have the permission to do that)
def isReleaseBuild() {
return VERSION_NAME.contains('SNAPSHOT') == false
}
def getRepositoryUrl() {
return hasProperty('repositoryUrl') ? property('repositoryUrl') : 'https://oss.sonatype.org/service/local/staging/deploy/maven2/'
}
def getRepositoryUsername() {
return hasProperty('repositoryUsername') ? property('repositoryUsername') : ''
}
def getRepositoryPassword() {
return hasProperty('repositoryPassword') ? property('repositoryPassword') : ''
}
def configureReactNativePom(def pom) {
pom.project {
name POM_NAME
artifactId POM_ARTIFACT_ID
packaging POM_PACKAGING
description 'A framework for building native apps with React'
url 'https://github.com/facebook/react-native'
scm {
url 'https://github.com/facebook/react-native.git'
connection 'scm:git:https://github.com/facebook/react-native.git'
developerConnection 'scm:git:git@github.com:facebook/react-native.git'
}
licenses {
license {
name 'BSD License'
url 'https://github.com/facebook/react-native/blob/master/LICENSE'
distribution 'repo'
}
}
developers {
developer {
id 'facebook'
name 'Facebook'
}
}
}
}
afterEvaluate { project ->
task androidJavadoc(type: Javadoc) {
source = android.sourceSets.main.java.srcDirs
classpath += files(android.bootClasspath)
classpath += files(project.getConfigurations().getByName('compile').asList())
include '**/*.java'
exclude '**/ReactBuildConfig.java'
}
task androidJavadocJar(type: Jar, dependsOn: androidJavadoc) {
classifier = 'javadoc'
from androidJavadoc.destinationDir
}
task androidSourcesJar(type: Jar) {
classifier = 'sources'
from android.sourceSets.main.java.srcDirs
include '**/*.java'
}
android.libraryVariants.all { variant ->
def name = variant.name.capitalize()
task "jar${name}"(type: Jar, dependsOn: variant.javaCompile) {
from variant.javaCompile.destinationDir
}
}
artifacts {
archives androidSourcesJar
// TODO Make Javadoc generation work with Java 1.8, currently only works with 1.7
// archives androidJavadocJar
}
version = VERSION_NAME
group = GROUP
signing {
required { isReleaseBuild() && gradle.taskGraph.hasTask('uploadArchives') }
sign configurations.archives
}
uploadArchives {
configuration = configurations.archives
repositories.mavenDeployer {
beforeDeployment {
MavenDeployment deployment -> signing.signPom(deployment)
}
repository(url: getRepositoryUrl()) {
authentication(
userName: getRepositoryUsername(),
password: getRepositoryPassword())
}
configureReactNativePom pom
}
}
task installArchives(type: Upload) {
configuration = configurations.archives
repositories.mavenDeployer {
beforeDeployment {
MavenDeployment deployment -> signing.signPom(deployment)
}
repository url: "file://${System.properties['user.home']}/.m2/repository"
configureReactNativePom pom
}
}
}

View File

@ -1,4 +1,4 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
<!-- <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.reacttests">
<uses-permission android:name="android.permission.INTERNET" />
@ -19,4 +19,11 @@
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
</application>
</manifest> -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.reacttests">
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<application />
</manifest>

View File

@ -1,78 +1,78 @@
package com.reacttests;
// package com.reacttests;
import android.app.Activity;
import android.os.Bundle;
import android.view.KeyEvent;
// import android.app.Activity;
// import android.os.Bundle;
// import android.view.KeyEvent;
import com.facebook.react.LifecycleState;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactRootView;
import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
import com.facebook.react.shell.MainReactPackage;
import com.facebook.soloader.SoLoader;
// import com.facebook.react.LifecycleState;
// import com.facebook.react.ReactInstanceManager;
// import com.facebook.react.ReactRootView;
// import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
// import com.facebook.react.shell.MainReactPackage;
// import com.facebook.soloader.SoLoader;
public class MainActivity extends Activity implements DefaultHardwareBackBtnHandler {
// public class MainActivity extends Activity implements DefaultHardwareBackBtnHandler {
private ReactInstanceManager mReactInstanceManager;
private ReactRootView mReactRootView;
// private ReactInstanceManager mReactInstanceManager;
// private ReactRootView mReactRootView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mReactRootView = new ReactRootView(this);
// @Override
// protected void onCreate(Bundle savedInstanceState) {
// super.onCreate(savedInstanceState);
// mReactRootView = new ReactRootView(this);
mReactInstanceManager = ReactInstanceManager.builder()
.setApplication(getApplication())
.setBundleAssetName("index.android.bundle")
.setJSMainModuleName("index.android")
.addPackage(new MainReactPackage())
.setUseDeveloperSupport(BuildConfig.DEBUG)
.setInitialLifecycleState(LifecycleState.RESUMED)
.build();
// mReactInstanceManager = ReactInstanceManager.builder()
// .setApplication(getApplication())
// .setBundleAssetName("index.android.bundle")
// .setJSMainModuleName("index.android")
// .addPackage(new MainReactPackage())
// .setUseDeveloperSupport(BuildConfig.DEBUG)
// .setInitialLifecycleState(LifecycleState.RESUMED)
// .build();
mReactRootView.startReactApplication(mReactInstanceManager, "ReactTests", null);
// mReactRootView.startReactApplication(mReactInstanceManager, "ReactTests", null);
setContentView(mReactRootView);
}
// setContentView(mReactRootView);
// }
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_MENU && mReactInstanceManager != null) {
mReactInstanceManager.showDevOptionsDialog();
return true;
}
return super.onKeyUp(keyCode, event);
}
// @Override
// public boolean onKeyUp(int keyCode, KeyEvent event) {
// if (keyCode == KeyEvent.KEYCODE_MENU && mReactInstanceManager != null) {
// mReactInstanceManager.showDevOptionsDialog();
// return true;
// }
// return super.onKeyUp(keyCode, event);
// }
@Override
public void onBackPressed() {
if (mReactInstanceManager != null) {
mReactInstanceManager.onBackPressed();
} else {
super.onBackPressed();
}
}
// @Override
// public void onBackPressed() {
// if (mReactInstanceManager != null) {
// mReactInstanceManager.onBackPressed();
// } else {
// super.onBackPressed();
// }
// }
@Override
public void invokeDefaultOnBackPressed() {
super.onBackPressed();
}
// @Override
// public void invokeDefaultOnBackPressed() {
// super.onBackPressed();
// }
@Override
protected void onPause() {
super.onPause();
// @Override
// protected void onPause() {
// super.onPause();
if (mReactInstanceManager != null) {
mReactInstanceManager.onPause();
}
}
// if (mReactInstanceManager != null) {
// mReactInstanceManager.onPause();
// }
// }
@Override
protected void onResume() {
super.onResume();
// @Override
// protected void onResume() {
// super.onResume();
if (mReactInstanceManager != null) {
mReactInstanceManager.onResume(this);
}
}
}
// if (mReactInstanceManager != null) {
// mReactInstanceManager.onResume(this);
// }
// }
// }

View File

@ -2,7 +2,7 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := librealmreact
LOCAL_MODULE := nabil
LOCAL_SRC_FILES := \
js_list.cpp \
@ -10,16 +10,10 @@ LOCAL_SRC_FILES := \
js_init.cpp \
js_realm.cpp \
js_util.cpp \
realm-react-android.c \
js_object.cpp \
js_schema.cpp \
rpc.cpp \
LOCAL_C_INCLUDES := $(LOCAL_PATH)/..
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_C_INCLUDES)
LOCAL_CFLAGS := \
LOCAL_CFLAGS += -Wall -Werror -fexceptions
CXX11_FLAGS := -std=c++11
LOCAL_CFLAGS += $(CXX11_FLAGS)
@ -27,7 +21,7 @@ LOCAL_EXPORT_CPPFLAGS := $(CXX11_FLAGS)
LOCAL_SHARED_LIBRARIES := libjsc
include $(BUILD_STATIC_LIBRARY)
include $(BUILD_SHARED_LIBRARY)
$(call import-module,jsc)

View File

@ -0,0 +1,15 @@
APP_BUILD_SCRIPT := Android.mk
APP_ABI := armeabi-v7a x86
APP_PLATFORM := android-9
APP_MK_DIR := $(dir $(lastword $(MAKEFILE_LIST)))
NDK_MODULE_PATH := $(APP_MK_DIR)$(HOST_DIRSEP)$(THIRD_PARTY_NDK_DIR)$(HOST_DIRSEP)$(APP_MK_DIR)first-party
APP_STL := gnustl_shared
# Make sure every shared lib includes a .note.gnu.build-id header
APP_LDFLAGS := -Wl,--build-id
NDK_TOOLCHAIN_VERSION := 4.8

View File

@ -0,0 +1,30 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
assert.cpp \
log.cpp \
LOCAL_C_INCLUDES := $(LOCAL_PATH)/.. $(LOCAL_PATH)/include
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/.. $(LOCAL_PATH)/include
LOCAL_CFLAGS := -DLOG_TAG=\"libfb\"
LOCAL_CFLAGS += -Wall -Werror
# include/utils/threads.h has unused parameters
LOCAL_CFLAGS += -Wno-unused-parameter
ifeq ($(TOOLCHAIN_PERMISSIVE),true)
LOCAL_CFLAGS += -Wno-error=unused-but-set-variable
endif
LOCAL_CFLAGS += -DHAVE_POSIX_CLOCKS
CXX11_FLAGS := -std=c++11
LOCAL_CFLAGS += $(CXX11_FLAGS)
LOCAL_EXPORT_CPPFLAGS := $(CXX11_FLAGS)
LOCAL_LDLIBS := -llog -ldl -landroid
LOCAL_EXPORT_LDLIBS := -llog
LOCAL_MODULE := libfb
include $(BUILD_SHARED_LIBRARY)

View File

@ -0,0 +1,47 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#pragma once
#include <atomic>
#include <fb/assert.h>
#include <fb/noncopyable.h>
#include <fb/nonmovable.h>
#include <fb/RefPtr.h>
namespace facebook {
class Countable : public noncopyable, public nonmovable {
public:
// RefPtr expects refcount to start at 0
Countable() : m_refcount(0) {}
virtual ~Countable()
{
FBASSERT(m_refcount == 0);
}
private:
void ref() {
++m_refcount;
}
void unref() {
if (0 == --m_refcount) {
delete this;
}
}
bool hasOnlyOneRef() const {
return m_refcount == 1;
}
template <typename T> friend class RefPtr;
std::atomic<int> m_refcount;
};
}

View File

@ -0,0 +1,50 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#pragma once
#include <cstring>
#include <string>
#include <sstream>
namespace facebook {
#define FROM_HERE facebook::ProgramLocation(__FUNCTION__, __FILE__, __LINE__)
class ProgramLocation {
public:
ProgramLocation() : m_functionName("Unspecified"), m_fileName("Unspecified"), m_lineNumber(0) {}
ProgramLocation(const char* functionName, const char* fileName, int line) :
m_functionName(functionName),
m_fileName(fileName),
m_lineNumber(line)
{}
const char* functionName() const { return m_functionName; }
const char* fileName() const { return m_fileName; }
int lineNumber() const { return m_lineNumber; }
std::string asFormattedString() const {
std::stringstream str;
str << "Function " << m_functionName << " in file " << m_fileName << ":" << m_lineNumber;
return str.str();
}
bool operator==(const ProgramLocation& other) const {
// Assumes that the strings are static
return (m_functionName == other.m_functionName) && (m_fileName == other.m_fileName) && m_lineNumber == other.m_lineNumber;
}
private:
const char* m_functionName;
const char* m_fileName;
int m_lineNumber;
};
}

View File

@ -0,0 +1,274 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#pragma once
#include <utility>
#include <fb/assert.h>
namespace facebook {
// Reference counting smart pointer. This is designed to work with the
// Countable class or other implementations in the future. It is designed in a
// way to be both efficient and difficult to misuse. Typical usage is very
// simple once you learn the patterns (and the compiler will help!):
//
// By default, the internal pointer is null.
// RefPtr<Foo> ref;
//
// Object creation requires explicit construction:
// RefPtr<Foo> ref = createNew<Foo>(...);
//
// Or if the constructor is not public:
// RefPtr<Foo> ref = adoptRef(new Foo(...));
//
// But you can implicitly create from nullptr:
// RefPtr<Foo> maybeRef = cond ? ref : nullptr;
//
// Move/Copy Construction/Assignment are straightforward:
// RefPtr<Foo> ref2 = ref;
// ref = std::move(ref2);
//
// Destruction automatically drops the RefPtr's reference as expected.
//
// Upcasting is implicit but downcasting requires an explicit cast:
// struct Bar : public Foo {};
// RefPtr<Bar> barRef = static_cast<RefPtr<Bar>>(ref);
// ref = barRef;
//
template <class T>
class RefPtr {
public:
constexpr RefPtr() :
m_ptr(nullptr)
{}
// Allow implicit construction from a pointer only from nullptr
constexpr RefPtr(std::nullptr_t ptr) :
m_ptr(nullptr)
{}
RefPtr(const RefPtr<T>& ref) :
m_ptr(ref.m_ptr)
{
refIfNecessary(m_ptr);
}
// Only allow implicit upcasts. A downcast will result in a compile error
// unless you use static_cast (which will end up invoking the explicit
// operator below).
template <typename U>
RefPtr(const RefPtr<U>& ref, typename std::enable_if<std::is_base_of<T,U>::value, U>::type* = nullptr) :
m_ptr(ref.get())
{
refIfNecessary(m_ptr);
}
RefPtr(RefPtr<T>&& ref) :
m_ptr(nullptr)
{
*this = std::move(ref);
}
// Only allow implicit upcasts. A downcast will result in a compile error
// unless you use static_cast (which will end up invoking the explicit
// operator below).
template <typename U>
RefPtr(RefPtr<U>&& ref, typename std::enable_if<std::is_base_of<T,U>::value, U>::type* = nullptr) :
m_ptr(nullptr)
{
*this = std::move(ref);
}
~RefPtr() {
unrefIfNecessary(m_ptr);
m_ptr = nullptr;
}
RefPtr<T>& operator=(const RefPtr<T>& ref) {
if (m_ptr != ref.m_ptr) {
unrefIfNecessary(m_ptr);
m_ptr = ref.m_ptr;
refIfNecessary(m_ptr);
}
return *this;
}
// The STL assumes rvalue references are unique and for simplicity's sake, we
// make the same assumption here, that &ref != this.
RefPtr<T>& operator=(RefPtr<T>&& ref) {
unrefIfNecessary(m_ptr);
m_ptr = ref.m_ptr;
ref.m_ptr = nullptr;
return *this;
}
template <typename U>
RefPtr<T>& operator=(RefPtr<U>&& ref) {
unrefIfNecessary(m_ptr);
m_ptr = ref.m_ptr;
ref.m_ptr = nullptr;
return *this;
}
void reset() {
unrefIfNecessary(m_ptr);
m_ptr = nullptr;
}
T* get() const {
return m_ptr;
}
T* operator->() const {
return m_ptr;
}
T& operator*() const {
return *m_ptr;
}
template <typename U>
explicit operator RefPtr<U> () const;
explicit operator bool() const {
return m_ptr ? true : false;
}
bool isTheLastRef() const {
FBASSERT(m_ptr);
return m_ptr->hasOnlyOneRef();
}
// Creates a strong reference from a raw pointer, assuming that is already
// referenced from some other RefPtr. This should be used sparingly.
static inline RefPtr<T> assumeAlreadyReffed(T* ptr) {
return RefPtr<T>(ptr, ConstructionMode::External);
}
// Creates a strong reference from a raw pointer, assuming that it points to a
// freshly-created object. See the documentation for RefPtr for usage.
static inline RefPtr<T> adoptRef(T* ptr) {
return RefPtr<T>(ptr, ConstructionMode::Adopted);
}
private:
enum class ConstructionMode {
Adopted,
External
};
RefPtr(T* ptr, ConstructionMode mode) :
m_ptr(ptr)
{
FBASSERTMSGF(ptr, "Got null pointer in %s construction mode", mode == ConstructionMode::Adopted ? "adopted" : "external");
ptr->ref();
if (mode == ConstructionMode::Adopted) {
FBASSERT(ptr->hasOnlyOneRef());
}
}
static inline void refIfNecessary(T* ptr) {
if (ptr) {
ptr->ref();
}
}
static inline void unrefIfNecessary(T* ptr) {
if (ptr) {
ptr->unref();
}
}
template <typename U> friend class RefPtr;
T* m_ptr;
};
// Creates a strong reference from a raw pointer, assuming that is already
// referenced from some other RefPtr and that it is non-null. This should be
// used sparingly.
template <typename T>
static inline RefPtr<T> assumeAlreadyReffed(T* ptr) {
return RefPtr<T>::assumeAlreadyReffed(ptr);
}
// As above, but tolerant of nullptr.
template <typename T>
static inline RefPtr<T> assumeAlreadyReffedOrNull(T* ptr) {
return ptr ? RefPtr<T>::assumeAlreadyReffed(ptr) : nullptr;
}
// Creates a strong reference from a raw pointer, assuming that it points to a
// freshly-created object. See the documentation for RefPtr for usage.
template <typename T>
static inline RefPtr<T> adoptRef(T* ptr) {
return RefPtr<T>::adoptRef(ptr);
}
template <typename T, typename ...Args>
static inline RefPtr<T> createNew(Args&&... arguments) {
return RefPtr<T>::adoptRef(new T(std::forward<Args>(arguments)...));
}
template <typename T> template <typename U>
RefPtr<T>::operator RefPtr<U>() const {
static_assert(std::is_base_of<T, U>::value, "Invalid static cast");
return assumeAlreadyReffedOrNull<U>(static_cast<U*>(m_ptr));
}
template <typename T, typename U>
inline bool operator==(const RefPtr<T>& a, const RefPtr<U>& b) {
return a.get() == b.get();
}
template <typename T, typename U>
inline bool operator!=(const RefPtr<T>& a, const RefPtr<U>& b) {
return a.get() != b.get();
}
template <typename T, typename U>
inline bool operator==(const RefPtr<T>& ref, U* ptr) {
return ref.get() == ptr;
}
template <typename T, typename U>
inline bool operator!=(const RefPtr<T>& ref, U* ptr) {
return ref.get() != ptr;
}
template <typename T, typename U>
inline bool operator==(U* ptr, const RefPtr<T>& ref) {
return ref.get() == ptr;
}
template <typename T, typename U>
inline bool operator!=(U* ptr, const RefPtr<T>& ref) {
return ref.get() != ptr;
}
template <typename T>
inline bool operator==(const RefPtr<T>& ref, std::nullptr_t ptr) {
return ref.get() == ptr;
}
template <typename T>
inline bool operator!=(const RefPtr<T>& ref, std::nullptr_t ptr) {
return ref.get() != ptr;
}
template <typename T>
inline bool operator==(std::nullptr_t ptr, const RefPtr<T>& ref) {
return ref.get() == ptr;
}
template <typename T>
inline bool operator!=(std::nullptr_t ptr, const RefPtr<T>& ref) {
return ref.get() != ptr;
}
}

View File

@ -0,0 +1,40 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#pragma once
#include <fb/assert.h>
#include <utility>
namespace facebook {
// Class that lets you declare a global but does not add a static constructor
// to the binary. Eventually I'd like to have this auto-initialize in a
// multithreaded environment but for now it's easiest just to use manual
// initialization.
template <typename T>
class StaticInitialized {
public:
constexpr StaticInitialized() :
m_instance(nullptr)
{}
template <typename ...Args>
void initialize(Args&&... arguments) {
FBASSERT(!m_instance);
m_instance = new T(std::forward<Args>(arguments)...);
}
T* operator->() const {
return m_instance;
}
private:
T* m_instance;
};
}

View File

@ -0,0 +1,118 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#pragma once
#include <pthread.h>
#include <errno.h>
#include <fb/assert.h>
namespace facebook {
///////////////////////////////////////////////////////////////////////////////
/**
* A thread-local object is a "global" object within a thread. This is useful
* for writing apartment-threaded code, where nothing is actullay shared
* between different threads (hence no locking) but those variables are not
* on stack in local scope. To use it, just do something like this,
*
* ThreadLocal<MyClass> static_object;
* static_object->data_ = ...;
* static_object->doSomething();
*
* ThreadLocal<int> static_number;
* int value = *static_number;
*
* So, syntax-wise it's similar to pointers. T can be primitive types, and if
* it's a class, there has to be a default constructor.
*/
template<typename T>
class ThreadLocal {
public:
/**
* Constructor that has to be called from a thread-neutral place.
*/
ThreadLocal() :
m_key(0),
m_cleanup(OnThreadExit) {
initialize();
}
/**
* As above but with a custom cleanup function
*/
typedef void (*CleanupFunction)(void* obj);
explicit ThreadLocal(CleanupFunction cleanup) :
m_key(0),
m_cleanup(cleanup) {
FBASSERT(cleanup);
initialize();
}
/**
* Access object's member or method through this operator overload.
*/
T *operator->() const {
return get();
}
T &operator*() const {
return *get();
}
T *get() const {
return (T*)pthread_getspecific(m_key);
}
T* release() {
T* obj = get();
pthread_setspecific(m_key, NULL);
return obj;
}
void reset(T* other = NULL) {
T* old = (T*)pthread_getspecific(m_key);
if (old != other) {
FBASSERT(m_cleanup);
m_cleanup(old);
pthread_setspecific(m_key, other);
}
}
private:
void initialize() {
int ret = pthread_key_create(&m_key, m_cleanup);
if (ret != 0) {
const char *msg = "(unknown error)";
switch (ret) {
case EAGAIN:
msg = "PTHREAD_KEYS_MAX (1024) is exceeded";
break;
case ENOMEM:
msg = "Out-of-memory";
break;
}
(void) msg;
FBASSERTMSGF(0, "pthread_key_create failed: %d %s", ret, msg);
}
}
static void OnThreadExit(void *obj) {
if (NULL != obj) {
delete (T*)obj;
}
}
pthread_key_t m_key;
CleanupFunction m_cleanup;
};
}

View File

@ -0,0 +1,41 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#include <cstdarg>
#include <stdio.h>
#include <fb/assert.h>
#include <fb/log.h>
namespace facebook {
#define ASSERT_BUF_SIZE 4096
static char sAssertBuf[ASSERT_BUF_SIZE];
static AssertHandler gAssertHandler;
void assertInternal(const char* formatstr ...) {
va_list va_args;
va_start(va_args, formatstr);
vsnprintf(sAssertBuf, sizeof(sAssertBuf), formatstr, va_args);
va_end(va_args);
if (gAssertHandler != NULL) {
gAssertHandler(sAssertBuf);
}
FBLOG(LOG_FATAL, "fbassert", "%s", sAssertBuf);
// crash at this specific address so that we can find our crashes easier
*(int*)0xdeadb00c = 0;
// let the compiler know we won't reach the end of the function
__builtin_unreachable();
}
void setAssertHandler(AssertHandler assertHandler) {
gAssertHandler = assertHandler;
}
} // namespace facebook

View File

@ -0,0 +1,34 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#ifndef FBASSERT_H
#define FBASSERT_H
namespace facebook {
#define ENABLE_FBASSERT 1
#if ENABLE_FBASSERT
#define FBASSERTMSGF(expr, msg, ...) !(expr) ? facebook::assertInternal("Assert (%s:%d): " msg, __FILE__, __LINE__, ##__VA_ARGS__) : (void) 0
#else
#define FBASSERTMSGF(expr, msg, ...)
#endif // ENABLE_FBASSERT
#define FBASSERT(expr) FBASSERTMSGF(expr, "%s", #expr)
#define FBCRASH(msg, ...) facebook::assertInternal("Fatal error (%s:%d): " msg, __FILE__, __LINE__, ##__VA_ARGS__)
#define FBUNREACHABLE() facebook::assertInternal("This code should be unreachable (%s:%d)", __FILE__, __LINE__)
void assertInternal(const char* formatstr, ...) __attribute__((noreturn));
// This allows storing the assert message before the current process terminates due to a crash
typedef void (*AssertHandler)(const char* message);
void setAssertHandler(AssertHandler assertHandler);
} // namespace facebook
#endif // FBASSERT_H

View File

@ -0,0 +1,361 @@
/*
* Copyright (C) 2005 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* FB Wrapper for logging functions.
*
* The android logging API uses the macro "LOG()" for its logic, which means
* that it conflicts with random other places that use LOG for their own
* purposes and doesn't work right half the places you include it
*
* FBLOG uses exactly the same semantics (FBLOGD for debug etc) but because of
* the FB prefix it's strictly better. FBLOGV also gets stripped out based on
* whether NDEBUG is set, but can be overridden by FBLOG_NDEBUG
*
* Most of the rest is a copy of <cutils/log.h> with minor changes.
*/
//
// C/C++ logging functions. See the logging documentation for API details.
//
// We'd like these to be available from C code (in case we import some from
// somewhere), so this has a C interface.
//
// The output will be correct when the log file is shared between multiple
// threads and/or multiple processes so long as the operating system
// supports O_APPEND. These calls have mutex-protected data structures
// and so are NOT reentrant. Do not use LOG in a signal handler.
//
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#ifdef ANDROID
#include <android/log.h>
#else
// These declarations are needed for our internal use even on non-Android builds.
// (they are borrowed from <android/log.h>)
/*
* Android log priority values, in ascending priority order.
*/
typedef enum android_LogPriority {
ANDROID_LOG_UNKNOWN = 0,
ANDROID_LOG_DEFAULT, /* only for SetMinPriority() */
ANDROID_LOG_VERBOSE,
ANDROID_LOG_DEBUG,
ANDROID_LOG_INFO,
ANDROID_LOG_WARN,
ANDROID_LOG_ERROR,
ANDROID_LOG_FATAL,
ANDROID_LOG_SILENT, /* only for SetMinPriority(); must be last */
} android_LogPriority;
/*
* Send a simple string to the log.
*/
int __android_log_write(int prio, const char *tag, const char *text);
/*
* Send a formatted string to the log, used like printf(fmt,...)
*/
int __android_log_print(int prio, const char *tag, const char *fmt, ...)
#if defined(__GNUC__)
__attribute__ ((format(printf, 3, 4)))
#endif
;
#endif
// ---------------------------------------------------------------------
/*
* Normally we strip FBLOGV (VERBOSE messages) from release builds.
* You can modify this (for example with "#define FBLOG_NDEBUG 0"
* at the top of your source file) to change that behavior.
*/
#ifndef FBLOG_NDEBUG
#ifdef NDEBUG
#define FBLOG_NDEBUG 1
#else
#define FBLOG_NDEBUG 0
#endif
#endif
/*
* This is the local tag used for the following simplified
* logging macros. You can change this preprocessor definition
* before using the other macros to change the tag.
*/
#ifndef LOG_TAG
#define LOG_TAG NULL
#endif
// ---------------------------------------------------------------------
/*
* Simplified macro to send a verbose log message using the current LOG_TAG.
*/
#ifndef FBLOGV
#if FBLOG_NDEBUG
#define FBLOGV(...) ((void)0)
#else
#define FBLOGV(...) ((void)FBLOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
#endif
#endif
#define CONDITION(cond) (__builtin_expect((cond)!=0, 0))
#ifndef FBLOGV_IF
#if FBLOG_NDEBUG
#define FBLOGV_IF(cond, ...) ((void)0)
#else
#define FBLOGV_IF(cond, ...) \
( (CONDITION(cond)) \
? ((void)FBLOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) \
: (void)0 )
#endif
#endif
/*
* Simplified macro to send a debug log message using the current LOG_TAG.
*/
#ifndef FBLOGD
#define FBLOGD(...) ((void)FBLOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__))
#endif
#ifndef FBLOGD_IF
#define FBLOGD_IF(cond, ...) \
( (CONDITION(cond)) \
? ((void)FBLOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)) \
: (void)0 )
#endif
/*
* Simplified macro to send an info log message using the current LOG_TAG.
*/
#ifndef FBLOGI
#define FBLOGI(...) ((void)FBLOG(LOG_INFO, LOG_TAG, __VA_ARGS__))
#endif
#ifndef FBLOGI_IF
#define FBLOGI_IF(cond, ...) \
( (CONDITION(cond)) \
? ((void)FBLOG(LOG_INFO, LOG_TAG, __VA_ARGS__)) \
: (void)0 )
#endif
/*
* Simplified macro to send a warning log message using the current LOG_TAG.
*/
#ifndef FBLOGW
#define FBLOGW(...) ((void)FBLOG(LOG_WARN, LOG_TAG, __VA_ARGS__))
#endif
#ifndef FBLOGW_IF
#define FBLOGW_IF(cond, ...) \
( (CONDITION(cond)) \
? ((void)FBLOG(LOG_WARN, LOG_TAG, __VA_ARGS__)) \
: (void)0 )
#endif
/*
* Simplified macro to send an error log message using the current LOG_TAG.
*/
#ifndef FBLOGE
#define FBLOGE(...) ((void)FBLOG(LOG_ERROR, LOG_TAG, __VA_ARGS__))
#endif
#ifndef FBLOGE_IF
#define FBLOGE_IF(cond, ...) \
( (CONDITION(cond)) \
? ((void)FBLOG(LOG_ERROR, LOG_TAG, __VA_ARGS__)) \
: (void)0 )
#endif
// ---------------------------------------------------------------------
/*
* Conditional based on whether the current LOG_TAG is enabled at
* verbose priority.
*/
#ifndef IF_FBLOGV
#if FBLOG_NDEBUG
#define IF_FBLOGV() if (false)
#else
#define IF_FBLOGV() IF_FBLOG(LOG_VERBOSE, LOG_TAG)
#endif
#endif
/*
* Conditional based on whether the current LOG_TAG is enabled at
* debug priority.
*/
#ifndef IF_FBLOGD
#define IF_FBLOGD() IF_FBLOG(LOG_DEBUG, LOG_TAG)
#endif
/*
* Conditional based on whether the current LOG_TAG is enabled at
* info priority.
*/
#ifndef IF_FBLOGI
#define IF_FBLOGI() IF_FBLOG(LOG_INFO, LOG_TAG)
#endif
/*
* Conditional based on whether the current LOG_TAG is enabled at
* warn priority.
*/
#ifndef IF_FBLOGW
#define IF_FBLOGW() IF_FBLOG(LOG_WARN, LOG_TAG)
#endif
/*
* Conditional based on whether the current LOG_TAG is enabled at
* error priority.
*/
#ifndef IF_FBLOGE
#define IF_FBLOGE() IF_FBLOG(LOG_ERROR, LOG_TAG)
#endif
// ---------------------------------------------------------------------
/*
* Log a fatal error. If the given condition fails, this stops program
* execution like a normal assertion, but also generating the given message.
* It is NOT stripped from release builds. Note that the condition test
* is -inverted- from the normal assert() semantics.
*/
#define FBLOG_ALWAYS_FATAL_IF(cond, ...) \
( (CONDITION(cond)) \
? ((void)fb_printAssert(#cond, LOG_TAG, __VA_ARGS__)) \
: (void)0 )
#define FBLOG_ALWAYS_FATAL(...) \
( ((void)fb_printAssert(NULL, LOG_TAG, __VA_ARGS__)) )
/*
* Versions of LOG_ALWAYS_FATAL_IF and LOG_ALWAYS_FATAL that
* are stripped out of release builds.
*/
#if FBLOG_NDEBUG
#define FBLOG_FATAL_IF(cond, ...) ((void)0)
#define FBLOG_FATAL(...) ((void)0)
#else
#define FBLOG_FATAL_IF(cond, ...) FBLOG_ALWAYS_FATAL_IF(cond, __VA_ARGS__)
#define FBLOG_FATAL(...) FBLOG_ALWAYS_FATAL(__VA_ARGS__)
#endif
/*
* Assertion that generates a log message when the assertion fails.
* Stripped out of release builds. Uses the current LOG_TAG.
*/
#define FBLOG_ASSERT(cond, ...) FBLOG_FATAL_IF(!(cond), __VA_ARGS__)
//#define LOG_ASSERT(cond) LOG_FATAL_IF(!(cond), "Assertion failed: " #cond)
// ---------------------------------------------------------------------
/*
* Basic log message macro.
*
* Example:
* FBLOG(LOG_WARN, NULL, "Failed with error %d", errno);
*
* The second argument may be NULL or "" to indicate the "global" tag.
*/
#ifndef FBLOG
#define FBLOG(priority, tag, ...) \
FBLOG_PRI(ANDROID_##priority, tag, __VA_ARGS__)
#endif
#ifndef FBLOG_BY_DELIMS
#define FBLOG_BY_DELIMS(priority, tag, delims, msg, ...) \
logPrintByDelims(ANDROID_##priority, tag, delims, msg, ##__VA_ARGS__)
#endif
/*
* Log macro that allows you to specify a number for the priority.
*/
#ifndef FBLOG_PRI
#define FBLOG_PRI(priority, tag, ...) \
fb_printLog(priority, tag, __VA_ARGS__)
#endif
/*
* Log macro that allows you to pass in a varargs ("args" is a va_list).
*/
#ifndef FBLOG_PRI_VA
#define FBLOG_PRI_VA(priority, tag, fmt, args) \
fb_vprintLog(priority, NULL, tag, fmt, args)
#endif
/*
* Conditional given a desired logging priority and tag.
*/
#ifndef IF_FBLOG
#define IF_FBLOG(priority, tag) \
if (fb_testLog(ANDROID_##priority, tag))
#endif
typedef void (*LogHandler)(int priority, const char* tag, const char* message);
void setLogHandler(LogHandler logHandler);
/*
* ===========================================================================
*
* The stuff in the rest of this file should not be used directly.
*/
int fb_printLog(int prio, const char *tag, const char *fmt, ...)
#if defined(__GNUC__)
__attribute__ ((format(printf, 3, 4)))
#endif
;
#define fb_vprintLog(prio, cond, tag, fmt...) \
__android_log_vprint(prio, tag, fmt)
#define fb_printAssert(cond, tag, fmt...) \
__android_log_assert(cond, tag, fmt)
#define fb_writeLog(prio, tag, text) \
__android_log_write(prio, tag, text)
#define fb_bWriteLog(tag, payload, len) \
__android_log_bwrite(tag, payload, len)
#define fb_btWriteLog(tag, type, payload, len) \
__android_log_btwrite(tag, type, payload, len)
#define fb_testLog(prio, tag) (1)
/*
* FB extensions
*/
void logPrintByDelims(int priority, const char* tag, const char* delims,
const char* msg, ...);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,100 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#include <fb/log.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#define LOG_BUFFER_SIZE 4096
static LogHandler gLogHandler;
void setLogHandler(LogHandler logHandler) {
gLogHandler = logHandler;
}
int fb_printLog(int prio, const char *tag, const char *fmt, ...) {
char logBuffer[LOG_BUFFER_SIZE];
va_list va_args;
va_start(va_args, fmt);
int result = vsnprintf(logBuffer, sizeof(logBuffer), fmt, va_args);
va_end(va_args);
if (gLogHandler != NULL) {
gLogHandler(prio, tag, logBuffer);
}
__android_log_write(prio, tag, logBuffer);
return result;
}
void logPrintByDelims(int priority, const char* tag, const char* delims,
const char* msg, ...)
{
va_list ap;
char buf[32768];
char* context;
char* tok;
va_start(ap, msg);
vsnprintf(buf, sizeof(buf), msg, ap);
va_end(ap);
tok = strtok_r(buf, delims, &context);
if (!tok) {
return;
}
do {
__android_log_write(priority, tag, tok);
} while ((tok = strtok_r(NULL, delims, &context)));
}
#ifndef ANDROID
// Implementations of the basic android logging functions for non-android platforms.
static char logTagChar(int prio) {
switch (prio) {
default:
case ANDROID_LOG_UNKNOWN:
case ANDROID_LOG_DEFAULT:
case ANDROID_LOG_SILENT:
return ' ';
case ANDROID_LOG_VERBOSE:
return 'V';
case ANDROID_LOG_DEBUG:
return 'D';
case ANDROID_LOG_INFO:
return 'I';
case ANDROID_LOG_WARN:
return 'W';
case ANDROID_LOG_ERROR:
return 'E';
case ANDROID_LOG_FATAL:
return 'F';
}
}
int __android_log_write(int prio, const char *tag, const char *text) {
return fprintf(stderr, "[%c/%.16s] %s\n", logTagChar(prio), tag, text);
}
int __android_log_print(int prio, const char *tag, const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
int res = fprintf(stderr, "[%c/%.16s] ", logTagChar(prio), tag);
res += vfprintf(stderr, "%s\n", ap);
va_end(ap);
return res;
}
#endif

View File

@ -0,0 +1,21 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#pragma once
namespace facebook {
struct noncopyable {
noncopyable(const noncopyable&) = delete;
noncopyable& operator=(const noncopyable&) = delete;
protected:
noncopyable() = default;
};
}

View File

@ -0,0 +1,21 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#pragma once
namespace facebook {
struct nonmovable {
nonmovable(nonmovable&&) = delete;
nonmovable& operator=(nonmovable&&) = delete;
protected:
nonmovable() = default;
};
}

View File

@ -0,0 +1,83 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
/** @file ALog.h
*
* Very simple android only logging. Define LOG_TAG to enable the macros.
*/
#pragma once
#ifdef __ANDROID__
#include <android/log.h>
namespace facebook {
namespace alog {
template<typename... ARGS>
inline void log(int level, const char* tag, const char* msg, ARGS... args) noexcept {
__android_log_print(level, tag, msg, args...);
}
template<typename... ARGS>
inline void log(int level, const char* tag, const char* msg) noexcept {
__android_log_write(level, tag, msg);
}
template<typename... ARGS>
inline void logv(const char* tag, const char* msg, ARGS... args) noexcept {
log(ANDROID_LOG_VERBOSE, tag, msg, args...);
}
template<typename... ARGS>
inline void logd(const char* tag, const char* msg, ARGS... args) noexcept {
log(ANDROID_LOG_DEBUG, tag, msg, args...);
}
template<typename... ARGS>
inline void logi(const char* tag, const char* msg, ARGS... args) noexcept {
log(ANDROID_LOG_INFO, tag, msg, args...);
}
template<typename... ARGS>
inline void logw(const char* tag, const char* msg, ARGS... args) noexcept {
log(ANDROID_LOG_WARN, tag, msg, args...);
}
template<typename... ARGS>
inline void loge(const char* tag, const char* msg, ARGS... args) noexcept {
log(ANDROID_LOG_ERROR, tag, msg, args...);
}
template<typename... ARGS>
inline void logf(const char* tag, const char* msg, ARGS... args) noexcept {
log(ANDROID_LOG_FATAL, tag, msg, args...);
}
#ifdef LOG_TAG
# define ALOGV(...) ::facebook::alog::logv(LOG_TAG, __VA_ARGS__)
# define ALOGD(...) ::facebook::alog::logd(LOG_TAG, __VA_ARGS__)
# define ALOGI(...) ::facebook::alog::logi(LOG_TAG, __VA_ARGS__)
# define ALOGW(...) ::facebook::alog::logw(LOG_TAG, __VA_ARGS__)
# define ALOGE(...) ::facebook::alog::loge(LOG_TAG, __VA_ARGS__)
# define ALOGF(...) ::facebook::alog::logf(LOG_TAG, __VA_ARGS__)
#endif
}}
#else
# define ALOGV(...) ((void)0)
# define ALOGD(...) ((void)0)
# define ALOGI(...) ((void)0)
# define ALOGW(...) ((void)0)
# define ALOGE(...) ((void)0)
# define ALOGF(...) ((void)0)
#endif

View File

@ -0,0 +1,35 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
Countable.cpp \
Environment.cpp \
fbjni.cpp \
jni_helpers.cpp \
LocalString.cpp \
OnLoad.cpp \
WeakReference.cpp \
fbjni/Exceptions.cpp \
fbjni/Hybrid.cpp \
fbjni/References.cpp
LOCAL_C_INCLUDES := $(LOCAL_PATH)/..
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/..
LOCAL_CFLAGS := -DLOG_TAG=\"fbjni\" -fexceptions -frtti
LOCAL_CFLAGS += -Wall -Werror
CXX11_FLAGS := -std=gnu++11
LOCAL_CFLAGS += $(CXX11_FLAGS)
LOCAL_EXPORT_CPPFLAGS := $(CXX11_FLAGS)
LOCAL_LDLIBS := -landroid
LOCAL_SHARED_LIBRARIES := libfb
LOCAL_MODULE := libfbjni
include $(BUILD_SHARED_LIBRARY)
$(call import-module,fb)

View File

@ -0,0 +1,69 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#include <cstdint>
#include <jni/Countable.h>
#include <jni/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 },
});
}
} }

View File

@ -0,0 +1,33 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#pragma once
#include <jni.h>
#include <fb/Countable.h>
#include <fb/RefPtr.h>
namespace facebook {
namespace jni {
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;
}
void setCountableForJava(JNIEnv* env, jobject obj, RefPtr<Countable>&& countable);
void CountableOnLoad(JNIEnv* env);
} }

View File

@ -0,0 +1,18 @@
PROJECT_NAME = "Facebook JNI"
PROJECT_BRIEF = "Helper library to provide safe and convenient access to JNI with very low overhead"
JAVADOC_AUTOBRIEF = YES
EXTRACT_ALL = YES
RECURSIVE = YES
EXCLUDE = tests Asserts.h Countable.h GlobalReference.h LocalReference.h LocalString.h Registration.h WeakReference.h jni_helpers.h Environment.h
EXCLUDE_PATTERNS = *-inl.h *.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
PREDEFINED = LOG_TAG=fbjni
EXAMPLE_PATH = samples
#ENABLED_SECTIONS = INTERNAL

View File

@ -0,0 +1,89 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#include <pthread.h>
#include <fb/log.h>
#include <fb/StaticInitialized.h>
#include <fb/ThreadLocal.h>
#include <jni/Environment.h>
namespace facebook {
namespace jni {
static StaticInitialized<ThreadLocal<JNIEnv>> g_env;
static JavaVM* g_vm = nullptr;
/* static */
JNIEnv* Environment::current() {
JNIEnv* env = g_env->get();
if ((env == nullptr) && (g_vm != nullptr)) {
if (g_vm->GetEnv((void**) &env, JNI_VERSION_1_6) != JNI_OK) {
FBLOGE("Error retrieving JNI Environment, thread is probably not attached to JVM");
env = nullptr;
} else {
g_env->reset(env);
}
}
return env;
}
/* static */
void Environment::detachCurrentThread() {
auto env = g_env->get();
if (env) {
FBASSERT(g_vm);
g_vm->DetachCurrentThread();
g_env->reset();
}
}
struct EnvironmentInitializer {
EnvironmentInitializer(JavaVM* vm) {
FBASSERT(!g_vm);
FBASSERT(vm);
g_vm = vm;
g_env.initialize([] (void*) {});
}
};
/* static */
void Environment::initialize(JavaVM* vm) {
static EnvironmentInitializer init(vm);
}
/* static */
JNIEnv* Environment::ensureCurrentThreadIsAttached() {
auto env = g_env->get();
if (!env) {
FBASSERT(g_vm);
g_vm->AttachCurrentThread(&env, nullptr);
g_env->reset(env);
}
return env;
}
ThreadScope::ThreadScope()
: attachedWithThisScope_(false) {
JNIEnv* env = nullptr;
if (g_vm->GetEnv((void**) &env, JNI_VERSION_1_6) != JNI_EDETACHED) {
return;
}
env = facebook::jni::Environment::ensureCurrentThreadIsAttached();
FBASSERT(env);
attachedWithThisScope_ = true;
}
ThreadScope::~ThreadScope() {
if (attachedWithThisScope_) {
Environment::detachCurrentThread();
}
}
} }

View File

@ -0,0 +1,61 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#pragma once
#include <string>
#include <jni.h>
namespace facebook {
namespace jni {
// Keeps a thread-local reference to the current thread's JNIEnv.
struct Environment {
// May be null if this thread isn't attached to the JVM
static JNIEnv* current();
static void initialize(JavaVM* vm);
static JNIEnv* ensureCurrentThreadIsAttached();
static void detachCurrentThread();
};
/**
* RAII Object that attaches a thread to the JVM. Failing to detach from a thread before it
* exits will cause a crash, as will calling Detach an extra time, and this guard class helps
* keep that straight. In addition, it remembers whether it performed the attach or not, so it
* is safe to nest it with itself or with non-fbjni code that manages the attachment correctly.
*
* Potential concerns:
* - Attaching to the JVM is fast (~100us on MotoG), but ideally you would attach while the
* app is not busy.
* - Having a thread detach at arbitrary points is not safe in Dalvik; you need to be sure that
* there is no Java code on the current stack or you run the risk of a crash like:
* ERROR: detaching thread with interp frames (count=18)
* (More detail at https://groups.google.com/forum/#!topic/android-ndk/2H8z5grNqjo)
* ThreadScope won't do a detach if the thread was already attached before the guard is
* instantiated, but there's probably some usage that could trip this up.
* - Newly attached C++ threads only get the bootstrap class loader -- i.e. java language
* classes, not any of our application's classes. This will be different behavior than threads
* that were initiated on the Java side. A workaround is to pass a global reference for a
* class or instance to the new thread; this bypasses the need for the class loader.
* (See http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/invocation.html#attach_current_thread)
*/
class ThreadScope {
public:
ThreadScope();
ThreadScope(ThreadScope&) = delete;
ThreadScope(ThreadScope&&) = default;
ThreadScope& operator=(ThreadScope&) = delete;
ThreadScope& operator=(ThreadScope&&) = delete;
~ThreadScope();
private:
bool attachedWithThisScope_;
};
} }

View File

@ -0,0 +1,91 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#pragma once
#include <memory>
#include <type_traits>
#include <jni.h>
#include <jni/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_;
};
}}

View File

@ -0,0 +1,37 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#pragma once
#include <memory>
#include <type_traits>
#include <jni.h>
#include <jni/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>>;
} }

View File

@ -0,0 +1,242 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#include <jni/LocalString.h>
#include <jni/Environment.h>
#include <fb/assert.h>
#include <vector>
namespace facebook {
namespace jni {
namespace {
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;
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) {
// 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;
}
}
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) {
const char* modified = env->GetStringUTFChars(str, NULL);
jsize length = env->GetStringUTFLength(str);
std::string s = detail::modifiedUTF8ToUTF8(reinterpret_cast<const uint8_t*>(modified), length);
env->ReleaseStringUTFChars(str, modified);
return s;
}
} }

View File

@ -0,0 +1,61 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#pragma once
#include <string>
#include <jni.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);
}
// 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 LocalString {
public:
// Assumes UTF8 encoding and make a required convertion 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;
};
// The string from JNI is converted to standard UTF-8 if the string contains supplementary
// characters.
std::string fromJString(JNIEnv* env, jstring str);
} }

View File

@ -0,0 +1,23 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#include <jni.h>
#include <fb/assert.h>
#include <jni/Countable.h>
#include <jni/Environment.h>
#include <jni/fbjni.h>
using namespace facebook::jni;
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
return facebook::jni::initialize(vm, [] {
CountableOnLoad(Environment::current());
HybridDataOnLoad();
});
}

View File

@ -0,0 +1,27 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#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);
}
} }

View File

@ -0,0 +1,43 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#include <jni/Environment.h>
#include <jni/WeakReference.h>
namespace facebook {
namespace jni {
WeakReference::WeakReference(jobject strongRef) :
m_weakReference(Environment::current()->NewWeakGlobalRef(strongRef))
{
}
WeakReference::~WeakReference() {
auto env = Environment::current();
FBASSERTMSGF(env, "Attempt to delete jni::WeakReference from non-JNI thread");
env->DeleteWeakGlobalRef(m_weakReference);
}
ResolvedWeakReference::ResolvedWeakReference(jobject weakRef) :
m_strongReference(Environment::current()->NewLocalRef(weakRef))
{
}
ResolvedWeakReference::ResolvedWeakReference(const RefPtr<WeakReference>& weakRef) :
m_strongReference(Environment::current()->NewLocalRef(weakRef->weakRef()))
{
}
ResolvedWeakReference::~ResolvedWeakReference() {
if (m_strongReference)
Environment::current()->DeleteLocalRef(m_strongReference);
}
} }

View File

@ -0,0 +1,53 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#pragma once
#include <string>
#include <jni.h>
#include <fb/noncopyable.h>
#include <fb/Countable.h>
namespace facebook {
namespace jni {
class 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 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;
};
} }

View File

@ -0,0 +1,206 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#include "fbjni.h"
#include <mutex>
#include <vector>
#include <jni/LocalString.h>
namespace facebook {
namespace jni {
template<typename... Args>
static void log(Args... args) {
// TODO (7623232) Migrate to glog
#ifdef __ANDROID__
facebook::alog::loge("fbjni", args...);
#endif
}
jint initialize(JavaVM* vm, void(*init_fn)()) noexcept {
static std::once_flag init_flag;
static auto failed = false;
std::call_once(init_flag, [vm] {
try {
Environment::initialize(vm);
internal::initExceptionHelpers();
} catch (std::exception& ex) {
log("Failed to initialize fbjni: %s", ex.what());
failed = true;
} catch (...) {
log("Failed to initialize fbjni");
failed = true;
}
});
if (failed) {
return JNI_ERR;
}
try {
init_fn();
} catch (...) {
translatePendingCppExceptionToJavaException();
// So Java will handle the translated exception, fall through and
// return a good version number.
}
return JNI_VERSION_1_6;
}
alias_ref<jclass> findClassStatic(const char* name) {
const auto env = internal::getEnv();
auto cls = env->FindClass(name);
FACEBOOK_JNI_THROW_EXCEPTION_IF(!cls);
auto leaking_ref = (jclass)env->NewGlobalRef(cls);
FACEBOOK_JNI_THROW_EXCEPTION_IF(!leaking_ref);
return wrap_alias(leaking_ref);
}
local_ref<jclass> findClassLocal(const char* name) {
const auto env = internal::getEnv();
auto cls = env->FindClass(name);
FACEBOOK_JNI_THROW_EXCEPTION_IF(!cls);
return adopt_local(cls);
}
// jstring /////////////////////////////////////////////////////////////////////////////////////////
std::string JObjectWrapper<jstring>::toStdString() const {
const auto env = internal::getEnv();
auto modified = env->GetStringUTFChars(self(), nullptr);
auto length = env->GetStringUTFLength(self());
auto string = detail::modifiedUTF8ToUTF8(reinterpret_cast<const uint8_t*>(modified), length);
env->ReleaseStringUTFChars(self(), modified);
return string;
}
local_ref<jstring> make_jstring(const char* utf8) {
if (!utf8) {
return {};
}
const auto env = internal::getEnv();
size_t len;
size_t modlen = detail::modifiedLength(reinterpret_cast<const uint8_t*>(utf8), &len);
jstring result;
if (modlen == len) {
// The only difference between utf8 and modifiedUTF8 is in encoding 4-byte UTF8 chars
// and '\0' that is encoded on 2 bytes.
//
// Since modifiedUTF8-encoded string can be no shorter than it's UTF8 conterpart we
// know that if those two strings are of the same length we don't need to do any
// conversion -> no 4-byte chars nor '\0'.
result = env->NewStringUTF(utf8);
} else {
auto modified = std::vector<char>(modlen + 1); // allocate extra byte for \0
detail::utf8ToModifiedUTF8(
reinterpret_cast<const uint8_t*>(utf8), len,
reinterpret_cast<uint8_t*>(modified.data()), modified.size());
result = env->NewStringUTF(modified.data());
}
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
return adopt_local(result);
}
// PinnedPrimitiveArray ///////////////////////////////////////////////////////////////////////////
// TODO(T7847300): Allow array to be specified as constant so that JNI_ABORT can be passed
// on release, as opposed to 0, which results in unnecessary copying.
#pragma push_macro("DEFINE_PRIMITIVE_METHODS")
#undef DEFINE_PRIMITIVE_METHODS
#define DEFINE_PRIMITIVE_METHODS(TYPE, NAME) \
template<> \
TYPE* PinnedPrimitiveArray<TYPE>::get() { \
FACEBOOK_JNI_THROW_EXCEPTION_IF(array_.get() == nullptr); \
const auto env = internal::getEnv(); \
elements_ = env->Get ## NAME ## ArrayElements( \
static_cast<TYPE ## Array>(array_.get()), &isCopy_); \
size_ = array_->size(); \
return elements_; \
} \
template<> \
void PinnedPrimitiveArray<TYPE>::release() { \
FACEBOOK_JNI_THROW_EXCEPTION_IF(array_.get() == nullptr); \
const auto env = internal::getEnv(); \
env->Release ## NAME ## ArrayElements( \
static_cast<TYPE ## Array>(array_.get()), elements_, 0); \
elements_ = nullptr; \
size_ = 0; \
}
DEFINE_PRIMITIVE_METHODS(jboolean, Boolean)
DEFINE_PRIMITIVE_METHODS(jbyte, Byte)
DEFINE_PRIMITIVE_METHODS(jchar, Char)
DEFINE_PRIMITIVE_METHODS(jshort, Short)
DEFINE_PRIMITIVE_METHODS(jint, Int)
DEFINE_PRIMITIVE_METHODS(jlong, Long)
DEFINE_PRIMITIVE_METHODS(jfloat, Float)
DEFINE_PRIMITIVE_METHODS(jdouble, Double)
#pragma pop_macro("DEFINE_PRIMITIVE_METHODS")
#define DEFINE_PRIMITIVE_ARRAY_UTILS(TYPE, NAME) \
\
local_ref<j ## TYPE ## Array> make_ ## TYPE ## _array(jsize size) { \
auto array = internal::getEnv()->New ## NAME ## Array(size); \
FACEBOOK_JNI_THROW_EXCEPTION_IF(!array); \
return adopt_local(array); \
} \
\
local_ref<j ## TYPE ## Array> JArray ## NAME::newArray(size_t count) { \
return make_ ## TYPE ## _array(count); \
} \
\
j ## TYPE* JArray ## NAME::getRegion(jsize start, jsize length, j ## TYPE* buf) { \
internal::getEnv()->Get ## NAME ## ArrayRegion(self(), start, length, buf); \
FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); \
return buf; \
} \
\
std::unique_ptr<j ## TYPE[]> JArray ## NAME::getRegion(jsize start, jsize length) { \
auto buf = std::unique_ptr<j ## TYPE[]>{new j ## TYPE[length]}; \
internal::getEnv()->Get ## NAME ## ArrayRegion(self(), start, length, buf.get()); \
FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); \
return buf; \
} \
\
void JArray ## NAME::setRegion(jsize start, jsize length, const j ## TYPE* buf) { \
internal::getEnv()->Set ## NAME ## ArrayRegion(self(), start, length, buf); \
FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); \
} \
\
PinnedPrimitiveArray<j ## TYPE> JArray ## NAME::pin() { \
return PinnedPrimitiveArray<j ## TYPE>{self()}; \
} \
DEFINE_PRIMITIVE_ARRAY_UTILS(boolean, Boolean)
DEFINE_PRIMITIVE_ARRAY_UTILS(byte, Byte)
DEFINE_PRIMITIVE_ARRAY_UTILS(char, Char)
DEFINE_PRIMITIVE_ARRAY_UTILS(short, Short)
DEFINE_PRIMITIVE_ARRAY_UTILS(int, Int)
DEFINE_PRIMITIVE_ARRAY_UTILS(long, Long)
DEFINE_PRIMITIVE_ARRAY_UTILS(float, Float)
DEFINE_PRIMITIVE_ARRAY_UTILS(double, Double)
// Internal debug /////////////////////////////////////////////////////////////////////////////////
namespace internal {
ReferenceStats g_reference_stats;
void facebook::jni::internal::ReferenceStats::reset() noexcept {
locals_deleted = globals_deleted = weaks_deleted = 0;
}
}
}}

View File

@ -0,0 +1,23 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#pragma once
#include <jni.h>
#include "Environment.h"
#include "ALog.h"
#include "fbjni/Common.h"
#include "fbjni/Exceptions.h"
#include "fbjni/ReferenceAllocators.h"
#include "fbjni/References.h"
#include "fbjni/Meta.h"
#include "fbjni/CoreClasses.h"
#include "fbjni/Hybrid.h"
#include "fbjni/Registration.h"

View File

@ -0,0 +1,68 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
/** @file Common.h
*
* Defining the stuff that don't deserve headers of their own...
*/
#pragma once
#include <jni.h>
#include <jni/Environment.h>
#include <jni/ALog.h>
/// @cond INTERNAL
namespace facebook {
namespace jni {
/**
* 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.
*/
jint initialize(JavaVM*, 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
#if defined(__ANDROID__) && defined(FBJNI_DEBUG_REFS)
template<typename... Args>
inline void dbglog(Args... args) noexcept {
facebook::alog::logv("fbjni_ref", args...);
}
#else
template<typename... Args>
inline void dbglog(Args...) noexcept {}
#endif
}}}
/// @endcond

View File

@ -0,0 +1,443 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#pragma once
#include <string.h>
#include <type_traits>
#include "Common.h"
#include "Exceptions.h"
namespace facebook {
namespace jni {
inline bool isSameObject(alias_ref<jobject> lhs, alias_ref<jobject> rhs) noexcept {
return internal::getEnv()->IsSameObject(lhs.get(), rhs.get()) != JNI_FALSE;
}
// jobject /////////////////////////////////////////////////////////////////////////////////////////
inline JObjectWrapper<jobject>::JObjectWrapper(jobject reference) noexcept
: this_{reference}
{}
inline JObjectWrapper<jobject>::JObjectWrapper(const JObjectWrapper<jobject>& other) noexcept
: this_{other.this_} {
internal::dbglog("wrapper copy from this=%p ref=%p other=%p", this, other.this_, &other);
}
inline local_ref<jclass> JObjectWrapper<jobject>::getClass() const noexcept {
return adopt_local(internal::getEnv()->GetObjectClass(self()));
}
inline bool JObjectWrapper<jobject>::isInstanceOf(alias_ref<jclass> cls) const noexcept {
return internal::getEnv()->IsInstanceOf(self(), cls.get()) != JNI_FALSE;
}
template<typename T>
inline T JObjectWrapper<jobject>::getFieldValue(JField<T> field) const noexcept {
return field.get(self());
}
template<typename T>
inline local_ref<T*> JObjectWrapper<jobject>::getFieldValue(JField<T*> field) noexcept {
return adopt_local(field.get(self()));
}
template<typename T>
inline void JObjectWrapper<jobject>::setFieldValue(JField<T> field, T value) noexcept {
field.set(self(), value);
}
inline std::string JObjectWrapper<jobject>::toString() const {
static auto method = findClassLocal("java/lang/Object")->getMethod<jstring()>("toString");
return method(self())->toStdString();
}
inline void JObjectWrapper<jobject>::set(jobject reference) noexcept {
this_ = reference;
}
inline jobject JObjectWrapper<jobject>::get() const noexcept {
return this_;
}
inline jobject JObjectWrapper<jobject>::self() const noexcept {
return this_;
}
inline void swap(JObjectWrapper<jobject>& a, JObjectWrapper<jobject>& b) noexcept {
using std::swap;
swap(a.this_, b.this_);
}
// 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> JObjectWrapper<jclass>::getSuperclass() const noexcept {
return adopt_local(internal::getEnv()->GetSuperclass(self()));
}
inline void JObjectWrapper<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 JObjectWrapper<jclass>::isAssignableFrom(alias_ref<jclass> other) const noexcept {
const auto env = internal::getEnv();
const auto result = env->IsAssignableFrom(self(), other.get());
return result;
}
template<typename F>
inline JConstructor<F> JObjectWrapper<jclass>::getConstructor() const {
return getConstructor<F>(jmethod_traits<F>::constructor_descriptor().c_str());
}
template<typename F>
inline JConstructor<F> JObjectWrapper<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> JObjectWrapper<jclass>::getMethod(const char* name) const {
return getMethod<F>(name, jmethod_traits<F>::descriptor().c_str());
}
template<typename F>
inline JMethod<F> JObjectWrapper<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> JObjectWrapper<jclass>::getStaticMethod(const char* name) const {
return getStaticMethod<F>(name, jmethod_traits<F>::descriptor().c_str());
}
template<typename F>
inline JStaticMethod<F> JObjectWrapper<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> JObjectWrapper<jclass>::getNonvirtualMethod(const char* name) const {
return getNonvirtualMethod<F>(name, jmethod_traits<F>::descriptor().c_str());
}
template<typename F>
inline JNonvirtualMethod<F> JObjectWrapper<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>>
JObjectWrapper<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>> JObjectWrapper<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>> JObjectWrapper<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>> JObjectWrapper<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 JObjectWrapper<jclass>::getStaticFieldValue(JStaticField<T> field) const noexcept {
return field.get(self());
}
template<typename T>
inline local_ref<T*> JObjectWrapper<jclass>::getStaticFieldValue(JStaticField<T*> field) noexcept {
return adopt_local(field.get(self()));
}
template<typename T>
inline void JObjectWrapper<jclass>::setStaticFieldValue(JStaticField<T> field, T value) noexcept {
field.set(self(), value);
}
template<typename R, typename... Args>
inline local_ref<R> JObjectWrapper<jclass>::newObject(
JConstructor<R(Args...)> constructor,
Args... args) const {
const auto env = internal::getEnv();
auto object = env->NewObject(self(), constructor.getId(), args...);
FACEBOOK_JNI_THROW_EXCEPTION_IF(!object);
return adopt_local(static_cast<R>(object));
}
inline jclass JObjectWrapper<jclass>::self() const noexcept {
return static_cast<jclass>(this_);
}
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());
}
inline jstring JObjectWrapper<jstring>::self() const noexcept {
return static_cast<jstring>(this_);
}
// jthrowable //////////////////////////////////////////////////////////////////////////////////////
inline jthrowable JObjectWrapper<jthrowable>::self() const noexcept {
return static_cast<jthrowable>(this_);
}
// jtypeArray //////////////////////////////////////////////////////////////////////////////////////
template<typename T>
inline ElementProxy<T>::ElementProxy(
JObjectWrapper<_jtypeArray<T>*>* target,
size_t idx)
: target_{target}, idx_{idx} {}
template<typename T>
inline ElementProxy<T>& ElementProxy<T>::operator=(const T& o) {
target_->setElement(idx_, o);
return *this;
}
template<typename T>
inline ElementProxy<T>& ElementProxy<T>::operator=(alias_ref<T>& o) {
target_->setElement(idx_, o.get());
return *this;
}
template<typename T>
inline ElementProxy<T>& ElementProxy<T>::operator=(alias_ref<T>&& o) {
target_->setElement(idx_, o.get());
return *this;
}
template<typename T>
inline ElementProxy<T>& ElementProxy<T>::operator=(const ElementProxy<T>& o) {
auto src = o.target_->getElement(o.idx_);
target_->setElement(idx_, src.get());
return *this;
}
template<typename T>
inline ElementProxy<T>::ElementProxy::operator const local_ref<T> () const {
return target_->getElement(idx_);
}
template<typename T>
inline ElementProxy<T>::ElementProxy::operator local_ref<T> () {
return target_->getElement(idx_);
}
template<typename T>
local_ref<jtypeArray<T>> JObjectWrapper<jtypeArray<T>>::newArray(size_t size) {
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<jtypeArray<T>>(rawArray));
}
template<typename T>
inline void JObjectWrapper<jtypeArray<T>>::setElement(size_t idx, const T& value) {
const auto env = internal::getEnv();
env->SetObjectArrayElement(static_cast<jobjectArray>(self()), idx, value);
}
template<typename T>
inline local_ref<T> JObjectWrapper<jtypeArray<T>>::getElement(size_t idx) {
const auto env = internal::getEnv();
auto rawElement = env->GetObjectArrayElement(static_cast<jobjectArray>(self()), idx);
return adopt_local(static_cast<T>(rawElement));
}
template<typename T>
inline size_t JObjectWrapper<jtypeArray<T>>::size() {
const auto env = internal::getEnv();
return env->GetArrayLength(static_cast<jobjectArray>(self()));
}
template<typename T>
inline ElementProxy<T> JObjectWrapper<jtypeArray<T>>::operator[](size_t index) {
return ElementProxy<T>(this, index);
}
template<typename T>
inline jtypeArray<T> JObjectWrapper<jtypeArray<T>>::self() const noexcept {
return static_cast<jtypeArray<T>>(this_);
}
// jarray /////////////////////////////////////////////////////////////////////////////////////////
inline size_t JObjectWrapper<jarray>::size() const noexcept {
const auto env = internal::getEnv();
return env->GetArrayLength(self());
}
inline jarray JObjectWrapper<jarray>::self() const noexcept {
return static_cast<jarray>(this_);
}
// PinnedPrimitiveArray ///////////////////////////////////////////////////////////////////////////
template<typename T>
inline PinnedPrimitiveArray<T>::PinnedPrimitiveArray(alias_ref<jarray> array) noexcept
: array_{array} {
get();
}
template<typename T>
PinnedPrimitiveArray<T>::PinnedPrimitiveArray(PinnedPrimitiveArray&& o) noexcept {
array_ = std::move(o.array_);
elements_ = o.elements_;
isCopy_ = o.isCopy_;
size_ = o.size_;
o.elements_ = nullptr;
o.isCopy_ = false;
o.size_ = 0;
}
template<typename T>
PinnedPrimitiveArray<T>&
PinnedPrimitiveArray<T>::operator=(PinnedPrimitiveArray&& o) noexcept {
array_ = std::move(o.array_);
elements_ = o.elements_;
isCopy_ = o.isCopy_;
size_ = o.size_;
o.elements_ = nullptr;
o.isCopy_ = false;
o.size_ = 0;
return *this;
}
template<typename T>
inline T& PinnedPrimitiveArray<T>::operator[](size_t index) {
FACEBOOK_JNI_THROW_EXCEPTION_IF(elements_ == nullptr);
return elements_[index];
}
template<typename T>
inline bool PinnedPrimitiveArray<T>::isCopy() const noexcept {
return isCopy_ == JNI_TRUE;
}
template<typename T>
inline size_t PinnedPrimitiveArray<T>::size() const noexcept {
return size_;
}
template<typename T>
inline PinnedPrimitiveArray<T>::~PinnedPrimitiveArray() noexcept {
if (elements_) {
release();
}
}
#pragma push_macro("DECLARE_PRIMITIVE_METHODS")
#undef DECLARE_PRIMITIVE_METHODS
#define DECLARE_PRIMITIVE_METHODS(TYPE, NAME) \
template<> TYPE* PinnedPrimitiveArray<TYPE>::get(); \
template<> void PinnedPrimitiveArray<TYPE>::release(); \
DECLARE_PRIMITIVE_METHODS(jboolean, Boolean)
DECLARE_PRIMITIVE_METHODS(jbyte, Byte)
DECLARE_PRIMITIVE_METHODS(jchar, Char)
DECLARE_PRIMITIVE_METHODS(jshort, Short)
DECLARE_PRIMITIVE_METHODS(jint, Int)
DECLARE_PRIMITIVE_METHODS(jlong, Long)
DECLARE_PRIMITIVE_METHODS(jfloat, Float)
DECLARE_PRIMITIVE_METHODS(jdouble, Double)
#pragma pop_macro("DECLARE_PRIMITIVE_METHODS")
template<typename T, typename Base>
inline alias_ref<jclass> JavaClass<T, Base>::javaClassStatic() {
static auto cls = findClassStatic(
std::string(T::kJavaDescriptor + 1, strlen(T::kJavaDescriptor) - 2).c_str());
return cls;
}
template<typename T, typename Base>
inline local_ref<jclass> JavaClass<T, Base>::javaClassLocal() {
std::string className(T::kJavaDescriptor + 1, strlen(T::kJavaDescriptor) - 2);
return findClassLocal(className.c_str());
}
}}

View File

@ -0,0 +1,510 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#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 conveniance.
*/
#include "Meta.h"
#include "References.h"
#include <memory>
#include <jni.h>
namespace facebook {
namespace jni {
/// 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
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
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.
bool isSameObject(alias_ref<jobject> lhs, alias_ref<jobject> rhs) noexcept;
/// Wrapper to provide functionality to jobject references
template<>
class JObjectWrapper<jobject> {
public:
/// Java type descriptor
static constexpr const char* kJavaDescriptor = "Ljava/lang/Object;";
static constexpr const char* get_instantiated_java_descriptor() { return nullptr; }
/// Wrap an existing JNI reference
JObjectWrapper(jobject reference = nullptr) noexcept;
// Copy constructor
JObjectWrapper(const JObjectWrapper& other) noexcept;
/// 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) 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;
protected:
jobject this_;
private:
template<typename T, typename A>
friend class base_owned_ref;
template<typename T>
friend class alias_ref;
friend void swap(JObjectWrapper<jobject>& a, JObjectWrapper<jobject>& b) noexcept;
void set(jobject reference) noexcept;
jobject get() const noexcept;
jobject self() const noexcept;
};
using JObject = JObjectWrapper<jobject>;
void swap(JObjectWrapper<jobject>& a, JObjectWrapper<jobject>& b) noexcept;
/// Wrapper to provide functionality to jclass references
struct NativeMethod;
template<>
class JObjectWrapper<jclass> : public JObjectWrapper<jobject> {
public:
/// Java type descriptor
static constexpr const char* kJavaDescriptor = "Ljava/lang/Class;";
using JObjectWrapper<jobject>::JObjectWrapper;
/// 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),
/// });
///
/// 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.
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;
};
using JClass = JObjectWrapper<jclass>;
// 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
template<>
class JObjectWrapper<jstring> : public JObjectWrapper<jobject> {
public:
/// Java type descriptor
static constexpr const char* kJavaDescriptor = "Ljava/lang/String;";
using JObjectWrapper<jobject>::JObjectWrapper;
/// Convenience method to convert a jstring object to a std::string
std::string toStdString() const;
private:
jstring self() const noexcept;
};
/// Convenience functions to convert a std::string or const char* into a @ref local_ref to a
/// jstring
local_ref<jstring> make_jstring(const char* modifiedUtf8);
local_ref<jstring> make_jstring(const std::string& modifiedUtf8);
using JString = JObjectWrapper<jstring>;
/// Wrapper to provide functionality to jthrowable references
template<>
class JObjectWrapper<jthrowable> : public JObjectWrapper<jobject> {
public:
/// Java type descriptor
static constexpr const char* kJavaDescriptor = "Ljava/lang/Throwable;";
using JObjectWrapper<jobject>::JObjectWrapper;
private:
jthrowable self() const noexcept;
};
/// @cond INTERNAL
template<class T> class _jtypeArray : public _jobjectArray {};
// @endcond
/// Wrapper to provide functionality for arrays of j-types
template<class T> using jtypeArray = _jtypeArray<T>*;
template<typename T>
class ElementProxy {
private:
JObjectWrapper<_jtypeArray<T>*>* target_;
size_t idx_;
public:
ElementProxy(JObjectWrapper<_jtypeArray<T>*>* target, size_t idx);
ElementProxy<T>& operator=(const T& o);
ElementProxy<T>& operator=(alias_ref<T>& o);
ElementProxy<T>& operator=(alias_ref<T>&& o);
ElementProxy<T>& operator=(const ElementProxy<T>& o);
operator const local_ref<T> () const;
operator local_ref<T> ();
};
template<typename T>
class JObjectWrapper<jtypeArray<T>> : public JObjectWrapper<jobject> {
public:
static constexpr const char* kJavaDescriptor = nullptr;
static std::string get_instantiated_java_descriptor() {
return "[" + jtype_traits<T>::descriptor();
};
using JObjectWrapper<jobject>::JObjectWrapper;
/// 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<jtypeArray<T>> 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);
/// Get the size of the array.
size_t size();
/// 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.
ElementProxy<T> operator[](size_t idx);
private:
jtypeArray<T> self() const noexcept;
};
template <class T>
using JArrayClass = JObjectWrapper<jtypeArray<T>>;
template<typename T>
local_ref<jtypeArray<T>> adopt_local_array(jobjectArray ref) {
return adopt_local(static_cast<jtypeArray<T>>(ref));
}
template<typename T>
local_ref<T> adopt_local(ElementProxy<T> elementProxy) {
return static_cast<local_ref<T>>(elementProxy);
}
/// 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<>
class JObjectWrapper<jarray> : public JObjectWrapper<jobject> {
public:
static constexpr const char* kJavaDescriptor = nullptr;
using JObjectWrapper<jobject>::JObjectWrapper;
size_t size() const noexcept;
private:
jarray self() const noexcept;
};
using JArray = JObjectWrapper<jarray>;
template <typename T>
class PinnedPrimitiveArray;
#pragma push_macro("DECLARE_PRIMITIVE_ARRAY_UTILS")
#undef DECLARE_PRIMITIVE_ARRAY_UTILS
#define DECLARE_PRIMITIVE_ARRAY_UTILS(TYPE, NAME, DESC) \
\
local_ref<j ## TYPE ## Array> make_ ## TYPE ## _array(jsize size); \
\
template<> class JObjectWrapper<j ## TYPE ## Array> : public JArray { \
public: \
static constexpr const char* kJavaDescriptor = "[" # DESC; \
\
using JArray::JArray; \
\
static local_ref<j ## TYPE ## Array> newArray(size_t count); \
\
j ## TYPE* getRegion(jsize start, jsize length, j ## TYPE* buf); \
std::unique_ptr<j ## TYPE[]> getRegion(jsize start, jsize length); \
void setRegion(jsize start, jsize length, const j ## TYPE* buf); \
PinnedPrimitiveArray<j ## TYPE> pin(); \
\
private: \
j ## TYPE ## Array self() const noexcept { \
return static_cast<j ## TYPE ## Array>(this_); \
} \
}; \
\
using JArray ## NAME = JObjectWrapper<j ## TYPE ## Array> \
DECLARE_PRIMITIVE_ARRAY_UTILS(boolean, Boolean, "Z");
DECLARE_PRIMITIVE_ARRAY_UTILS(byte, Byte, "B");
DECLARE_PRIMITIVE_ARRAY_UTILS(char, Char, "C");
DECLARE_PRIMITIVE_ARRAY_UTILS(short, Short, "S");
DECLARE_PRIMITIVE_ARRAY_UTILS(int, Int, "I");
DECLARE_PRIMITIVE_ARRAY_UTILS(long, Long, "J");
DECLARE_PRIMITIVE_ARRAY_UTILS(float, Float, "F");
DECLARE_PRIMITIVE_ARRAY_UTILS(double, Double, "D");
#pragma pop_macro("DECLARE_PRIMITIVE_ARRAY_UTILS")
/// 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() function. 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>
class PinnedPrimitiveArray {
public:
static_assert(is_jni_primitive<T>::value,
"PinnedPrimitiveArray requires primitive jni type.");
PinnedPrimitiveArray(PinnedPrimitiveArray&&) noexcept;
PinnedPrimitiveArray(const PinnedPrimitiveArray&) = delete;
~PinnedPrimitiveArray() noexcept;
PinnedPrimitiveArray& operator=(PinnedPrimitiveArray&&) noexcept;
PinnedPrimitiveArray& operator=(const PinnedPrimitiveArray&) = delete;
T* get();
void release();
const T& operator[](size_t index) const;
T& operator[](size_t index);
bool isCopy() const noexcept;
size_t size() const noexcept;
private:
alias_ref<jarray> array_;
T* elements_;
jboolean isCopy_;
size_t size_;
PinnedPrimitiveArray(alias_ref<jarray>) noexcept;
friend class JObjectWrapper<jbooleanArray>;
friend class JObjectWrapper<jbyteArray>;
friend class JObjectWrapper<jcharArray>;
friend class JObjectWrapper<jshortArray>;
friend class JObjectWrapper<jintArray>;
friend class JObjectWrapper<jlongArray>;
friend class JObjectWrapper<jfloatArray>;
friend class JObjectWrapper<jdoubleArray>;
};
namespace detail {
class BaseJavaClass {
public:
typedef _jobject _javaobject;
typedef _javaobject* javaobject;
};
}
// 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;";
// };
//
// alias_ref<MyClass::javaobject> myClass = foo();
template <typename T, typename Base = detail::BaseJavaClass>
class JavaClass {
public:
// JNI pattern for jobject assignable pointer
struct _javaobject : public Base::_javaobject {
typedef T javaClass;
};
typedef _javaobject* javaobject;
static alias_ref<jclass> javaClassStatic();
static local_ref<jclass> javaClassLocal();
};
template <typename T>
class JObjectWrapper<T,
typename std::enable_if<
is_plain_jni_reference<T>::value &&
std::is_class<typename std::remove_pointer<T>::type::javaClass>::value
>::type>
: public JObjectWrapper<jobject> {
public:
static constexpr const char* kJavaDescriptor =
std::remove_pointer<T>::type::javaClass::kJavaDescriptor;
using JObjectWrapper<jobject>::JObjectWrapper;
template<typename U>
JObjectWrapper(const JObjectWrapper<U>& w)
: JObjectWrapper<jobject>(w) {
static_assert(std::is_convertible<U, T>::value,
"U must be convertible to T");
}
};
}}
#include "CoreClasses-inl.h"
// This is here because code in Meta-inl.h uses alias_ref, which
// requires JObjectWrapper<jobject> to be concrete before it can work.
#include "Meta-inl.h"

View File

@ -0,0 +1,399 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#include "Exceptions.h"
#include "CoreClasses.h"
#include <jni/ALog.h>
#include <fb/assert.h>
#include <alloca.h>
#include <cstdlib>
#include <ios>
#include <stdexcept>
#include <stdio.h>
#include <string>
#include <system_error>
#include <jni.h>
namespace facebook {
namespace jni {
// CommonJniExceptions /////////////////////////////////////////////////////////////////////////////
class CommonJniExceptions {
public:
static void init();
static jclass getThrowableClass() {
return throwableClass_;
}
static jclass getUnknownCppExceptionClass() {
return unknownCppExceptionClass_;
}
static jthrowable getUnknownCppExceptionObject() {
return unknownCppExceptionObject_;
}
static jthrowable getRuntimeExceptionObject() {
return runtimeExceptionObject_;
}
private:
static jclass throwableClass_;
static jclass unknownCppExceptionClass_;
static jthrowable unknownCppExceptionObject_;
static jthrowable runtimeExceptionObject_;
};
// The variables in this class are all JNI global references and are intentionally leaked because
// we assume this library cannot be unloaded. These global references are created manually instead
// of using global_ref from References.h to avoid circular dependency.
jclass CommonJniExceptions::throwableClass_ = nullptr;
jclass CommonJniExceptions::unknownCppExceptionClass_ = nullptr;
jthrowable CommonJniExceptions::unknownCppExceptionObject_ = nullptr;
jthrowable CommonJniExceptions::runtimeExceptionObject_ = nullptr;
// Variable to guarantee that fallback exceptions have been initialized early. We don't want to
// do pure dynamic initialization -- we want to warn programmers early that they need to run the
// helpers at library load time instead of lazily getting them when the exception helpers are
// first used.
static std::atomic<bool> gIsInitialized(false);
void CommonJniExceptions::init() {
JNIEnv* env = internal::getEnv();
FBASSERTMSGF(env, "Could not get JNI Environment");
// Throwable class
jclass localThrowableClass = env->FindClass("java/lang/Throwable");
FBASSERT(localThrowableClass);
throwableClass_ = static_cast<jclass>(env->NewGlobalRef(localThrowableClass));
FBASSERT(throwableClass_);
env->DeleteLocalRef(localThrowableClass);
// UnknownCppException class
jclass localUnknownCppExceptionClass = env->FindClass("com/facebook/jni/UnknownCppException");
FBASSERT(localUnknownCppExceptionClass);
jmethodID unknownCppExceptionConstructorMID = env->GetMethodID(
localUnknownCppExceptionClass,
"<init>",
"()V");
FBASSERT(unknownCppExceptionConstructorMID);
unknownCppExceptionClass_ = static_cast<jclass>(env->NewGlobalRef(localUnknownCppExceptionClass));
FBASSERT(unknownCppExceptionClass_);
env->DeleteLocalRef(localUnknownCppExceptionClass);
// UnknownCppException object
jthrowable localUnknownCppExceptionObject = static_cast<jthrowable>(env->NewObject(
unknownCppExceptionClass_,
unknownCppExceptionConstructorMID));
FBASSERT(localUnknownCppExceptionObject);
unknownCppExceptionObject_ = static_cast<jthrowable>(env->NewGlobalRef(
localUnknownCppExceptionObject));
FBASSERT(unknownCppExceptionObject_);
env->DeleteLocalRef(localUnknownCppExceptionObject);
// RuntimeException object
jclass localRuntimeExceptionClass = env->FindClass("java/lang/RuntimeException");
FBASSERT(localRuntimeExceptionClass);
jmethodID runtimeExceptionConstructorMID = env->GetMethodID(
localRuntimeExceptionClass,
"<init>",
"()V");
FBASSERT(runtimeExceptionConstructorMID);
jthrowable localRuntimeExceptionObject = static_cast<jthrowable>(env->NewObject(
localRuntimeExceptionClass,
runtimeExceptionConstructorMID));
FBASSERT(localRuntimeExceptionObject);
runtimeExceptionObject_ = static_cast<jthrowable>(env->NewGlobalRef(localRuntimeExceptionObject));
FBASSERT(runtimeExceptionObject_);
env->DeleteLocalRef(localRuntimeExceptionClass);
env->DeleteLocalRef(localRuntimeExceptionObject);
}
// initExceptionHelpers() //////////////////////////////////////////////////////////////////////////
void internal::initExceptionHelpers() {
CommonJniExceptions::init();
gIsInitialized.store(true, std::memory_order_seq_cst);
}
void assertIfExceptionsNotInitialized() {
// Use relaxed memory order because we don't need memory barriers.
// The real init-once enforcement is done by the compiler for the
// "static" in initExceptionHelpers.
FBASSERTMSGF(gIsInitialized.load(std::memory_order_relaxed),
"initExceptionHelpers was never called!");
}
// Exception throwing & translating functions //////////////////////////////////////////////////////
// Functions that throw Java exceptions
namespace {
void setJavaExceptionAndAbortOnFailure(jthrowable throwable) noexcept {
assertIfExceptionsNotInitialized();
JNIEnv* env = internal::getEnv();
if (throwable) {
env->Throw(throwable);
}
if (env->ExceptionCheck() != JNI_TRUE) {
std::abort();
}
}
void setDefaultException() noexcept {
assertIfExceptionsNotInitialized();
setJavaExceptionAndAbortOnFailure(CommonJniExceptions::getRuntimeExceptionObject());
}
void setCppSystemErrorExceptionInJava(const std::system_error& ex) noexcept {
assertIfExceptionsNotInitialized();
JNIEnv* env = internal::getEnv();
jclass cppSystemErrorExceptionClass = env->FindClass(
"com/facebook/jni/CppSystemErrorException");
if (!cppSystemErrorExceptionClass) {
setDefaultException();
return;
}
jmethodID constructorMID = env->GetMethodID(
cppSystemErrorExceptionClass,
"<init>",
"(Ljava/lang/String;I)V");
if (!constructorMID) {
setDefaultException();
return;
}
jthrowable cppSystemErrorExceptionObject = static_cast<jthrowable>(env->NewObject(
cppSystemErrorExceptionClass,
constructorMID,
env->NewStringUTF(ex.what()),
ex.code().value()));
setJavaExceptionAndAbortOnFailure(cppSystemErrorExceptionObject);
}
template<typename... ARGS>
void setNewJavaException(jclass exceptionClass, const char* fmt, ARGS... args) {
assertIfExceptionsNotInitialized();
int msgSize = snprintf(nullptr, 0, fmt, args...);
JNIEnv* env = internal::getEnv();
try {
char *msg = (char*) alloca(msgSize);
snprintf(msg, kMaxExceptionMessageBufferSize, fmt, args...);
env->ThrowNew(exceptionClass, msg);
} catch (...) {
env->ThrowNew(exceptionClass, "");
}
if (env->ExceptionCheck() != JNI_TRUE) {
setDefaultException();
}
}
void setNewJavaException(jclass exceptionClass, const char* msg) {
assertIfExceptionsNotInitialized();
setNewJavaException(exceptionClass, "%s", msg);
}
template<typename... ARGS>
void setNewJavaException(const char* className, const char* fmt, ARGS... args) {
assertIfExceptionsNotInitialized();
JNIEnv* env = internal::getEnv();
jclass exceptionClass = env->FindClass(className);
if (env->ExceptionCheck() != JNI_TRUE && !exceptionClass) {
// If FindClass() has failed but no exception has been thrown, throw a default exception.
setDefaultException();
return;
}
setNewJavaException(exceptionClass, fmt, args...);
}
}
// Functions that throw C++ exceptions
// TODO(T6618159) Take a stack dump here to save context if it results in a crash when propagated
void throwPendingJniExceptionAsCppException() {
assertIfExceptionsNotInitialized();
JNIEnv* env = internal::getEnv();
if (env->ExceptionCheck() == JNI_FALSE) {
return;
}
jthrowable throwable = env->ExceptionOccurred();
if (!throwable) {
throw std::runtime_error("Unable to get pending JNI exception.");
}
env->ExceptionClear();
throw JniException(throwable);
}
void throwCppExceptionIf(bool condition) {
assertIfExceptionsNotInitialized();
if (!condition) {
return;
}
JNIEnv* env = internal::getEnv();
if (env->ExceptionCheck() == JNI_TRUE) {
throwPendingJniExceptionAsCppException();
return;
}
throw JniException();
}
void throwNewJavaException(jthrowable throwable) {
throw JniException(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());
}
// Translate C++ to Java Exception
void translatePendingCppExceptionToJavaException() noexcept {
assertIfExceptionsNotInitialized();
try {
try {
throw;
} catch(const JniException& ex) {
ex.setJavaException();
} catch(const std::ios_base::failure& ex) {
setNewJavaException("java/io/IOException", ex.what());
} catch(const std::bad_alloc& ex) {
setNewJavaException("java/lang/OutOfMemoryError", ex.what());
} catch(const std::out_of_range& ex) {
setNewJavaException("java/lang/ArrayIndexOutOfBoundsException", ex.what());
} catch(const std::system_error& ex) {
setCppSystemErrorExceptionInJava(ex);
} catch(const std::runtime_error& ex) {
setNewJavaException("java/lang/RuntimeException", ex.what());
} catch(const std::exception& ex) {
setNewJavaException("com/facebook/jni/CppException", ex.what());
} catch(const char* msg) {
setNewJavaException(CommonJniExceptions::getUnknownCppExceptionClass(), msg);
} catch(...) {
setJavaExceptionAndAbortOnFailure(CommonJniExceptions::getUnknownCppExceptionObject());
}
} catch(...) {
// This block aborts the program, if something bad happens when handling exceptions, thus
// keeping this function noexcept.
std::abort();
}
}
// JniException ////////////////////////////////////////////////////////////////////////////////////
const std::string JniException::kExceptionMessageFailure_ = "Unable to get exception message.";
JniException::JniException() : JniException(CommonJniExceptions::getRuntimeExceptionObject()) { }
JniException::JniException(jthrowable throwable) : isMessageExtracted_(false) {
assertIfExceptionsNotInitialized();
throwableGlobalRef_ = static_cast<jthrowable>(internal::getEnv()->NewGlobalRef(throwable));
if (!throwableGlobalRef_) {
throw std::bad_alloc();
}
}
JniException::JniException(JniException &&rhs)
: throwableGlobalRef_(std::move(rhs.throwableGlobalRef_)),
what_(std::move(rhs.what_)),
isMessageExtracted_(rhs.isMessageExtracted_) {
rhs.throwableGlobalRef_ = nullptr;
}
JniException::JniException(const JniException &rhs)
: what_(rhs.what_), isMessageExtracted_(rhs.isMessageExtracted_) {
JNIEnv* env = internal::getEnv();
if (rhs.getThrowable()) {
throwableGlobalRef_ = static_cast<jthrowable>(env->NewGlobalRef(rhs.getThrowable()));
if (!throwableGlobalRef_) {
throw std::bad_alloc();
}
} else {
throwableGlobalRef_ = nullptr;
}
}
JniException::~JniException() noexcept {
if (throwableGlobalRef_) {
internal::getEnv()->DeleteGlobalRef(throwableGlobalRef_);
}
}
jthrowable JniException::getThrowable() const noexcept {
return throwableGlobalRef_;
}
// TODO 6900503: consider making this thread-safe.
void JniException::populateWhat() const noexcept {
JNIEnv* env = internal::getEnv();
jmethodID toStringMID = env->GetMethodID(
CommonJniExceptions::getThrowableClass(),
"toString",
"()Ljava/lang/String;");
jstring messageJString = (jstring) env->CallObjectMethod(
throwableGlobalRef_,
toStringMID);
isMessageExtracted_ = true;
if (env->ExceptionCheck()) {
env->ExceptionClear();
what_ = kExceptionMessageFailure_;
return;
}
const char* chars = env->GetStringUTFChars(messageJString, nullptr);
if (!chars) {
what_ = kExceptionMessageFailure_;
return;
}
try {
what_ = std::string(chars);
} catch(...) {
what_ = kExceptionMessageFailure_;
}
env->ReleaseStringUTFChars(messageJString, chars);
}
const char* JniException::what() const noexcept {
if (!isMessageExtracted_) {
populateWhat();
}
return what_.c_str();
}
void JniException::setJavaException() const noexcept {
setJavaExceptionAndAbortOnFailure(throwableGlobalRef_);
}
}}

View File

@ -0,0 +1,130 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
/**
* @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 "Common.h"
// 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)
namespace facebook {
namespace jni {
namespace internal {
void initExceptionHelpers();
}
/**
* Before using any of the state initialized above, call this. It
* will assert if initialization has not yet occurred.
*/
void assertIfExceptionsNotInitialized();
// 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 JniException : public std::exception {
public:
JniException();
explicit JniException(jthrowable throwable);
JniException(JniException &&rhs);
JniException(const JniException &other);
~JniException() noexcept;
jthrowable getThrowable() const noexcept;
virtual const char* what() const noexcept;
void setJavaException() const noexcept;
private:
jthrowable throwableGlobalRef_;
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
void throwPendingJniExceptionAsCppException();
void throwCppExceptionIf(bool condition);
static const int kMaxExceptionMessageBufferSize = 512;
[[noreturn]] void throwNewJavaException(jthrowable);
[[noreturn]] void throwNewJavaException(const char* throwableName, const char* msg);
// 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) {
assertIfExceptionsNotInitialized();
int msgSize = snprintf(nullptr, 0, fmt, args...);
char *msg = (char*) alloca(msgSize);
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. This is a noexcept function at C++ level.
void translatePendingCppExceptionToJavaException() noexcept;
// For convenience, some exception names in java.lang are available here.
const char* const gJavaLangIllegalArgumentException = "java/lang/IllegalArgumentException";
}}

View File

@ -0,0 +1,76 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#include "Hybrid.h"
#include "Exceptions.h"
#include "Registration.h"
namespace facebook {
namespace jni {
namespace detail {
void setNativePointer(alias_ref<HybridData::javaobject> hybridData,
std::unique_ptr<BaseHybridClass> new_value) {
static auto pointerField = hybridData->getClass()->getField<jlong>("mNativePointer");
auto* old_value = reinterpret_cast<BaseHybridClass*>(hybridData->getFieldValue(pointerField));
if (new_value) {
// Modify should only ever be called once with a non-null
// new_value. If this happens again it's a programmer error, so
// blow up.
FBASSERTMSGF(old_value == 0, "Attempt to set C++ native pointer twice");
} else if (old_value == 0) {
return;
}
// delete on a null pointer is defined to be a noop.
delete old_value;
// This releases ownership from the unique_ptr, and passes the pointer, and
// ownership of it, to HybridData which is managed by the java GC. The
// finalizer on hybridData calls resetNative which will delete the object, if
// reseetNative has not already been called.
hybridData->setFieldValue(pointerField, reinterpret_cast<jlong>(new_value.release()));
}
BaseHybridClass* getNativePointer(alias_ref<HybridData::javaobject> hybridData) {
static auto pointerField = hybridData->getClass()->getField<jlong>("mNativePointer");
auto* value = reinterpret_cast<BaseHybridClass*>(hybridData->getFieldValue(pointerField));
if (!value) {
throwNewJavaException("java/lang/NullPointerException", "java.lang.NullPointerException");
}
return value;
}
local_ref<HybridData::javaobject> getHybridData(alias_ref<jobject> jthis,
JField<HybridData::javaobject> field) {
auto hybridData = jthis->getFieldValue(field);
if (!hybridData) {
throwNewJavaException("java/lang/NullPointerException", "java.lang.NullPointerException");
}
return hybridData;
}
}
namespace {
void resetNative(alias_ref<detail::HybridData::javaobject> jthis) {
detail::setNativePointer(jthis, nullptr);
}
}
void HybridDataOnLoad() {
registerNatives("com/facebook/jni/HybridData", {
makeNativeMethod("resetNative", resetNative),
});
}
}}

View File

@ -0,0 +1,277 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#pragma once
#include <memory>
#include <type_traits>
#include <fb/assert.h>
#include "CoreClasses.h"
namespace facebook {
namespace jni {
namespace detail {
class BaseHybridClass : public BaseJavaClass {
public:
virtual ~BaseHybridClass() {}
};
struct HybridData : public JavaClass<HybridData> {
constexpr static auto kJavaDescriptor = "Lcom/facebook/jni/HybridData;";
};
void setNativePointer(alias_ref<HybridData::javaobject> hybridData,
std::unique_ptr<BaseHybridClass> new_value);
BaseHybridClass* getNativePointer(alias_ref<HybridData::javaobject> hybridData);
local_ref<HybridData::javaobject> getHybridData(alias_ref<jobject> jthis,
JField<HybridData::javaobject> field);
// 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;
};
// 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);
}
};
// 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 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 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 T jniType;
// No automatic synthesis of global_ref
static jniType toJniRet(global_ref<jniType> t) {
return t.get();
}
static jniType toCall(global_ref<jniType> t) {
return t.get();
}
};
// 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. This is only relevant when the jniType is passed down, as
// in newObjectJavaArgs.
template <typename T>
inline T callToJni(T&& t) {
return t;
}
inline jstring callToJni(local_ref<jstring>&& sref) {
return sref.get();
}
struct jstring_holder {
local_ref<jstring> s_;
jstring_holder(const char* s) : s_(make_jstring(s)) {}
operator jstring() { return s_.get(); }
};
template <typename T, typename Enabled = void>
struct HybridRoot {};
template <typename T>
struct HybridRoot<T,
typename std::enable_if<!std::is_base_of<BaseHybridClass, T>::value>::type>
: public BaseHybridClass {};
}
template <typename T, typename Base = detail::BaseHybridClass>
class HybridClass : public Base
, public detail::HybridRoot<Base>
, public JavaClass<T, Base> {
public:
typedef detail::HybridData::javaobject jhybriddata;
typedef typename JavaClass<T, Base>::javaobject jhybridobject;
using JavaClass<T, Base>::javaClassStatic;
using JavaClass<T, Base>::javaClassLocal;
using JavaClass<T, Base>::javaobject;
typedef typename JavaClass<T, Base>::_javaobject _javaobject;
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 Base::Base;
static void registerHybrid(std::initializer_list<NativeMethod> methods) {
javaClassStatic()->registerNatives(methods);
}
static local_ref<jhybriddata> makeHybridData(std::unique_ptr<T> cxxPart) {
static auto dataCtor = detail::HybridData::javaClassStatic()->getConstructor<jhybriddata()>();
auto hybridData = detail::HybridData::javaClassStatic()->newObject(dataCtor);
detail::setNativePointer(hybridData, std::move(cxxPart));
return hybridData;
}
template <typename... Args>
static local_ref<jhybriddata> makeCxxInstance(Args&&... args) {
return makeHybridData(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<jhybridobject> newObjectCxxArgs(Args&&... args) {
auto hybridData = makeCxxInstance(std::forward<Args>(args)...);
static auto ctor = javaClassStatic()->template getConstructor<jhybridobject(jhybriddata)>();
return javaClassStatic()->newObject(ctor, hybridData.get());
}
// Factory method for creating a hybrid object where the arguments
// are passed to the java ctor.
template <typename... Args>
static local_ref<jhybridobject> newObjectJavaArgs(Args&&... args) {
static auto ctor =
javaClassStatic()->template getConstructor<
jhybridobject(typename detail::Convert<typename std::decay<Args>::type>::jniType...)>();
// This can't use the same impl as Convert::toJniRet because that
// function sometimes creates and then releases local_refs, which
// could potentially cause the locals table to fill. Instead, we
// use two calls, one which can return a local_ref if needed, and
// a second which extracts its value. The lifetime of the
// local_ref is the expression, after which it is destroyed and
// the local_ref is cleaned up.
auto lref =
javaClassStatic()->newObject(
ctor, detail::callToJni(
detail::Convert<typename std::decay<Args>::type>::toCall(args))...);
return lref;
}
// 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) {}
};
// 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 typename std::remove_pointer<typename T::PlainJniType>::type::javaClass* cthis(T jthis) {
static auto dataField =
jthis->getClass()->template getField<detail::HybridData::javaobject>("mHybridData");
// I'd like to use dynamic_cast here, but -fno-rtti is the default.
auto* value = static_cast<typename std::remove_pointer<typename T::PlainJniType>::type::javaClass*>(
detail::getNativePointer(detail::getHybridData(jthis, dataField)));
// This would require some serious programmer error.
FBASSERTMSGF(value != 0, "Incorrect C++ type in hybrid field");
return value;
}
void HybridDataOnLoad();
}
}

View File

@ -0,0 +1,352 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#pragma once
#include <jni.h>
#include "Common.h"
#include "Exceptions.h"
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_;
}
template<typename... Args>
inline void JMethod<void(Args...)>::operator()(alias_ref<jobject> self, Args... args) {
const auto env = internal::getEnv();
env->CallVoidMethod(self.get(), getId(), 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(), 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")
template<typename T, typename... Args>
inline local_ref<T*> JMethod<T*(Args...)>::operator()(alias_ref<jobject> self, Args... args) {
const auto env = internal::getEnv();
auto result = env->CallObjectMethod(self.get(), getId(), args...);
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
return adopt_local(static_cast<T*>(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(), 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(), 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")
template<typename T, typename... Args>
inline local_ref<T*> JStaticMethod<T*(Args...)>::operator()(alias_ref<jclass> cls, Args... args) {
const auto env = internal::getEnv();
auto result = env->CallStaticObjectMethod(cls.get(), getId(), args...);
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
return adopt_local(static_cast<T*>(result));
}
template<typename... Args>
inline void
JNonvirtualMethod<void(Args...)>::operator()(alias_ref<jobject> self, jclass cls, Args... args) {
const auto env = internal::getEnv();
env->CallNonvirtualVoidMethod(self.get(), cls, getId(), 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, jclass cls, Args... args) { \
const auto env = internal::getEnv(); \
auto result = env->CallNonvirtual ## METHOD ## Method(self.get(), cls, getId(), 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")
template<typename T, typename... Args>
inline local_ref<T*> JNonvirtualMethod<T*(Args...)>::operator()(
alias_ref<jobject> self,
jclass cls,
Args... args) {
const auto env = internal::getEnv();
auto result = env->CallNonvirtualObjectMethod(self.get(), cls, getId(), args...);
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
return adopt_local(static_cast<T*>(result));
}
// jtype_traits ////////////////////////////////////////////////////////////////////////////////////
/// The generic way to associate a descriptor to a type is to look it up in the
/// corresponding @ref JObjectWrapper specialization. This makes it easy to add
/// support for your user defined type.
template<typename T>
struct jtype_traits {
// The jni type signature (described at
// http://docs.oracle.com/javase/1.5.0/docs/guide/jni/spec/types.html).
static std::string descriptor() {
static const auto descriptor = JObjectWrapper<T>::kJavaDescriptor != nullptr ?
std::string{JObjectWrapper<T>::kJavaDescriptor} :
JObjectWrapper<T>::get_instantiated_java_descriptor();
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 (JObjectWrapper<T>::kJavaDescriptor != nullptr) {
std::string base_name = JObjectWrapper<T>::kJavaDescriptor;
return base_name.substr(1, base_name.size() - 2);
}
return JObjectWrapper<T>::get_instantiated_java_descriptor();
}
};
#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(); } \
}; \
template<> \
struct jtype_traits<TYPE ## Array> { \
static std::string descriptor() { return std::string{"[" #DSC}; } \
static std::string base_name() { return descriptor(); } \
};
// 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")
// 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...>();
}
}}

View File

@ -0,0 +1,302 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
/** @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.h"
namespace facebook {
namespace jni {
/// 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 JObjectWrapper<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
/// JMethod specialization for references that wraps the return value in a @ref local_ref
template<typename T, typename... Args>
class JMethod<T*(Args...)> : public JMethodBase {
public:
static_assert(IsPlainJniReference<T*>(), "T* 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<T*> operator()(alias_ref<jobject> self, Args... args);
friend class JObjectWrapper<jclass>;
};
/// Convenience type representing constructors
template<typename F>
using JConstructor = JMethod<F>;
/// 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 JObjectWrapper<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
/// JStaticMethod specialization for references that wraps the return value in a @ref local_ref
template<typename T, typename... Args>
class JStaticMethod<T*(Args...)> : public JMethodBase {
static_assert(IsPlainJniReference<T*>(), "T* must be a JNI reference");
public:
using JMethodBase::JMethodBase;
JStaticMethod() noexcept {};
JStaticMethod(const JStaticMethod& other) noexcept = default;
/// Invoke a method and return a local reference wrapping the result
local_ref<T*> operator()(alias_ref<jclass> cls, Args... args);
friend class JObjectWrapper<jclass>;
};
/// 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, jclass cls, Args... args); \
\
friend class JObjectWrapper<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
/// JNonvirtualMethod specialization for references that wraps the return value in a @ref local_ref
template<typename T, typename... Args>
class JNonvirtualMethod<T*(Args...)> : public JMethodBase {
static_assert(IsPlainJniReference<T*>(), "T* must be a JNI reference");
public:
using JMethodBase::JMethodBase;
JNonvirtualMethod() noexcept {};
JNonvirtualMethod(const JNonvirtualMethod& other) noexcept = default;
/// Invoke a method and return a local reference wrapping the result
local_ref<T*> operator()(alias_ref<jobject> self, jclass cls, Args... args);
friend class JObjectWrapper<jclass>;
};
/**
* 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 JObjectWrapper<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 JObjectWrapper<jclass>;
};
/// 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;
/// 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();
};
}}

View File

@ -0,0 +1,123 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#pragma once
#include <cassert>
#include <new>
#include <atomic>
#include "Exceptions.h"
#include "References.h"
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_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);
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);
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);
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;
}
}}

View File

@ -0,0 +1,60 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
/**
* @file ReferenceAllocators.h
*
* Reference allocators are used to create and delete various classes of JNI references (local,
* global, and weak global).
*/
#pragma once
#include "Common.h"
namespace facebook { namespace jni {
/// Allocator that handles local references
class 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 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 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.
*/
bool doesGetObjectRefTypeWork();
}
/// @endcond
}}
#include "ReferenceAllocators-inl.h"

View File

@ -0,0 +1,387 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#pragma once
#include <new>
#include "CoreClasses.h"
namespace facebook {
namespace jni {
template<typename T>
inline enable_if_t<IsPlainJniReference<T>(), local_ref<T>> adopt_local(T ref) noexcept {
return local_ref<T>{ref};
}
template<typename T>
inline enable_if_t<IsPlainJniReference<T>(), global_ref<T>> adopt_global(T ref) noexcept {
return global_ref<T>{ref};
}
template<typename T>
inline enable_if_t<IsPlainJniReference<T>(), weak_ref<T>> adopt_weak_global(T ref) noexcept {
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>
inline enable_if_t<IsPlainJniReference<T>(), T> getPlainJniReference(T ref) {
return ref;
}
template<typename T>
inline T getPlainJniReference(alias_ref<T> ref) {
return ref.get();
}
template<typename T, typename A>
inline T getPlainJniReference(const base_owned_ref<T, A>& ref) {
return ref.getPlainJniReference();
}
namespace internal {
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);
}
}
template<typename T>
enable_if_t<IsNonWeakReference<T>(), local_ref<plain_jni_reference_t<T>>>
make_local(const T& ref) {
return adopt_local(internal::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(internal::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(internal::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 constexpr base_owned_ref<T, Alloc>::base_owned_ref() noexcept
: object_{nullptr}
{}
template<typename T, typename Alloc>
inline constexpr base_owned_ref<T, Alloc>::base_owned_ref(
std::nullptr_t t) noexcept
: object_{nullptr}
{}
template<typename T, typename Alloc>
inline base_owned_ref<T, Alloc>::base_owned_ref(
const base_owned_ref& other)
: object_{Alloc{}.newReference(other.getPlainJniReference())}
{}
template<typename T, typename Alloc>
template<typename U>
inline base_owned_ref<T, Alloc>::base_owned_ref(const base_owned_ref<U, Alloc>& other)
: object_{Alloc{}.newReference(other.getPlainJniReference())}
{}
template<typename T, typename Alloc>
inline facebook::jni::base_owned_ref<T, Alloc>::base_owned_ref(
T reference) noexcept
: object_{reference} {
assert(Alloc{}.verifyReference(reference));
internal::dbglog("New wrapped ref=%p this=%p", getPlainJniReference(), this);
}
template<typename T, typename Alloc>
inline base_owned_ref<T, Alloc>::base_owned_ref(
base_owned_ref<T, Alloc>&& other) noexcept
: object_{other.object_} {
internal::dbglog("New move from ref=%p other=%p", other.getPlainJniReference(), &other);
internal::dbglog("New move to ref=%p this=%p", getPlainJniReference(), this);
// JObjectWrapper is a simple type and does not support move semantics so we explicitly
// clear other
other.object_.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
: object_{other.object_} {
internal::dbglog("New move from ref=%p other=%p", other.getPlainJniReference(), &other);
internal::dbglog("New move to ref=%p this=%p", getPlainJniReference(), this);
// JObjectWrapper is a simple type and does not support move semantics so we explicitly
// clear other
other.object_.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", getPlainJniReference(), this);
}
template<typename T, typename Alloc>
inline T base_owned_ref<T, Alloc>::release() noexcept {
auto value = getPlainJniReference();
internal::dbglog("Ref release ref=%p this=%p", value, this);
object_.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(T reference) noexcept {
if (getPlainJniReference()) {
assert(Alloc{}.verifyReference(reference));
Alloc{}.deleteReference(getPlainJniReference());
}
object_.set(reference);
}
template<typename T, typename Alloc>
inline T base_owned_ref<T, Alloc>::getPlainJniReference() const noexcept {
return static_cast<T>(object_.get());
}
// 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",
getPlainJniReference(), this, other.getPlainJniReference(), &other);
reset(other.release());
return *this;
}
template<typename T>
local_ref<T> weak_ref<T>::lockLocal() {
return adopt_local(static_cast<T>(LocalReferenceAllocator{}.newReference(getPlainJniReference())));
}
template<typename T>
global_ref<T> weak_ref<T>::lockGlobal() {
return adopt_global(static_cast<T>(GlobalReferenceAllocator{}.newReference(getPlainJniReference())));
}
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.getPlainJniReference(), &a, b.getPlainJniReference(), &b);
using std::swap;
swap(a.object_, b.object_);
}
// 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",
getPlainJniReference(), this, other.getPlainJniReference(), &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 T basic_strong_ref<T, Alloc>::get() const noexcept {
return getPlainJniReference();
}
template<typename T, typename Alloc>
inline JObjectWrapper<T>* basic_strong_ref<T, Alloc>::operator->() noexcept {
return &object_;
}
template<typename T, typename Alloc>
inline const JObjectWrapper<T>* basic_strong_ref<T, Alloc>::operator->() const noexcept {
return &object_;
}
template<typename T, typename Alloc>
inline JObjectWrapper<T>& basic_strong_ref<T, Alloc>::operator*() noexcept {
return object_;
}
template<typename T, typename Alloc>
inline const JObjectWrapper<T>& basic_strong_ref<T, Alloc>::operator*() const noexcept {
return object_;
}
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.getPlainJniReference(), &a, b.getPlainJniReference(), &b);
using std::swap;
swap(a.object_, b.object_);
}
// alias_ref //////////////////////////////////////////////////////////////////////////////
template<typename T>
inline constexpr alias_ref<T>::alias_ref() noexcept
: object_{nullptr}
{}
template<typename T>
inline constexpr alias_ref<T>::alias_ref(std::nullptr_t) noexcept
: object_{nullptr}
{}
template<typename T>
inline alias_ref<T>::alias_ref(const alias_ref& other) noexcept
: object_{other.object_}
{}
template<typename T>
inline alias_ref<T>::alias_ref(T ref) noexcept
: object_{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
: object_{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
: object_{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 T facebook::jni::alias_ref<T>::get() const noexcept {
return static_cast<T>(object_.get());
}
template<typename T>
inline JObjectWrapper<T>* alias_ref<T>::operator->() noexcept {
return &object_;
}
template<typename T>
inline const JObjectWrapper<T>* alias_ref<T>::operator->() const noexcept {
return &object_;
}
template<typename T>
inline JObjectWrapper<T>& alias_ref<T>::operator*() noexcept {
return object_;
}
template<typename T>
inline const JObjectWrapper<T>& alias_ref<T>::operator*() const noexcept {
return object_;
}
template<typename T>
inline void swap(alias_ref<T>& a, alias_ref<T>& b) noexcept {
using std::swap;
swap(a.object_, b.object_);
}
}}

View File

@ -0,0 +1,41 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#include "References.h"
namespace facebook {
namespace jni {
JniLocalScope::JniLocalScope(JNIEnv* env, jint capacity)
: env_(env) {
hasFrame_ = false;
auto pushResult = env->PushLocalFrame(capacity);
FACEBOOK_JNI_THROW_EXCEPTION_IF(pushResult < 0);
hasFrame_ = true;
}
JniLocalScope::~JniLocalScope() {
if (hasFrame_) {
env_->PopLocalFrame(nullptr);
}
}
namespace internal {
// Default implementation always returns true.
// Platform-specific sources can override this.
bool doesGetObjectRefTypeWork() __attribute__ ((weak));
bool doesGetObjectRefTypeWork() {
return true;
}
}
}
}

View File

@ -0,0 +1,515 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
/** @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 "ReferenceAllocators.h"
#include "TypeTraits.h"
namespace facebook {
namespace jni {
/**
* The JObjectWrapper is specialized to provide functionality for various Java classes, some
* specializations are provided, and it is easy to add your own. See example
* @sample WrapperSample.cpp
*/
template<typename T, typename Enable = void>
class JObjectWrapper;
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>;
/// Convenience function to wrap an existing local reference
template<typename T>
enable_if_t<IsPlainJniReference<T>(), local_ref<T>> adopt_local(T ref) noexcept;
/// Convenience function to wrap an existing global reference
template<typename T>
enable_if_t<IsPlainJniReference<T>(), global_ref<T>> adopt_global(T ref) noexcept;
/// Convenience function to wrap an existing weak reference
template<typename T>
enable_if_t<IsPlainJniReference<T>(), weak_ref<T>> adopt_weak_global(T ref) noexcept;
/**
* 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);
/// 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>
T getPlainJniReference(alias_ref<T> ref);
/**
* Retrieve the plain JNI reference from any reference owned reference.
*/
template<typename T, typename Alloc>
T getPlainJniReference(const base_owned_ref<T, Alloc>& ref);
/**
* 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 {
static_assert(IsPlainJniReference<T>(), "T must be a JNI reference");
public:
/**
* Release the ownership and set the reference to null. Thus no deleter is invoked.
* @return Returns the reference
*/
T release() noexcept;
/**
* Reset the reference to refer to nullptr.
*/
void reset() noexcept;
protected:
JObjectWrapper<T> object_;
/*
* 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(T reference) noexcept;
/// Create a null reference
constexpr base_owned_ref() noexcept;
/// Create a null reference
constexpr 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;
T getPlainJniReference() const noexcept;
void reset(T reference) noexcept;
friend T 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> {
static_assert(IsPlainJniReference<T>(), "T must be a JNI reference");
public:
using PlainJniType = 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
constexpr weak_ref() noexcept
: base_owned_ref<T, Allocator>{} {}
/// Create a null reference
constexpr explicit 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} {}
/// 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();
// Creates an owned global reference to the referred object or to null if the object is reclaimed
global_ref<T> lockGlobal();
private:
using base_owned_ref<T, Allocator>::getPlainJniReference;
/*
* Wrap an existing reference and transfers its ownership to the newly created unique reference.
* NB! Does not create a new reference
*/
explicit weak_ref(T reference) noexcept
: base_owned_ref<T, Allocator>{reference} {}
template<typename T2> friend class weak_ref;
friend weak_ref<enable_if_t<IsPlainJniReference<T>(), T>>
adopt_weak_global<T>(T 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> {
static_assert(IsPlainJniReference<T>(), "T must be a JNI reference");
public:
using PlainJniType = 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
constexpr basic_strong_ref() noexcept
: base_owned_ref<T, Alloc>{} {}
/// Create a null reference
constexpr explicit 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} {}
/// 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;
/// 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;
/// Get the plain JNI reference
T get() const noexcept;
/// Access the functionality provided by the object wrappers
JObjectWrapper<T>* operator->() noexcept;
/// Access the functionality provided by the object wrappers
const JObjectWrapper<T>* operator->() const noexcept;
/// Provide a reference to the underlying wrapper (be sure that it is non-null before invoking)
JObjectWrapper<T>& operator*() noexcept;
/// Provide a const reference to the underlying wrapper (be sure that it is non-null
/// before invoking)
const JObjectWrapper<T>& operator*() const noexcept;
private:
using base_owned_ref<T, Alloc>::object_;
using base_owned_ref<T, Alloc>::getPlainJniReference;
/*
* 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(T reference) noexcept
: base_owned_ref<T, Alloc>{reference} {}
friend enable_if_t<IsPlainJniReference<T>(), local_ref<T>> adopt_local<T>(T ref) noexcept;
friend enable_if_t<IsPlainJniReference<T>(), 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 referencec 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 {
static_assert(IsPlainJniReference<T>(), "T must be a JNI reference");
public:
using PlainJniType = T;
/// Create a null reference
constexpr alias_ref() noexcept;
/// Create a null reference
constexpr alias_ref(std::nullptr_t) noexcept;
/// Copy constructor
alias_ref(const alias_ref& other) noexcept;
/// Wrap an existing plain JNI reference
alias_ref(T ref) noexcept;
/// Wrap an existing smart reference of any type convertible to T
template<typename TOther, typename = enable_if_t<IsConvertible<TOther, T>(), 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<TOther, T>(), 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
T get() const noexcept;
/// Access the functionality provided by the object wrappers
JObjectWrapper<T>* operator->() noexcept;
/// Access the functionality provided by the object wrappers
const JObjectWrapper<T>* operator->() const noexcept;
/// Provide a guaranteed non-null reference (be sure that it is non-null before invoking)
JObjectWrapper<T>& operator*() noexcept;
/// Provide a guaranteed non-null reference (be sure that it is non-null before invoking)
const JObjectWrapper<T>& operator*() const noexcept;
private:
JObjectWrapper<T> object_;
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 JniLocalScope {
public:
JniLocalScope(JNIEnv* p_env, jint capacity);
~JniLocalScope();
private:
JNIEnv* env_;
bool hasFrame_;
};
}}
#include "References-inl.h"

View File

@ -0,0 +1,193 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#pragma once
#include "Exceptions.h"
#include "Hybrid.h"
namespace facebook {
namespace jni {
namespace detail {
// convert to HybridClass* from jhybridobject
template <typename T>
struct 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 facebook::jni::cthis(wrap_alias(t));
}
// There is no automatic return conversion for objects.
};
// registration wrapper for legacy JNI-style functions
template<typename F, F func, typename C, typename... Args>
inline NativeMethodWrapper* exceptionWrapJNIMethod(void (*)(JNIEnv*, C, Args... args)) {
struct funcWrapper {
static void call(JNIEnv* env, jobject obj, Args... args) {
// Note that if func was declared noexcept, then both gcc and clang are smart
// enough to elide the try/catch.
try {
(*func)(env, static_cast<C>(obj), args...);
} catch (...) {
translatePendingCppExceptionToJavaException();
}
}
};
// This intentionally erases the real type; JNI will do it anyway
return reinterpret_cast<NativeMethodWrapper*>(&(funcWrapper::call));
}
template<typename F, F func, typename C, typename R, typename... Args>
inline NativeMethodWrapper* exceptionWrapJNIMethod(R (*)(JNIEnv*, C, Args... args)) {
struct funcWrapper {
static R call(JNIEnv* env, jobject obj, Args... args) {
try {
return (*func)(env, static_cast<C>(obj), args...);
} catch (...) {
translatePendingCppExceptionToJavaException();
return R{};
}
}
};
// This intentionally erases the real type; JNI will do it anyway
return reinterpret_cast<NativeMethodWrapper*>(&(funcWrapper::call));
}
// registration wrappers for functions, with autoconversion of arguments.
template<typename F, F func, typename C, typename... Args>
inline NativeMethodWrapper* exceptionWrapJNIMethod(void (*)(alias_ref<C>, Args... args)) {
struct funcWrapper {
static void call(JNIEnv*, jobject obj,
typename Convert<typename std::decay<Args>::type>::jniType... args) {
try {
(*func)(static_cast<C>(obj), Convert<typename std::decay<Args>::type>::fromJni(args)...);
} catch (...) {
translatePendingCppExceptionToJavaException();
}
}
};
// This intentionally erases the real type; JNI will do it anyway
return reinterpret_cast<NativeMethodWrapper*>(&(funcWrapper::call));
}
template<typename F, F func, typename C, typename R, typename... Args>
inline NativeMethodWrapper* exceptionWrapJNIMethod(R (*)(alias_ref<C>, Args... args)) {
struct funcWrapper {
typedef typename Convert<typename std::decay<R>::type>::jniType jniRet;
static jniRet call(JNIEnv*, jobject obj,
typename Convert<typename std::decay<Args>::type>::jniType... args) {
try {
return Convert<typename std::decay<R>::type>::toJniRet(
(*func)(static_cast<C>(obj), Convert<typename std::decay<Args>::type>::fromJni(args)...));
} catch (...) {
translatePendingCppExceptionToJavaException();
return jniRet{};
}
}
};
// This intentionally erases the real type; JNI will do it anyway
return reinterpret_cast<NativeMethodWrapper*>(&(funcWrapper::call));
}
// registration wrappers for non-static methods, with autoconvertion of arguments.
template<typename M, M method, typename C, typename... Args>
inline NativeMethodWrapper* exceptionWrapJNIMethod(void (C::*method0)(Args... args)) {
struct funcWrapper {
static void call(JNIEnv* env, jobject obj,
typename Convert<typename std::decay<Args>::type>::jniType... args) {
try {
try {
auto aref = wrap_alias(static_cast<typename C::jhybridobject>(obj));
// 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*>(facebook::jni::cthis(aref));
(cobj->*method)(Convert<typename std::decay<Args>::type>::fromJni(args)...);
} catch (const std::exception& ex) {
C::mapException(ex);
throw;
}
} catch (...) {
translatePendingCppExceptionToJavaException();
}
}
};
// This intentionally erases the real type; JNI will do it anyway
return reinterpret_cast<NativeMethodWrapper*>(&(funcWrapper::call));
}
template<typename M, M method, typename C, typename R, typename... Args>
inline NativeMethodWrapper* exceptionWrapJNIMethod(R (C::*method0)(Args... args)) {
struct funcWrapper {
typedef typename Convert<typename std::decay<R>::type>::jniType jniRet;
static jniRet call(JNIEnv* env, jobject obj,
typename Convert<typename std::decay<Args>::type>::jniType... args) {
try {
try {
auto aref = wrap_alias(static_cast<typename C::jhybridobject>(obj));
// 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*>(facebook::jni::cthis(aref));
return Convert<typename std::decay<R>::type>::toJniRet(
(cobj->*method)(Convert<typename std::decay<Args>::type>::fromJni(args)...));
} catch (const std::exception& ex) {
C::mapException(ex);
throw;
}
} catch (...) {
translatePendingCppExceptionToJavaException();
return jniRet{};
}
}
};
// This intentionally erases the real type; JNI will do it anyway
return reinterpret_cast<NativeMethodWrapper*>(&(funcWrapper::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)) {
typedef typename Convert<typename std::decay<R>::type>::jniType jniRet;
return jmethod_traits<jniRet(typename Convert<typename std::decay<Args>::type>::jniType...)>
::descriptor();
}
template<typename R, typename C, typename... Args>
inline std::string makeDescriptor(R (C::*)(Args... args)) {
typedef typename Convert<typename std::decay<R>::type>::jniType jniRet;
return jmethod_traits<jniRet(typename Convert<typename std::decay<Args>::type>::jniType...)>
::descriptor();
}
}
}}

View File

@ -0,0 +1,87 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#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... Args>
NativeMethodWrapper* exceptionWrapJNIMethod(void (*func0)(JNIEnv*, jobject, Args... args));
// Same as above, but for non-void return types.
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... Args>
NativeMethodWrapper* exceptionWrapJNIMethod(void (*func0)(alias_ref<C>, Args... args));
// Automatically wrap object argument, and don't take env explicitly,
// non-void return type.
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... Args>
NativeMethodWrapper* exceptionWrapJNIMethod(void (C::*method0)(Args... args));
// Extract C++ instance from object, and invoke given method on it,
// non-void return type
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));
}
// 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__)
}}
#include "Registration-inl.h"

View File

@ -0,0 +1,149 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#pragma once
#include <type_traits>
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 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>
class weak_global_ref;
template<typename T, typename Alloc>
class basic_strong_ref;
template<typename T>
class alias_ref;
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_global_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 {
static_assert(IsPlainJniReference<T>(), "Need a plain JNI reference");
using plain_jni_reference_t = T;
};
template<template <typename...> class R, typename T, typename... A>
struct reference_traits<R<T, A...>> {
static_assert(IsAnyReference<T>(), "Need an fbjni reference");
using plain_jni_reference_t = T;
};
template<typename T>
using plain_jni_reference_t = typename reference_traits<T>::plain_jni_reference_t;
}
}

View File

@ -0,0 +1,195 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#include <jni.h>
#include <stddef.h>
#include <cstdio>
#define MSG_SIZE 1024
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
*/
jint throwException(JNIEnv* pEnv, const char* szClassName, const char* szFmt, va_list va_args) {
char szMsg[MSG_SIZE];
vsnprintf(szMsg, MSG_SIZE, szFmt, va_args);
jclass exClass = pEnv->FindClass(szClassName);
return pEnv->ThrowNew(exClass, szMsg);
}
/**
* 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
*/
jint throwNoClassDefError(JNIEnv* pEnv, const char* szFmt, ...) {
va_list va_args;
va_start(va_args, szFmt);
jint ret = throwException(pEnv, "java/lang/NoClassDefFoundError", szFmt, va_args);
va_end(va_args);
return ret;
}
/**
* 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
*/
jint throwRuntimeException(JNIEnv* pEnv, const char* szFmt, ...) {
va_list va_args;
va_start(va_args, szFmt);
jint ret = throwException(pEnv, "java/lang/RuntimeException", szFmt, va_args);
va_end(va_args);
return ret;
}
/**
* Instructs the JNI environment to throw an IllegalArgumentException.
*
* @param pEnv JNI environment
* @param szFmt sprintf-style format string
* @param ... sprintf-style args
* @return 0 on success; a negative value on failure
*/
jint throwIllegalArgumentException(JNIEnv* pEnv, const char* szFmt, ...) {
va_list va_args;
va_start(va_args, szFmt);
jint ret = throwException(pEnv, "java/lang/IllegalArgumentException", szFmt, va_args);
va_end(va_args);
return ret;
}
/**
* Instructs the JNI environment to throw an IllegalStateException.
*
* @param pEnv JNI environment
* @param szFmt sprintf-style format string
* @param ... sprintf-style args
* @return 0 on success; a negative value on failure
*/
jint throwIllegalStateException(JNIEnv* pEnv, const char* szFmt, ...) {
va_list va_args;
va_start(va_args, szFmt);
jint ret = throwException(pEnv, "java/lang/IllegalStateException", szFmt, va_args);
va_end(va_args);
return ret;
}
/**
* 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
*/
jint throwOutOfMemoryError(JNIEnv* pEnv, const char* szFmt, ...) {
va_list va_args;
va_start(va_args, szFmt);
jint ret = throwException(pEnv, "java/lang/OutOfMemoryError", szFmt, va_args);
va_end(va_args);
return ret;
}
/**
* 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
*/
jint throwAssertionError(JNIEnv* pEnv, const char* szFmt, ...) {
va_list va_args;
va_start(va_args, szFmt);
jint ret = throwException(pEnv, "java/lang/AssertionError", szFmt, va_args);
va_end(va_args);
return ret;
}
/**
* 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
*/
jint throwIOException(JNIEnv* pEnv, const char* szFmt, ...) {
va_list va_args;
va_start(va_args, szFmt);
jint ret = throwException(pEnv, "java/io/IOException", szFmt, va_args);
va_end(va_args);
return ret;
}
/**
* 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).
*/
jclass findClassOrThrow(JNIEnv* pEnv, const char* szClassName) {
jclass clazz = pEnv->FindClass(szClassName);
if (!clazz) {
return NULL;
}
return (jclass) pEnv->NewGlobalRef(clazz);
}
/**
* 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)
*/
jfieldID getFieldIdOrThrow(JNIEnv* pEnv, jclass clazz, const char* szFieldName, const char* szSig) {
return pEnv->GetFieldID(clazz, szFieldName, 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)
*/
jmethodID getMethodIdOrThrow(
JNIEnv* pEnv,
jclass clazz,
const char* szMethodName,
const char* szSig) {
return pEnv->GetMethodID(clazz, szMethodName, szSig);
}
} // namespace facebook

View File

@ -0,0 +1,137 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#pragma once
#include <jni.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
*/
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
*/
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
*/
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
*/
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
*/
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
*/
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
*/
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
*/
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).
*/
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)
*/
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)
*/
jmethodID getMethodIdOrThrow(
JNIEnv* pEnv,
jclass clazz,
const char* szMethodName,
const char* szSig);
} // namespace facebook

View File

@ -0,0 +1,11 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_C_INCLUDES := $(LOCAL_PATH)/boost_1_57_0
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/boost_1_57_0
CXX11_FLAGS := -std=gnu++11
LOCAL_EXPORT_CPPFLAGS := $(CXX11_FLAGS)
LOCAL_MODULE := boost
include $(BUILD_STATIC_LIBRARY)

View File

@ -0,0 +1,23 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := double-conversion
LOCAL_SRC_FILES := \
double-conversion/bignum.cc \
double-conversion/bignum-dtoa.cc \
double-conversion/cached-powers.cc \
double-conversion/diy-fp.cc \
double-conversion/double-conversion.cc \
double-conversion/fast-dtoa.cc \
double-conversion/fixed-dtoa.cc \
double-conversion/strtod.cc
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)
CXX11_FLAGS := -std=c++11 -Wno-unused-variable -Wno-unused-local-typedefs
LOCAL_CFLAGS += $(CXX11_FLAGS)
LOCAL_EXPORT_CPPFLAGS := $(CXX11_FLAGS)
include $(BUILD_STATIC_LIBRARY)

View File

@ -0,0 +1,41 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
folly/json.cpp \
folly/Unicode.cpp \
folly/Conv.cpp \
folly/detail/FunctionalExcept.cpp \
folly/detail/MallocImpl.cpp \
folly/Malloc.cpp \
folly/StringBase.cpp \
folly/dynamic.cpp \
LOCAL_C_INCLUDES := $(LOCAL_PATH)
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)
LOCAL_CFLAGS += -fexceptions -fno-omit-frame-pointer -frtti
LOCAL_CFLAGS += -Wall -Werror -std=c++11
CXX11_FLAGS := -std=gnu++11
LOCAL_CFLAGS += $(CXX11_FLAGS)
FOLLY_FLAGS := -DFOLLY_NO_CONFIG=1 -DFOLLY_HAVE_CLOCK_GETTIME=1
LOCAL_CFLAGS += $(FOLLY_FLAGS)
LOCAL_EXPORT_CPPFLAGS := $(CXX11_FLAGS) $(FOLLY_FLAGS)
LOCAL_MODULE := libfolly_json
LOCAL_SHARED_LIBRARIES := libglog libdouble-conversion
# Boost is header-only library we pretend to link is statically as
# this way android makefile will automatically setup path to boost header
# file, but except from that this will have no effect, as no c/cpp files
# are part of this static library
LOCAL_STATIC_LIBRARIES := libboost
include $(BUILD_SHARED_LIBRARY)
$(call import-module,glog)
$(call import-module,double-conversion)
$(call import-module,boost)

View File

@ -0,0 +1,32 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := \
glog-0.3.3/src/demangle.cc \
glog-0.3.3/src/logging.cc \
glog-0.3.3/src/raw_logging.cc \
glog-0.3.3/src/signalhandler.cc \
glog-0.3.3/src/symbolize.cc \
glog-0.3.3/src/utilities.cc \
glog-0.3.3/src/vlog_is_on.cc
LOCAL_C_INCLUDES += $(LOCAL_PATH) $(LOCAL_PATH)/.. $(LOCAL_PATH)/glog-0.3.3/src/
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/.. $(LOCAL_PATH)/glog-0.3.3/src/
LOCAL_CFLAGS += \
-Wall \
-Wwrite-strings \
-Woverloaded-virtual \
-Wno-sign-compare \
-DNDEBUG \
-g \
-O2 \
-D_START_GOOGLE_NAMESPACE_="namespace google {" \
-D_END_GOOGLE_NAMESPACE_="}"
LOCAL_MODULE := glog
include $(BUILD_SHARED_LIBRARY)

View File

@ -0,0 +1,179 @@
/* src/config.h. Generated from config.h.in by configure. */
/* src/config.h.in. Generated from configure.ac by autoheader. */
/* define if glog doesn't use RTTI */
#define DISABLE_RTTI 1
/* Namespace for Google classes */
#define GOOGLE_NAMESPACE google
/* Define if you have the `dladdr' function */
#define HAVE_DLADDR 1
/* Define to 1 if you have the <dlfcn.h> header file. */
#define HAVE_DLFCN_H 1
/* Define to 1 if you have the <execinfo.h> header file. */
/* #undef HAVE_EXECINFO_H */
/* Define if you have the `fcntl' function */
#define HAVE_FCNTL 1
/* Define to 1 if you have the <glob.h> header file. */
/* #undef HAVE_GLOB_H */
/* Define to 1 if you have the <inttypes.h> header file. */
#define HAVE_INTTYPES_H 1
/* Define to 1 if you have the `pthread' library (-lpthread). */
/* #undef HAVE_LIBPTHREAD */
/* Define to 1 if you have the <libunwind.h> header file. */
/* #undef HAVE_LIBUNWIND_H */
/* define if you have google gflags library */
/* #undef HAVE_LIB_GFLAGS */
/* define if you have google gmock library */
/* #undef HAVE_LIB_GMOCK */
/* define if you have google gtest library */
/* #undef HAVE_LIB_GTEST */
/* define if you have libunwind */
/* #undef HAVE_LIB_UNWIND */
/* Define to 1 if you have the <memory.h> header file. */
#define HAVE_MEMORY_H 1
/* define if the compiler implements namespaces */
#define HAVE_NAMESPACES 1
/* Define if you have POSIX threads libraries and header files. */
#define HAVE_PTHREAD 1
/* Define to 1 if you have the <pwd.h> header file. */
#define HAVE_PWD_H 1
/* define if the compiler implements pthread_rwlock_* */
#define HAVE_RWLOCK 1
/* Define if you have the `sigaltstack' function */
#define HAVE_SIGALTSTACK 1
/* Define to 1 if you have the <stdint.h> header file. */
#define HAVE_STDINT_H 1
/* Define to 1 if you have the <stdlib.h> header file. */
#define HAVE_STDLIB_H 1
/* Define to 1 if you have the <strings.h> header file. */
#define HAVE_STRINGS_H 1
/* Define to 1 if you have the <string.h> header file. */
#define HAVE_STRING_H 1
/* Define to 1 if you have the <syscall.h> header file. */
/* #undef HAVE_SYSCALL_H */
/* Define to 1 if you have the <syslog.h> header file. */
#define HAVE_SYSLOG_H 1
/* Define to 1 if you have the <sys/stat.h> header file. */
#define HAVE_SYS_STAT_H 1
/* Define to 1 if you have the <sys/syscall.h> header file. */
#define HAVE_SYS_SYSCALL_H 1
/* Define to 1 if you have the <sys/time.h> header file. */
#define HAVE_SYS_TIME_H 1
/* Define to 1 if you have the <sys/types.h> header file. */
#define HAVE_SYS_TYPES_H 1
/* Define to 1 if you have the <sys/ucontext.h> header file. */
/* #undef HAVE_SYS_UCONTEXT_H */
/* Define to 1 if you have the <sys/utsname.h> header file. */
#define HAVE_SYS_UTSNAME_H 1
/* Define to 1 if you have the <ucontext.h> header file. */
/* #undef HAVE_UCONTEXT_H */
/* Define to 1 if you have the <unistd.h> header file. */
#define HAVE_UNISTD_H 1
/* Define to 1 if you have the <unwind.h> header file. */
#define HAVE_UNWIND_H 1
/* define if the compiler supports using expression for operator */
#define HAVE_USING_OPERATOR 1
/* define if your compiler has __attribute__ */
#define HAVE___ATTRIBUTE__ 1
/* define if your compiler has __builtin_expect */
#define HAVE___BUILTIN_EXPECT 1
/* define if your compiler has __sync_val_compare_and_swap */
#define HAVE___SYNC_VAL_COMPARE_AND_SWAP 1
/* Define to the sub-directory in which libtool stores uninstalled libraries.
*/
#define LT_OBJDIR ".libs/"
/* Name of package */
#define PACKAGE "glog"
/* Define to the address where bug reports for this package should be sent. */
#define PACKAGE_BUGREPORT "opensource@google.com"
/* Define to the full name of this package. */
#define PACKAGE_NAME "glog"
/* Define to the full name and version of this package. */
#define PACKAGE_STRING "glog 0.3.3"
/* Define to the one symbol short name of this package. */
#define PACKAGE_TARNAME "glog"
/* Define to the home page for this package. */
#define PACKAGE_URL ""
/* Define to the version of this package. */
#define PACKAGE_VERSION "0.3.3"
/* How to access the PC from a struct ucontext */
/* #undef PC_FROM_UCONTEXT */
/* Define to necessary symbol if this constant uses a non-standard name on
your system. */
/* #undef PTHREAD_CREATE_JOINABLE */
/* The size of `void *', as computed by sizeof. */
#define SIZEOF_VOID_P 4
/* Define to 1 if you have the ANSI C header files. */
/* #undef STDC_HEADERS */
/* the namespace where STL code like vector<> is defined */
#define STL_NAMESPACE std
/* location of source code */
#define TEST_SRC_DIR "."
/* Version number of package */
#define VERSION "0.3.3"
/* Stops putting the code inside the Google namespace */
#define _END_GOOGLE_NAMESPACE_ }
/* Puts following code inside the Google namespace */
#define _START_GOOGLE_NAMESPACE_ namespace google {
/* TODO(vjn/dreiss): revisit these when use the android-21 (or newer) NDK platform. */
#undef HAVE_SYSCALL_H
#undef HAVE_SYS_SYSCALL_H
#undef OS_LINUX
#undef OS_MACOSX

View File

@ -0,0 +1,6 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE:= jsc
LOCAL_SRC_FILES := jni/$(TARGET_ARCH_ABI)/libjsc.so
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)
include $(PREBUILT_SHARED_LIBRARY)

View File

@ -6,6 +6,7 @@ buildscript {
}
dependencies {
classpath 'com.android.tools.build:gradle:1.3.1'
classpath 'de.undercouch:gradle-download-task:1.2'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files