Use the latest fbjni

This commit is contained in:
Pritesh Nandgaonkar
2018-06-15 15:55:10 +01:00
parent f4b76a3688
commit 72045da4be
115 changed files with 3444 additions and 3784 deletions

View File

@@ -27,6 +27,8 @@ subprojects {
repositories { repositories {
jcenter() jcenter()
google() google()
mavenLocal()
mavenCentral()
} }
} }

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.facebook.libfb">
</manifest>

View File

@@ -1,9 +1,81 @@
# BUILD FILE SYNTAX: SKYLARK load("//build_defs:fb_xplat_cxx_library.bzl", "fb_xplat_cxx_library")
# Copyright (c) 2017-present, Facebook, Inc. load("@xplat//build_defs:fb_java_library.bzl", "fb_java_library")
#
# This source code is licensed under the Apache 2.0 license found in the
# LICENSE file in the root directory of this source tree.
load("//:LITHO_DEFS.bzl", "define_fbjni_targets") ANNOTATIONS_SRCS = [
"java/com/facebook/jni/annotations/*.java",
]
define_fbjni_targets() fb_java_library(
name = "java_annotations",
srcs = glob(ANNOTATIONS_SRCS),
required_for_source_only_abi = True,
)
fb_java_library(
name = "java",
srcs = glob(
["java/**/*.java"],
exclude = ANNOTATIONS_SRCS,
),
required_for_source_only_abi = True,
visibility = ["PUBLIC"],
deps = [
"//libraries/soloader/java/com/facebook/soloader:soloader",
"//third-party/java/jsr-305:jsr-305",
],
exported_deps = [
":java_annotations",
],
)
fb_xplat_cxx_library(
name = "fbjni",
srcs = glob([
"cxx/fbjni/**/*.cpp",
]),
header_namespace = "",
exported_headers = subdir_glob([
("cxx", "fbjni/**/*.h"),
]),
allow_jni_merging = True,
compiler_flags = [
"-fexceptions",
"-fno-omit-frame-pointer",
"-frtti",
"-ffunction-sections",
],
enable_static_variant = True,
exported_platform_headers = [
(
"^(?!android-arm$).*$",
subdir_glob([
("cxx", "lyra/**/*.h"),
]),
),
],
fbandroid_deps = [
"xplat//third-party/linker_lib:atomic",
],
platform_srcs = [
(
"^(?!android-arm$).*$",
glob([
"cxx/lyra/*.cpp",
]),
),
],
preprocessor_flags = [
"-DLOG_TAG=\"libfbjni\"",
],
soname = "libfbjni.$(ext)",
visibility = [
"PUBLIC",
],
deps = [
"xplat//third-party/linker_lib:android",
],
exported_deps = [
"xplat//third-party/linker_lib:log",
"//native/jni-hack:jni-hack",
],
)

41
libs/fbjni/CMakeLists.txt Normal file
View File

@@ -0,0 +1,41 @@
#
# Copyright (c) 2014-present, Facebook, Inc.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
#
cmake_minimum_required(VERSION 3.6.0)
set(CMAKE_VERBOSE_MAKEFILE on)
add_compile_options(
-fno-omit-frame-pointer
-fexceptions
-Wall
-std=c++11
-DDISABLE_CPUCAP
-DDISABLE_XPLAT)
set(FBJNI_CXX ${PROJECT_SOURCE_DIR}/cxx)
list(APPEND FBJNI_HDRS ${FBJNI_CXX})
list(APPEND FBJNI_HDRS ${FBJNI_CXX}/fbjni/)
list(APPEND FBJNI_HDRS ${FBJNI_CXX}/fbjni/detail)
list(APPEND FBJNI_HDRS ${FBJNI_CXX}/lyra)
include_directories(${FBJNI_HDRS})
message(STATUS "FBJNI_HDRS =>" ${FBJNI_HDRS})
file(GLOB FBJNI_SRC
${FBJNI_CXX}/fbjni/*.cpp
${FBJNI_CXX}/fbjni/detail/*.cpp
${FBJNI_CXX}/lyra/*.cpp)
add_library(fb SHARED
${FBJNI_SRC})
#target_include_directories(fb PRIVATE
# include ${libjnihack_DIR})
target_link_libraries(fb log)

View File

@@ -7,9 +7,16 @@ android {
defaultConfig { defaultConfig {
minSdkVersion rootProject.minSdkVersion minSdkVersion rootProject.minSdkVersion
targetSdkVersion rootProject.targetSdkVersion targetSdkVersion rootProject.targetSdkVersion
sourceSets {
main {
manifest.srcFile './ApplicationManifest.xml'
java {
srcDir 'java'
}
}
}
ndk { ndk {
abiFilters 'arm64-v8a', 'x86' abiFilters 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a'
} }
externalNativeBuild { externalNativeBuild {
@@ -20,7 +27,7 @@ android {
} }
externalNativeBuild { externalNativeBuild {
cmake { cmake {
path './src/main/cpp/CMakeLists.txt' path './CMakeLists.txt'
} }
} }
} }
@@ -30,4 +37,5 @@ dependencies {
compileOnly deps.jsr305 compileOnly deps.jsr305
compileOnly deps.inferAnnotations compileOnly deps.inferAnnotations
compileOnly 'com.facebook.litho:litho-annotations:0.15.0' compileOnly 'com.facebook.litho:litho-annotations:0.15.0'
compileOnly 'com.facebook.soloader:soloader:0.5.0'
} }

View File

@@ -1,16 +1,23 @@
/* /**
* Copyright (c) 2016-present, Facebook, Inc. * Copyright 2018-present, Facebook, Inc.
* *
* This source code is licensed under the MIT license found in the * Licensed under the Apache License, Version 2.0 (the "License");
* LICENSE file in the root directory of this source tree. * 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.
*/ */
#include <fb/fbjni/ByteBuffer.h> #include <fbjni/ByteBuffer.h>
#include <stdexcept> #include <stdexcept>
#include <fb/fbjni/References.h>
namespace facebook { namespace facebook {
namespace jni { namespace jni {
@@ -22,6 +29,11 @@ local_ref<JByteBuffer> createEmpty() {
} }
} }
void JBuffer::rewind() const {
static auto meth = javaClassStatic()->getMethod<alias_ref<JBuffer>()>("rewind");
meth(self());
}
local_ref<JByteBuffer> JByteBuffer::wrapBytes(uint8_t* data, size_t size) { local_ref<JByteBuffer> JByteBuffer::wrapBytes(uint8_t* data, size_t size) {
// env->NewDirectByteBuffer requires that size is positive. Android's // env->NewDirectByteBuffer requires that size is positive. Android's
// dalvik returns an invalid result and Android's art aborts if size == 0. // dalvik returns an invalid result and Android's art aborts if size == 0.

View File

@@ -0,0 +1,45 @@
/**
* Copyright 2018-present, Facebook, Inc.
*
* 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.
*/
#pragma once
#include <fbjni/fbjni.h>
namespace facebook {
namespace jni {
class JBuffer : public JavaClass<JBuffer> {
public:
static constexpr const char* kJavaDescriptor = "Ljava/nio/Buffer;";
void rewind() const;
};
// JNI's NIO support has some awkward preconditions and error reporting. This
// class provides much more user-friendly access.
class JByteBuffer : public JavaClass<JByteBuffer, JBuffer> {
public:
static constexpr const char* kJavaDescriptor = "Ljava/nio/ByteBuffer;";
static local_ref<JByteBuffer> wrapBytes(uint8_t* data, size_t size);
bool isDirect() const;
uint8_t* getDirectBytes() const;
size_t getDirectSize() const;
};
}}

View File

@@ -0,0 +1,42 @@
/**
* Copyright 2018-present, Facebook, Inc.
*
* 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.
*/
#pragma once
#include <fbjni/fbjni.h>
#include <fbjni/File.h>
namespace facebook {
namespace jni {
class AContext : public JavaClass<AContext> {
public:
static constexpr const char* kJavaDescriptor = "Landroid/content/Context;";
// Define a method that calls into the represented Java class
local_ref<JFile::javaobject> getCacheDir() {
static const auto method = getClass()->getMethod<JFile::javaobject()>("getCacheDir");
return method(self());
}
local_ref<JFile::javaobject> getFilesDir() {
static const auto method = getClass()->getMethod<JFile::javaobject()>("getFilesDir");
return method(self());
}
};
}
}

View File

@@ -0,0 +1,37 @@
/**
* Copyright 2018-present, Facebook, Inc.
*
* 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.
*/
#pragma once
#include <fbjni/fbjni.h>
namespace facebook {
namespace jni {
class JFile : public JavaClass<JFile> {
public:
static constexpr const char* kJavaDescriptor = "Ljava/io/File;";
// Define a method that calls into the represented Java class
std::string getAbsolutePath() {
static const auto method = getClass()->getMethod<jstring()>("getAbsolutePath");
return method(self())->toStdString();
}
};
}
}

View File

@@ -0,0 +1,66 @@
/**
* Copyright 2018-present, Facebook, Inc.
*
* 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.
*/
#pragma once
#include <fbjni/fbjni.h>
#include <fbjni/NativeRunnable.h>
namespace facebook {
namespace jni {
class JThread : public JavaClass<JThread> {
public:
static constexpr const char* kJavaDescriptor = "Ljava/lang/Thread;";
void start() {
static const auto method = javaClassStatic()->getMethod<void()>("start");
method(self());
}
void join() {
static const auto method = javaClassStatic()->getMethod<void()>("join");
method(self());
}
static local_ref<JThread> create(std::function<void()>&& runnable) {
auto jrunnable = JNativeRunnable::newObjectCxxArgs(std::move(runnable));
return newInstance(static_ref_cast<JRunnable::javaobject>(jrunnable));
}
static local_ref<JThread> create(std::function<void()>&& runnable, std::string&& name) {
auto jrunnable = JNativeRunnable::newObjectCxxArgs(std::move(runnable));
return newInstance(static_ref_cast<JRunnable::javaobject>(jrunnable), make_jstring(std::move(name)));
}
static local_ref<JThread> getCurrent() {
static const auto method = javaClassStatic()->getStaticMethod<local_ref<JThread>()>("currentThread");
return method(javaClassStatic());
}
int getPriority() {
static const auto method = getClass()->getMethod<jint()>("getPriority");
return method(self());
}
void setPriority(int priority) {
static const auto method = getClass()->getMethod<void(int)>("setPriority");
method(self(), priority);
}
};
}
}

View File

@@ -1,15 +1,22 @@
/* /**
* Copyright (c) 2015-present, Facebook, Inc. * Copyright 2018-present, Facebook, Inc.
* *
* This source code is licensed under the MIT license found in the * Licensed under the Apache License, Version 2.0 (the "License");
* LICENSE file in the root directory of this source tree. * 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.
*/ */
#pragma once #pragma once
#include "CoreClasses.h" #include <fbjni/fbjni.h>
#include "Hybrid.h"
#include "Registration.h"
#include <functional> #include <functional>

View File

@@ -0,0 +1,28 @@
/**
* Copyright 2018-present, Facebook, Inc.
*
* 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.
*/
#include <fbjni/fbjni.h>
#include <fbjni/NativeRunnable.h>
using namespace facebook::jni;
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
return facebook::jni::initialize(vm, [] {
HybridDataOnLoad();
JNativeRunnable::OnLoad();
ThreadScope::OnLoad();
});
}

View File

@@ -0,0 +1,31 @@
/**
* Copyright 2018-present, Facebook, Inc.
*
* 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.
*/
#include <fbjni/ReadableByteChannel.h>
namespace facebook {
namespace jni {
int JReadableByteChannel::read(alias_ref<JByteBuffer> dest) const {
if (!self()) {
throwNewJavaException("java/lang/NullPointerException", "java.lang.NullPointerException");
}
static auto method = javaClassStatic()->getMethod<jint(alias_ref<JByteBuffer>)>("read");
return method(self(), dest);
}
}}

View File

@@ -0,0 +1,32 @@
/**
* Copyright 2018-present, Facebook, Inc.
*
* 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.
*/
#pragma once
#include <fbjni/fbjni.h>
#include <fbjni/ByteBuffer.h>
namespace facebook {
namespace jni {
class JReadableByteChannel : public JavaClass<JReadableByteChannel> {
public:
static constexpr const char* kJavaDescriptor = "Ljava/nio/channels/ReadableByteChannel;";
int read(alias_ref<JByteBuffer> dest) const;
};
}}

View File

@@ -1,8 +1,17 @@
/* /**
* Copyright (c) 2015-present, Facebook, Inc. * Copyright 2018-present, Facebook, Inc.
* *
* This source code is licensed under the MIT license found in the * Licensed under the Apache License, Version 2.0 (the "License");
* LICENSE file in the root directory of this source tree. * 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.
*/ */
#pragma once #pragma once
@@ -18,13 +27,13 @@ struct JPrimitive : JavaClass<T> {
using typename JavaClass<T>::javaobject; using typename JavaClass<T>::javaobject;
using JavaClass<T>::javaClassStatic; using JavaClass<T>::javaClassStatic;
static local_ref<javaobject> valueOf(jprim val) { static local_ref<javaobject> valueOf(jprim val) {
static auto cls = javaClassStatic(); static const auto cls = javaClassStatic();
static auto method = static const auto method =
cls->template getStaticMethod<javaobject(jprim)>("valueOf"); cls->template getStaticMethod<javaobject(jprim)>("valueOf");
return method(cls, val); return method(cls, val);
} }
jprim value() const { jprim value() const {
static auto method = static const auto method =
javaClassStatic()->template getMethod<jprim()>(T::kValueMethod); javaClassStatic()->template getMethod<jprim()>(T::kValueMethod);
return method(this->self()); return method(this->self());
} }
@@ -56,9 +65,12 @@ DEFINE_BOXED_PRIMITIVE(double, Double)
#undef DEFINE_BOXED_PRIMITIVE #undef DEFINE_BOXED_PRIMITIVE
struct JVoid : public jni::JavaClass<JVoid> {
static auto constexpr kJavaDescriptor = "Ljava/lang/Void;";
};
inline local_ref<jobject> autobox(alias_ref<jobject> val) { inline local_ref<jobject> autobox(alias_ref<jobject> val) {
return make_local(val); return make_local(val);
} }
}} }}

View File

@@ -1,8 +1,17 @@
/* /**
* Copyright (c) 2015-present, Facebook, Inc. * Copyright 2018-present, Facebook, Inc.
* *
* This source code is licensed under the MIT license found in the * Licensed under the Apache License, Version 2.0 (the "License");
* LICENSE file in the root directory of this source tree. * 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.
*/ */
/** @file Common.h /** @file Common.h
@@ -16,9 +25,6 @@
#include <jni.h> #include <jni.h>
#include <fb/visibility.h>
#include <fb/Environment.h>
#ifdef FBJNI_DEBUG_REFS #ifdef FBJNI_DEBUG_REFS
# ifdef __ANDROID__ # ifdef __ANDROID__
# include <android/log.h> # include <android/log.h>
@@ -43,11 +49,11 @@
namespace facebook { namespace facebook {
namespace jni { namespace jni {
FBEXPORT void throwPendingJniExceptionAsCppException(); void throwPendingJniExceptionAsCppException();
FBEXPORT void throwCppExceptionIf(bool condition); void throwCppExceptionIf(bool condition);
[[noreturn]] FBEXPORT void throwNewJavaException(jthrowable); [[noreturn]] void throwNewJavaException(jthrowable);
[[noreturn]] FBEXPORT void throwNewJavaException(const char* throwableName, const char* msg); [[noreturn]] void throwNewJavaException(const char* throwableName, const char* msg);
template<typename... Args> template<typename... Args>
[[noreturn]] void throwNewJavaException(const char* throwableName, const char* fmt, Args... args); [[noreturn]] void throwNewJavaException(const char* throwableName, const char* fmt, Args... args);
@@ -66,20 +72,10 @@ template<typename... Args>
* unhelpful way (typically a segfault) while trying to handle an exception * unhelpful way (typically a segfault) while trying to handle an exception
* which occurs later. * which occurs later.
*/ */
FBEXPORT jint initialize(JavaVM*, std::function<void()>&&) noexcept; jint initialize(JavaVM*, std::function<void()>&&) noexcept;
namespace internal { 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 // Define to get extremely verbose logging of references and to enable reference stats
#ifdef FBJNI_DEBUG_REFS #ifdef FBJNI_DEBUG_REFS
template<typename... Args> template<typename... Args>

View File

@@ -1,8 +1,17 @@
/* /**
* Copyright (c) 2015-present, Facebook, Inc. * Copyright 2018-present, Facebook, Inc.
* *
* This source code is licensed under the MIT license found in the * Licensed under the Apache License, Version 2.0 (the "License");
* LICENSE file in the root directory of this source tree. * 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.
*/ */
#pragma once #pragma once
@@ -22,15 +31,15 @@ namespace jni {
// jobject ///////////////////////////////////////////////////////////////////////////////////////// // jobject /////////////////////////////////////////////////////////////////////////////////////////
inline bool isSameObject(alias_ref<JObject> lhs, alias_ref<JObject> rhs) noexcept { inline bool isSameObject(alias_ref<JObject> lhs, alias_ref<JObject> rhs) noexcept {
return internal::getEnv()->IsSameObject(lhs.get(), rhs.get()) != JNI_FALSE; return Environment::current()->IsSameObject(lhs.get(), rhs.get()) != JNI_FALSE;
} }
inline local_ref<JClass> JObject::getClass() const noexcept { inline local_ref<JClass> JObject::getClass() const noexcept {
return adopt_local(internal::getEnv()->GetObjectClass(self())); return adopt_local(Environment::current()->GetObjectClass(self()));
} }
inline bool JObject::isInstanceOf(alias_ref<JClass> cls) const noexcept { inline bool JObject::isInstanceOf(alias_ref<JClass> cls) const noexcept {
return internal::getEnv()->IsInstanceOf(self(), cls.get()) != JNI_FALSE; return Environment::current()->IsInstanceOf(self(), cls.get()) != JNI_FALSE;
} }
template<typename T> template<typename T>
@@ -49,7 +58,7 @@ inline void JObject::setFieldValue(JField<T> field, T value) noexcept {
} }
inline std::string JObject::toString() const { inline std::string JObject::toString() const {
static auto method = findClassLocal("java/lang/Object")->getMethod<jstring()>("toString"); static const auto method = findClassLocal("java/lang/Object")->getMethod<jstring()>("toString");
return method(self())->toStdString(); return method(self())->toStdString();
} }
@@ -78,13 +87,13 @@ MonitorLock::MonitorLock() noexcept : owned_(nullptr) {}
MonitorLock::MonitorLock(alias_ref<JObject> object) noexcept MonitorLock::MonitorLock(alias_ref<JObject> object) noexcept
: owned_(object) { : owned_(object) {
internal::getEnv()->MonitorEnter(object.get()); Environment::current()->MonitorEnter(object.get());
} }
void MonitorLock::reset() noexcept { void MonitorLock::reset() noexcept {
if (owned_) { if (owned_) {
internal::getEnv()->MonitorExit(owned_.get()); Environment::current()->MonitorExit(owned_.get());
if (internal::getEnv()->ExceptionCheck()) { if (Environment::current()->ExceptionCheck()) {
abort(); // Lock mismatch abort(); // Lock mismatch
} }
owned_ = nullptr; owned_ = nullptr;
@@ -127,7 +136,7 @@ namespace detail {
template<typename JC, typename... Args> template<typename JC, typename... Args>
static local_ref<JC> newInstance(Args... args) { static local_ref<JC> newInstance(Args... args) {
static auto cls = JC::javaClassStatic(); static auto cls = JC::javaClassStatic();
static auto constructor = cls->template getConstructor<typename JC::javaobject(Args...)>(); static const auto constructor = cls->template getConstructor<typename JC::javaobject(Args...)>();
return cls->newObject(constructor, args...); return cls->newObject(constructor, args...);
} }
} }
@@ -155,17 +164,18 @@ struct NativeMethod {
}; };
inline local_ref<JClass> JClass::getSuperclass() const noexcept { inline local_ref<JClass> JClass::getSuperclass() const noexcept {
return adopt_local(internal::getEnv()->GetSuperclass(self())); return adopt_local(Environment::current()->GetSuperclass(self()));
} }
inline void JClass::registerNatives(std::initializer_list<NativeMethod> methods) { inline void JClass::registerNatives(std::initializer_list<NativeMethod> methods) {
const auto env = internal::getEnv(); const auto env = Environment::current();
JNINativeMethod jnimethods[methods.size()]; JNINativeMethod jnimethods[methods.size()];
size_t i = 0; size_t i = 0;
for (auto it = methods.begin(); it < methods.end(); ++it, ++i) { for (auto it = methods.begin(); it < methods.end(); ++it, ++i) {
jnimethods[i].name = it->name; // The JNI struct members are unnecessarily non-const.
jnimethods[i].signature = it->descriptor.c_str(); jnimethods[i].name = const_cast<char*>(it->name);
jnimethods[i].signature = const_cast<char*>(it->descriptor.c_str());
jnimethods[i].fnPtr = reinterpret_cast<void*>(it->wrapper); jnimethods[i].fnPtr = reinterpret_cast<void*>(it->wrapper);
} }
@@ -174,8 +184,13 @@ inline void JClass::registerNatives(std::initializer_list<NativeMethod> methods)
} }
inline bool JClass::isAssignableFrom(alias_ref<JClass> other) const noexcept { inline bool JClass::isAssignableFrom(alias_ref<JClass> other) const noexcept {
const auto env = internal::getEnv(); const auto env = Environment::current();
const auto result = env->IsAssignableFrom(self(), other.get()); // Ths method has behavior compatible with the
// java.lang.Class#isAssignableFrom method. The order of the
// arguments to the JNI IsAssignableFrom C function is "opposite"
// from what some might expect, which makes this code look a little
// odd, but it is correct.
const auto result = env->IsAssignableFrom(other.get(), self());
return result; return result;
} }
@@ -199,7 +214,7 @@ template<typename F>
inline JMethod<F> JClass::getMethod( inline JMethod<F> JClass::getMethod(
const char* name, const char* name,
const char* descriptor) const { const char* descriptor) const {
const auto env = internal::getEnv(); const auto env = Environment::current();
const auto method = env->GetMethodID(self(), name, descriptor); const auto method = env->GetMethodID(self(), name, descriptor);
FACEBOOK_JNI_THROW_EXCEPTION_IF(!method); FACEBOOK_JNI_THROW_EXCEPTION_IF(!method);
return JMethod<F>{method}; return JMethod<F>{method};
@@ -214,7 +229,7 @@ template<typename F>
inline JStaticMethod<F> JClass::getStaticMethod( inline JStaticMethod<F> JClass::getStaticMethod(
const char* name, const char* name,
const char* descriptor) const { const char* descriptor) const {
const auto env = internal::getEnv(); const auto env = Environment::current();
const auto method = env->GetStaticMethodID(self(), name, descriptor); const auto method = env->GetStaticMethodID(self(), name, descriptor);
FACEBOOK_JNI_THROW_EXCEPTION_IF(!method); FACEBOOK_JNI_THROW_EXCEPTION_IF(!method);
return JStaticMethod<F>{method}; return JStaticMethod<F>{method};
@@ -229,7 +244,7 @@ template<typename F>
inline JNonvirtualMethod<F> JClass::getNonvirtualMethod( inline JNonvirtualMethod<F> JClass::getNonvirtualMethod(
const char* name, const char* name,
const char* descriptor) const { const char* descriptor) const {
const auto env = internal::getEnv(); const auto env = Environment::current();
const auto method = env->GetMethodID(self(), name, descriptor); const auto method = env->GetMethodID(self(), name, descriptor);
FACEBOOK_JNI_THROW_EXCEPTION_IF(!method); FACEBOOK_JNI_THROW_EXCEPTION_IF(!method);
return JNonvirtualMethod<F>{method}; return JNonvirtualMethod<F>{method};
@@ -245,7 +260,7 @@ template<typename T>
inline JField<enable_if_t<IsJniScalar<T>(), T>> JClass::getField( inline JField<enable_if_t<IsJniScalar<T>(), T>> JClass::getField(
const char* name, const char* name,
const char* descriptor) const { const char* descriptor) const {
const auto env = internal::getEnv(); const auto env = Environment::current();
auto field = env->GetFieldID(self(), name, descriptor); auto field = env->GetFieldID(self(), name, descriptor);
FACEBOOK_JNI_THROW_EXCEPTION_IF(!field); FACEBOOK_JNI_THROW_EXCEPTION_IF(!field);
return JField<T>{field}; return JField<T>{field};
@@ -261,7 +276,7 @@ template<typename T>
inline JStaticField<enable_if_t<IsJniScalar<T>(), T>> JClass::getStaticField( inline JStaticField<enable_if_t<IsJniScalar<T>(), T>> JClass::getStaticField(
const char* name, const char* name,
const char* descriptor) const { const char* descriptor) const {
const auto env = internal::getEnv(); const auto env = Environment::current();
auto field = env->GetStaticFieldID(self(), name, descriptor); auto field = env->GetStaticFieldID(self(), name, descriptor);
FACEBOOK_JNI_THROW_EXCEPTION_IF(!field); FACEBOOK_JNI_THROW_EXCEPTION_IF(!field);
return JStaticField<T>{field}; return JStaticField<T>{field};
@@ -286,7 +301,7 @@ template<typename R, typename... Args>
inline local_ref<R> JClass::newObject( inline local_ref<R> JClass::newObject(
JConstructor<R(Args...)> constructor, JConstructor<R(Args...)> constructor,
Args... args) const { Args... args) const {
const auto env = internal::getEnv(); const auto env = Environment::current();
auto object = env->NewObject(self(), constructor.getId(), auto object = env->NewObject(self(), constructor.getId(),
detail::callToJni( detail::callToJni(
detail::Convert<typename std::decay<Args>::type>::toCall(args))...); detail::Convert<typename std::decay<Args>::type>::toCall(args))...);
@@ -339,46 +354,11 @@ struct Convert<const char*> {
}; };
} }
// JStackTrace //////////////////////////////////////////////////////////////////////////////////////
inline auto JStackTraceElement::create(
const std::string& declaringClass, const std::string& methodName, const std::string& file, int line)
-> local_ref<javaobject> {
return newInstance(declaringClass, methodName, file, line);
}
inline std::string JStackTraceElement::getClassName() const {
static auto meth = javaClassStatic()->getMethod<local_ref<JString>()>("getClassName");
return meth(self())->toStdString();
}
inline std::string JStackTraceElement::getMethodName() const {
static auto meth = javaClassStatic()->getMethod<local_ref<JString>()>("getMethodName");
return meth(self())->toStdString();
}
inline std::string JStackTraceElement::getFileName() const {
static auto meth = javaClassStatic()->getMethod<local_ref<JString>()>("getFileName");
return meth(self())->toStdString();
}
inline int JStackTraceElement::getLineNumber() const {
static auto meth = javaClassStatic()->getMethod<jint()>("getLineNumber");
return meth(self());
}
// jthrowable //////////////////////////////////////////////////////////////////////////////////////
inline local_ref<JThrowable> JThrowable::initCause(alias_ref<JThrowable> cause) {
static auto meth = javaClassStatic()->getMethod<javaobject(javaobject)>("initCause");
return meth(self(), cause.get());
}
// jtypeArray ////////////////////////////////////////////////////////////////////////////////////// // jtypeArray //////////////////////////////////////////////////////////////////////////////////////
namespace detail { namespace detail {
inline size_t JArray::size() const noexcept { inline size_t JArray::size() const noexcept {
const auto env = internal::getEnv(); const auto env = Environment::current();
return env->GetArrayLength(self()); return env->GetArrayLength(self());
} }
} }
@@ -438,8 +418,8 @@ std::string JArrayClass<T>::get_instantiated_base_name() {
template<typename T> template<typename T>
auto JArrayClass<T>::newArray(size_t size) -> local_ref<javaobject> { auto JArrayClass<T>::newArray(size_t size) -> local_ref<javaobject> {
static auto elementClass = findClassStatic(jtype_traits<T>::base_name().c_str()); static const auto elementClass = findClassStatic(jtype_traits<T>::base_name().c_str());
const auto env = internal::getEnv(); const auto env = Environment::current();
auto rawArray = env->NewObjectArray(size, elementClass.get(), nullptr); auto rawArray = env->NewObjectArray(size, elementClass.get(), nullptr);
FACEBOOK_JNI_THROW_EXCEPTION_IF(!rawArray); FACEBOOK_JNI_THROW_EXCEPTION_IF(!rawArray);
return adopt_local(static_cast<javaobject>(rawArray)); return adopt_local(static_cast<javaobject>(rawArray));
@@ -447,13 +427,13 @@ auto JArrayClass<T>::newArray(size_t size) -> local_ref<javaobject> {
template<typename T> template<typename T>
inline void JArrayClass<T>::setElement(size_t idx, const T& value) { inline void JArrayClass<T>::setElement(size_t idx, const T& value) {
const auto env = internal::getEnv(); const auto env = Environment::current();
env->SetObjectArrayElement(this->self(), idx, value); env->SetObjectArrayElement(this->self(), idx, value);
} }
template<typename T> template<typename T>
inline local_ref<T> JArrayClass<T>::getElement(size_t idx) { inline local_ref<T> JArrayClass<T>::getElement(size_t idx) {
const auto env = internal::getEnv(); const auto env = Environment::current();
auto rawElement = env->GetObjectArrayElement(this->self(), idx); auto rawElement = env->GetObjectArrayElement(this->self(), idx);
return adopt_local(static_cast<T>(rawElement)); return adopt_local(static_cast<T>(rawElement));
} }
@@ -463,6 +443,11 @@ inline detail::ElementProxy<JArrayClass<T>> JArrayClass<T>::operator[](size_t in
return detail::ElementProxy<JArrayClass<T>>(this, index); return detail::ElementProxy<JArrayClass<T>>(this, index);
} }
template<typename T>
local_ref<typename JArrayClass<T>::javaobject> adopt_local_array(jobjectArray ref) {
return adopt_local(static_cast<typename JArrayClass<T>::javaobject>(ref));
}
// jarray ///////////////////////////////////////////////////////////////////////////////////////// // jarray /////////////////////////////////////////////////////////////////////////////////////////
template <typename JArrayType> template <typename JArrayType>
@@ -537,7 +522,7 @@ class PinnedCriticalAlloc {
T** elements, T** elements,
size_t* size, size_t* size,
jboolean* isCopy) { jboolean* isCopy) {
const auto env = internal::getEnv(); const auto env = Environment::current();
*elements = static_cast<T*>(env->GetPrimitiveArrayCritical(array.get(), isCopy)); *elements = static_cast<T*>(env->GetPrimitiveArrayCritical(array.get(), isCopy));
FACEBOOK_JNI_THROW_EXCEPTION_IF(!elements); FACEBOOK_JNI_THROW_EXCEPTION_IF(!elements);
*size = array->size(); *size = array->size();
@@ -548,7 +533,7 @@ class PinnedCriticalAlloc {
jint start, jint start,
jint size, jint size,
jint mode) { jint mode) {
const auto env = internal::getEnv(); const auto env = Environment::current();
env->ReleasePrimitiveArrayCritical(array.get(), elements, mode); env->ReleasePrimitiveArrayCritical(array.get(), elements, mode);
} }
}; };

View File

@@ -1,8 +1,17 @@
/* /**
* Copyright (c) 2015-present, Facebook, Inc. * Copyright 2018-present, Facebook, Inc.
* *
* This source code is licensed under the MIT license found in the * Licensed under the Apache License, Version 2.0 (the "License");
* LICENSE file in the root directory of this source tree. * 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.
*/ */
#pragma once #pragma once
@@ -16,14 +25,11 @@
#include "References-forward.h" #include "References-forward.h"
#include "Meta-forward.h" #include "Meta-forward.h"
#include "TypeTraits.h" #include "TypeTraits.h"
#include <sstream>
#include <memory> #include <memory>
#include <jni.h> #include <jni.h>
#include <fb/visibility.h>
namespace facebook { namespace facebook {
namespace jni { namespace jni {
@@ -39,7 +45,7 @@ class JObject;
/// in a "static auto" variable, or a static global. /// in a "static auto" variable, or a static global.
/// ///
/// @return Returns a leaked global reference to the class /// @return Returns a leaked global reference to the class
FBEXPORT alias_ref<JClass> findClassStatic(const char* name); alias_ref<JClass> findClassStatic(const char* name);
/// Lookup a class by name. Note this functions returns a local reference, /// Lookup a class by name. Note this functions returns a local reference,
/// which means that it must not be stored in a static variable. /// which means that it must not be stored in a static variable.
@@ -48,12 +54,12 @@ FBEXPORT alias_ref<JClass> findClassStatic(const char* name);
/// (like caching method ids). /// (like caching method ids).
/// ///
/// @return Returns a global reference to the class /// @return Returns a global reference to the class
FBEXPORT local_ref<JClass> findClassLocal(const char* name); local_ref<JClass> findClassLocal(const char* name);
/// Check to see if two references refer to the same object. Comparison with nullptr /// 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 /// returns true if and only if compared to another nullptr. A weak reference that
/// refers to a reclaimed object count as nullptr. /// refers to a reclaimed object count as nullptr.
FBEXPORT bool isSameObject(alias_ref<JObject> lhs, alias_ref<JObject> rhs) noexcept; bool isSameObject(alias_ref<JObject> lhs, alias_ref<JObject> rhs) noexcept;
// Together, these classes allow convenient use of any class with the fbjni // Together, these classes allow convenient use of any class with the fbjni
// helpers. To use: // helpers. To use:
@@ -72,7 +78,7 @@ FBEXPORT bool isSameObject(alias_ref<JObject> lhs, alias_ref<JObject> rhs) noexc
// constexpr static auto kJavaDescriptor = "Lcom/example/package/MyClass;"; // constexpr static auto kJavaDescriptor = "Lcom/example/package/MyClass;";
// //
// void foo() { // void foo() {
// static auto method = javaClassStatic()->getMethod<void()>("foo"); // static const auto method = javaClassStatic()->getMethod<void()>("foo");
// method(self()); // method(self());
// } // }
// //
@@ -96,7 +102,7 @@ static local_ref<JC> newInstance(Args... args);
class MonitorLock; class MonitorLock;
class FBEXPORT JObject : detail::JObjectBase { class JObject : detail::JObjectBase {
public: public:
static constexpr auto kJavaDescriptor = "Ljava/lang/Object;"; static constexpr auto kJavaDescriptor = "Ljava/lang/Object;";
@@ -192,7 +198,7 @@ struct JTypeFor<T, Base, void> {
// jthrowable) to be used as javaobject. This should only be necessary for // jthrowable) to be used as javaobject. This should only be necessary for
// built-in jni types and not user-defined ones. // built-in jni types and not user-defined ones.
template <typename T, typename Base = JObject, typename JType = void> template <typename T, typename Base = JObject, typename JType = void>
class FBEXPORT JavaClass : public Base { class JavaClass : public Base {
using JObjType = typename detail::JTypeFor<T, Base, JType>; using JObjType = typename detail::JTypeFor<T, Base, JType>;
public: public:
using _javaobject = typename JObjType::_javaobject; using _javaobject = typename JObjType::_javaobject;
@@ -220,7 +226,7 @@ protected:
/// Wrapper to provide functionality to jclass references /// Wrapper to provide functionality to jclass references
struct NativeMethod; struct NativeMethod;
class FBEXPORT JClass : public JavaClass<JClass, JObject, jclass> { class JClass : public JavaClass<JClass, JObject, jclass> {
public: public:
/// Java type descriptor /// Java type descriptor
static constexpr const char* kJavaDescriptor = "Ljava/lang/Class;"; static constexpr const char* kJavaDescriptor = "Ljava/lang/Class;";
@@ -325,19 +331,23 @@ private:
void registerNatives(const char* name, std::initializer_list<NativeMethod> methods); void registerNatives(const char* name, std::initializer_list<NativeMethod> methods);
/// Wrapper to provide functionality to jstring references /// Wrapper to provide functionality to jstring references
class FBEXPORT JString : public JavaClass<JString, JObject, jstring> { class JString : public JavaClass<JString, JObject, jstring> {
public: public:
/// Java type descriptor /// Java type descriptor
static constexpr const char* kJavaDescriptor = "Ljava/lang/String;"; static constexpr const char* kJavaDescriptor = "Ljava/lang/String;";
/// Convenience method to convert a jstring object to a std::string /// Convenience method to convert a jstring object to a std::string
std::string toStdString() const; std::string toStdString() const;
/// Convenience method to convert a jstring object to a std::u16string
std::u16string toU16String() const;
}; };
/// Convenience functions to convert a std::string or const char* into a @ref local_ref to a /// Convenience functions to convert a const char*, std::string, or std::u16string
/// jstring /// into a @ref local_ref to a jstring.
FBEXPORT local_ref<JString> make_jstring(const char* modifiedUtf8); local_ref<JString> make_jstring(const char* modifiedUtf8);
FBEXPORT local_ref<JString> make_jstring(const std::string& modifiedUtf8); local_ref<JString> make_jstring(const std::string& modifiedUtf8);
local_ref<JString> make_jstring(const std::u16string& utf16);
namespace detail { namespace detail {
template<typename Target> template<typename Target>
@@ -365,7 +375,7 @@ class ElementProxy {
} }
namespace detail { namespace detail {
class FBEXPORT JArray : public JavaClass<JArray, JObject, jarray> { class JArray : public JavaClass<JArray, JObject, jarray> {
public: public:
// This cannot be used in a scope that derives a descriptor (like in a method // This cannot be used in a scope that derives a descriptor (like in a method
// signature). Use a more derived type instead (like JArrayInt or // signature). Use a more derived type instead (like JArrayInt or
@@ -377,7 +387,7 @@ class FBEXPORT JArray : public JavaClass<JArray, JObject, jarray> {
// This is used so that the JArrayClass<T> javaobject extends jni's // This is used so that the JArrayClass<T> javaobject extends jni's
// jobjectArray. This class should not be used directly. A general Object[] // jobjectArray. This class should not be used directly. A general Object[]
// should use JArrayClass<jobject>. // should use JArrayClass<jobject>.
class FBEXPORT JTypeArray : public JavaClass<JTypeArray, JArray, jobjectArray> { class JTypeArray : public JavaClass<JTypeArray, JArray, jobjectArray> {
// This cannot be used in a scope that derives a descriptor (like in a method // This cannot be used in a scope that derives a descriptor (like in a method
// signature). // signature).
static constexpr const char* kJavaDescriptor = nullptr; static constexpr const char* kJavaDescriptor = nullptr;
@@ -427,36 +437,13 @@ template <typename T>
using jtypeArray = typename JArrayClass<T>::javaobject; using jtypeArray = typename JArrayClass<T>::javaobject;
template<typename T> template<typename T>
local_ref<typename JArrayClass<T>::javaobject> adopt_local_array(jobjectArray ref) { local_ref<typename JArrayClass<T>::javaobject> adopt_local_array(jobjectArray ref);
return adopt_local(static_cast<typename JArrayClass<T>::javaobject>(ref));
}
template<typename Target> template<typename Target>
local_ref<typename Target::javaentry> adopt_local(detail::ElementProxy<Target> elementProxy) { local_ref<typename Target::javaentry> adopt_local(detail::ElementProxy<Target> elementProxy) {
return static_cast<local_ref<typename Target::javaentry>>(elementProxy); return static_cast<local_ref<typename Target::javaentry>>(elementProxy);
} }
struct FBEXPORT JStackTraceElement : JavaClass<JStackTraceElement> {
static auto constexpr kJavaDescriptor = "Ljava/lang/StackTraceElement;";
static local_ref<javaobject> create(const std::string& declaringClass, const std::string& methodName, const std::string& file, int line);
std::string getClassName() const;
std::string getMethodName() const;
std::string getFileName() const;
int getLineNumber() const;
};
/// Wrapper to provide functionality to jthrowable references
class FBEXPORT JThrowable : public JavaClass<JThrowable, JObject, jthrowable> {
public:
static constexpr const char* kJavaDescriptor = "Ljava/lang/Throwable;";
local_ref<JThrowable> initCause(alias_ref<JThrowable> cause);
using JStackTrace = JArrayClass<JStackTraceElement::javaobject>;
local_ref<JStackTrace> getStackTrace();
};
template <typename T, typename PinAlloc> template <typename T, typename PinAlloc>
class PinnedPrimitiveArray; class PinnedPrimitiveArray;
@@ -468,7 +455,7 @@ template <typename T> class PinnedCriticalAlloc;
/// This is an empty holder by itself. Construct a PinnedPrimitiveArray to actually interact with /// This is an empty holder by itself. Construct a PinnedPrimitiveArray to actually interact with
/// the elements of the array. /// the elements of the array.
template <typename JArrayType> template <typename JArrayType>
class FBEXPORT JPrimitiveArray : class JPrimitiveArray :
public JavaClass<JPrimitiveArray<JArrayType>, detail::JArray, JArrayType> { public JavaClass<JPrimitiveArray<JArrayType>, detail::JArray, JArrayType> {
static_assert(is_jni_primitive_array<JArrayType>(), ""); static_assert(is_jni_primitive_array<JArrayType>(), "");
public: public:
@@ -508,14 +495,14 @@ private:
void releaseElements(T* elements, jint mode); void releaseElements(T* elements, jint mode);
}; };
FBEXPORT local_ref<jbooleanArray> make_boolean_array(jsize size); local_ref<jbooleanArray> make_boolean_array(jsize size);
FBEXPORT local_ref<jbyteArray> make_byte_array(jsize size); local_ref<jbyteArray> make_byte_array(jsize size);
FBEXPORT local_ref<jcharArray> make_char_array(jsize size); local_ref<jcharArray> make_char_array(jsize size);
FBEXPORT local_ref<jshortArray> make_short_array(jsize size); local_ref<jshortArray> make_short_array(jsize size);
FBEXPORT local_ref<jintArray> make_int_array(jsize size); local_ref<jintArray> make_int_array(jsize size);
FBEXPORT local_ref<jlongArray> make_long_array(jsize size); local_ref<jlongArray> make_long_array(jsize size);
FBEXPORT local_ref<jfloatArray> make_float_array(jsize size); local_ref<jfloatArray> make_float_array(jsize size);
FBEXPORT local_ref<jdoubleArray> make_double_array(jsize size); local_ref<jdoubleArray> make_double_array(jsize size);
using JArrayBoolean = JPrimitiveArray<jbooleanArray>; using JArrayBoolean = JPrimitiveArray<jbooleanArray>;
using JArrayByte = JPrimitiveArray<jbyteArray>; using JArrayByte = JPrimitiveArray<jbyteArray>;
@@ -577,6 +564,29 @@ class PinnedPrimitiveArray {
friend class JPrimitiveArray<typename jtype_traits<T>::array_type>; friend class JPrimitiveArray<typename jtype_traits<T>::array_type>;
}; };
struct JStackTraceElement : JavaClass<JStackTraceElement> {
static auto constexpr kJavaDescriptor = "Ljava/lang/StackTraceElement;";
static local_ref<javaobject> create(const std::string& declaringClass, const std::string& methodName, const std::string& file, int line);
std::string getClassName() const;
std::string getMethodName() const;
std::string getFileName() const;
int getLineNumber() const;
};
/// Wrapper to provide functionality to jthrowable references
class JThrowable : public JavaClass<JThrowable, JObject, jthrowable> {
public:
static constexpr const char* kJavaDescriptor = "Ljava/lang/Throwable;";
using JStackTrace = JArrayClass<JStackTraceElement::javaobject>;
local_ref<JThrowable> initCause(alias_ref<JThrowable> cause);
local_ref<JStackTrace> getStackTrace();
void setStackTrace(alias_ref<JArrayClass<JStackTraceElement::javaobject>>);
};
#pragma push_macro("PlainJniRefMap") #pragma push_macro("PlainJniRefMap")
#undef PlainJniRefMap #undef PlainJniRefMap
#define PlainJniRefMap(rtype, jtype) \ #define PlainJniRefMap(rtype, jtype) \

View File

@@ -0,0 +1,301 @@
/**
* Copyright 2018-present, Facebook, Inc.
*
* 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.
*/
#include <fbjni/fbjni.h>
#include <functional>
#include <pthread.h>
namespace facebook {
namespace jni {
namespace {
JavaVM* g_vm = nullptr;
struct EnvironmentInitializer {
EnvironmentInitializer(JavaVM* vm) {
FBJNI_ASSERT(!g_vm);
FBJNI_ASSERT(vm);
g_vm = vm;
}
};
int getEnv(JNIEnv** env) {
FBJNI_ASSERT(g_vm);
// g_vm->GetEnv() might not clear the env* in failure cases.
*env = nullptr;
jint ret = g_vm->GetEnv((void**)env, JNI_VERSION_1_6);
// Other possibilites are that JNI_VERSION_1_6 is invalid, or some
// unknown return was received.
FBJNI_ASSERT(ret == JNI_OK || ret == JNI_EDETACHED);
return ret;
}
// Some jni.h define the first arg to AttachCurrentThread as void**,
// and some as JNIEnv**. This hack allows both to work.
template <typename>
struct AttachTraits;
template <>
struct AttachTraits<jint(JavaVM::*)(JNIEnv**, void*)> {
using EnvType = JNIEnv*;
};
template <>
struct AttachTraits<jint(JavaVM::*)(void**, void*)> {
using EnvType = void*;
};
JNIEnv* attachCurrentThread() {
JavaVMAttachArgs args{JNI_VERSION_1_6, nullptr, nullptr};
using AttachEnvType =
typename AttachTraits<decltype(&JavaVM::AttachCurrentThread)>::EnvType;
AttachEnvType env;
auto result = g_vm->AttachCurrentThread(&env, &args);
FBJNI_ASSERT(result == JNI_OK);
return reinterpret_cast<JNIEnv*>(env);
}
}
/* static */
void Environment::initialize(JavaVM* vm) {
static EnvironmentInitializer init(vm);
}
namespace {
pthread_key_t makeKey() {
pthread_key_t key;
int ret = pthread_key_create(&key, nullptr);
if (ret != 0) {
FBJNI_LOGF("pthread_key_create failed: %d", ret);
}
return key;
}
pthread_key_t getTLKey() {
static pthread_key_t key = makeKey();
return key;
}
inline detail::TLData* getTLData(pthread_key_t key) {
return reinterpret_cast<detail::TLData*>(pthread_getspecific(key));
}
inline void setTLData(pthread_key_t key, detail::TLData* data) {
int ret = pthread_setspecific(key, data);
if (ret != 0) {
(void) ret;
FBJNI_LOGF("pthread_setspecific failed: %d", ret);
}
}
// This returns non-nullptr iff the env was cached from java. So it
// can return nullptr for a thread which has been registered.
inline JNIEnv* cachedOrNull() {
detail::TLData* pdata = getTLData(getTLKey());
return (pdata ? pdata->env : nullptr);
}
}
namespace detail {
// This will return a cached env if there is one, or get one from JNI
// if the thread has already been attached some other way. If it
// returns nullptr, then the thread has never been registered, or the
// VM has never been set up for fbjni.
JNIEnv* currentOrNull() {
if (!g_vm) {
return nullptr;
}
detail::TLData* pdata = getTLData(getTLKey());
if (pdata && pdata->env) {
return pdata->env;
}
JNIEnv* env;
if (getEnv(&env) != JNI_OK) {
// If there's a ThreadScope on the stack, we should have gotten a
// JNIEnv and not ended up here.
FBJNI_ASSERT(!pdata || !pdata->attached);
}
return env;
}
// To understand JniEnvCacher and ThreadScope, it is helpful to
// realize that if a flagged JniEnvCacher is on the stack, then a
// flagged ThreadScope cannot be after it. If a flagged ThreadCacher
// is on the stack, then a JniEnvCacher *can* be after it. So,
// ThreadScope's setup and teardown can both assume they are the
// first/last interesting objects, but this is not true of
// JniEnvCacher.
JniEnvCacher::JniEnvCacher(JNIEnv* env)
: thisCached_(false)
{
FBJNI_ASSERT(env);
pthread_key_t key = getTLKey();
detail::TLData* pdata = getTLData(key);
if (pdata && pdata->env) {
return;
}
if (!pdata) {
pdata = &data_;
setTLData(key, pdata);
pdata->attached = false;
} else {
FBJNI_ASSERT(!pdata->env);
}
pdata->env = env;
thisCached_ = true;
}
JniEnvCacher::~JniEnvCacher() {
if (!thisCached_) {
return;
}
pthread_key_t key = getTLKey();
TLData* pdata = getTLData(key);
FBJNI_ASSERT(pdata);
FBJNI_ASSERT(pdata->env != nullptr);
pdata->env = nullptr;
if (!pdata->attached) {
setTLData(key, nullptr);
}
}
}
ThreadScope::ThreadScope()
: thisAttached_(false)
{
if (g_vm == nullptr) {
throw std::runtime_error("fbjni is uninitialized; no thread can be attached.");
}
JNIEnv* env;
// Check if the thread is attached somehow.
auto result = getEnv(&env);
if (result == JNI_OK) {
return;
}
// At this point, it appears there's no thread attached and no env is
// cached, or we would have returned already. So there better not
// be TLData.
pthread_key_t key = getTLKey();
detail::TLData* pdata = getTLData(key);
FBJNI_ASSERT(pdata == nullptr);
setTLData(key, &data_);
attachCurrentThread();
data_.env = nullptr;
data_.attached = true;
thisAttached_ = true;
}
ThreadScope::~ThreadScope() {
if (!thisAttached_) {
return;
}
pthread_key_t key = getTLKey();
detail::TLData* pdata = getTLData(key);
FBJNI_ASSERT(pdata);
FBJNI_ASSERT(pdata->env == nullptr);
FBJNI_ASSERT(pdata->attached);
FBJNI_ASSERT(g_vm);
g_vm->DetachCurrentThread();
setTLData(key, nullptr);
}
/* static */
JNIEnv* Environment::current() {
FBJNI_ASSERT(g_vm);
JNIEnv* env = detail::currentOrNull();
if (env == nullptr) {
throw std::runtime_error("Unable to retrieve jni environment. Is the thread attached?");
}
return env;
}
/* static */
JNIEnv* Environment::ensureCurrentThreadIsAttached() {
FBJNI_ASSERT(g_vm);
JNIEnv* env = detail::currentOrNull();
if (env == nullptr) {
env = attachCurrentThread();
FBJNI_ASSERT(env);
}
return env;
}
namespace {
struct JThreadScopeSupport : JavaClass<JThreadScopeSupport> {
static auto constexpr kJavaDescriptor = "Lcom/facebook/jni/ThreadScopeSupport;";
// These reinterpret_casts are a totally dangerous pattern. Don't use them. Use HybridData instead.
static void runStdFunction(std::function<void()>&& func) {
static const auto method = javaClassStatic()->getStaticMethod<void(jlong)>("runStdFunction");
method(javaClassStatic(), reinterpret_cast<jlong>(&func));
}
static void runStdFunctionImpl(alias_ref<JClass>, jlong ptr) {
(*reinterpret_cast<std::function<void()>*>(ptr))();
}
static void OnLoad() {
// We need the javaClassStatic so that the class lookup is cached and that
// runStdFunction can be called from a ThreadScope-attached thread.
javaClassStatic()->registerNatives({
makeNativeMethod("runStdFunctionImpl", runStdFunctionImpl),
});
}
};
}
/* static */
void ThreadScope::OnLoad() {
// These classes are required for ScopeWithClassLoader. Ensure they are looked up when loading.
JThreadScopeSupport::OnLoad();
}
/* static */
void ThreadScope::WithClassLoader(std::function<void()>&& runnable) {
if (cachedOrNull() == nullptr) {
ThreadScope ts;
JThreadScopeSupport::runStdFunction(std::move(runnable));
} else {
runnable();
}
}
} }

View File

@@ -1,8 +1,17 @@
/* /**
* Copyright (c) 2015-present, Facebook, Inc. * Copyright 2018-present, Facebook, Inc.
* *
* This source code is licensed under the MIT license found in the * Licensed under the Apache License, Version 2.0 (the "License");
* LICENSE file in the root directory of this source tree. * 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.
*/ */
#pragma once #pragma once
@@ -10,24 +19,69 @@
#include <string> #include <string>
#include <jni.h> #include <jni.h>
#include <fb/visibility.h>
namespace facebook { namespace facebook {
namespace jni { namespace jni {
// Keeps a thread-local reference to the current thread's JNIEnv. // Keeps a thread-local reference to the current thread's JNIEnv.
struct Environment { struct Environment {
// May be null if this thread isn't attached to the JVM // Throws a std::runtime_error if this thread isn't attached to the JVM
FBEXPORT static JNIEnv* current(); // TODO(T6594868) Benchmark against raw JNI access
static JNIEnv* current();
static void initialize(JavaVM* vm); static void initialize(JavaVM* vm);
// There are subtle issues with calling the next functions directly. It is // There are subtle issues with calling the next functions directly. It is
// much better to always use a ThreadScope to manage attaching/detaching for // much better to always use a ThreadScope to manage attaching/detaching for
// you. // you.
FBEXPORT static JNIEnv* ensureCurrentThreadIsAttached(); static JNIEnv* ensureCurrentThreadIsAttached();
FBEXPORT static void detachCurrentThread();
}; };
namespace detail {
// This will return null the thread isn't attached to the VM, or if
// fbjni has never been initialized with a VM at all. You probably
// shouldn't be using this.
JNIEnv* currentOrNull();
/**
* If there's thread-local data, it's a pointer to one of these. The
* instance is a member of JniEnvCacher or ThreadScope, and lives on
* the stack.
*/
struct TLData {
// This is modified only by JniEnvCacher, and is guaranteed to be
// valid if set, and refer to an env which originated from a JNI
// call into C++.
JNIEnv* env;
// This is modified only by ThreadScope, and is set only if an
// instance of ThreadScope which attached is on the stack.
bool attached;
};
/**
* RAII object which manages a cached JNIEnv* value. A Value is only
* cached if it is guaranteed safe, which means when C++ is called
* from a registered fbjni function.
*/
class JniEnvCacher {
public:
JniEnvCacher(JNIEnv* env);
JniEnvCacher(JniEnvCacher&) = delete;
JniEnvCacher(JniEnvCacher&&) = default;
JniEnvCacher& operator=(JniEnvCacher&) = delete;
JniEnvCacher& operator=(JniEnvCacher&&) = delete;
~JniEnvCacher();
private:
// If this flag is set, then, this object needs to clear the cache.
bool thisCached_;
// The thread local pointer may point here.
detail::TLData data_;
};
}
/** /**
* RAII Object that attaches a thread to the JVM. Failing to detach from a thread before it * 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 * exits will cause a crash, as will calling Detach an extra time, and this guard class helps
@@ -49,8 +103,11 @@ struct Environment {
* class or instance to the new thread; this bypasses the need for the class loader. * 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) * (See http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/invocation.html#attach_current_thread)
* If you need access to the application's classes, you can use ThreadScope::WithClassLoader. * If you need access to the application's classes, you can use ThreadScope::WithClassLoader.
* - If fbjni has never been initialized, there will be no JavaVM object to attach with.
* In that case, a std::runtime_error will be thrown. This is only likely to happen in a
* standalone C++ application, or if Environment::initialize is not used.
*/ */
class FBEXPORT ThreadScope { class ThreadScope {
public: public:
ThreadScope(); ThreadScope();
ThreadScope(ThreadScope&) = delete; ThreadScope(ThreadScope&) = delete;
@@ -68,8 +125,14 @@ class FBEXPORT ThreadScope {
static void WithClassLoader(std::function<void()>&& runnable); static void WithClassLoader(std::function<void()>&& runnable);
static void OnLoad(); static void OnLoad();
private: private:
bool attachedWithThisScope_; // If this flag is set, then this object needs to detach.
bool thisAttached_;
// The thread local pointer may point here.
detail::TLData data_;
}; };
} }
} }

View File

@@ -0,0 +1,400 @@
/**
* Copyright 2018-present, Facebook, Inc.
*
* 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.
*/
#include "CoreClasses.h"
#include "Log.h"
#ifndef FBJNI_NO_EXCEPTION_PTR
#include <lyra/lyra.h>
#include <lyra/lyra_exceptions.h>
#endif
#include <alloca.h>
#include <cstdlib>
#include <ios>
#include <stdexcept>
#include <stdio.h>
#include <string>
#include <system_error>
#include <jni.h>
namespace facebook {
namespace jni {
namespace {
class JRuntimeException : public JavaClass<JRuntimeException, JThrowable> {
public:
static auto constexpr kJavaDescriptor = "Ljava/lang/RuntimeException;";
static local_ref<JRuntimeException> create(const char* str) {
return newInstance(make_jstring(str));
}
static local_ref<JRuntimeException> create() {
return newInstance();
}
};
class JIOException : public JavaClass<JIOException, JThrowable> {
public:
static auto constexpr kJavaDescriptor = "Ljava/io/IOException;";
static local_ref<JIOException> create(const char* str) {
return newInstance(make_jstring(str));
}
};
class JOutOfMemoryError : public JavaClass<JOutOfMemoryError, JThrowable> {
public:
static auto constexpr kJavaDescriptor = "Ljava/lang/OutOfMemoryError;";
static local_ref<JOutOfMemoryError> create(const char* str) {
return newInstance(make_jstring(str));
}
};
class JArrayIndexOutOfBoundsException : public JavaClass<JArrayIndexOutOfBoundsException, JThrowable> {
public:
static auto constexpr kJavaDescriptor = "Ljava/lang/ArrayIndexOutOfBoundsException;";
static local_ref<JArrayIndexOutOfBoundsException> create(const char* str) {
return newInstance(make_jstring(str));
}
};
class JUnknownCppException : public JavaClass<JUnknownCppException, JThrowable> {
public:
static auto constexpr kJavaDescriptor = "Lcom/facebook/jni/UnknownCppException;";
static local_ref<JUnknownCppException> create() {
return newInstance();
}
static local_ref<JUnknownCppException> create(const char* str) {
return newInstance(make_jstring(str));
}
};
class JCppSystemErrorException : public JavaClass<JCppSystemErrorException, JThrowable> {
public:
static auto constexpr kJavaDescriptor = "Lcom/facebook/jni/CppSystemErrorException;";
static local_ref<JCppSystemErrorException> create(const std::system_error& e) {
return newInstance(make_jstring(e.what()), e.code().value());
}
};
// Exception throwing & translating functions //////////////////////////////////////////////////////
// Functions that throw Java exceptions
void setJavaExceptionAndAbortOnFailure(alias_ref<JThrowable> throwable) {
auto env = Environment::current();
if (throwable) {
env->Throw(throwable.get());
}
if (env->ExceptionCheck() != JNI_TRUE) {
FBJNI_LOGF("Failed to set Java exception");
}
}
}
// Functions that throw C++ exceptions
// TODO(T6618159) Inject the c++ stack into the exception's stack trace. One
// issue: when a java exception is created, it captures the full java stack
// across jni boundaries. lyra will only capture the c++ stack to the jni
// boundary. So, as we pass the java exception up to c++, we need to capture
// the c++ stack and then insert it into the correct place in the java stack
// trace. Then, as the exception propagates across the boundaries, we will
// slowly fill in the c++ parts of the trace.
void throwPendingJniExceptionAsCppException() {
JNIEnv* env = Environment::current();
if (env->ExceptionCheck() == JNI_FALSE) {
return;
}
auto throwable = adopt_local(env->ExceptionOccurred());
if (!throwable) {
throw std::runtime_error("Unable to get pending JNI exception.");
}
env->ExceptionClear();
throw JniException(throwable);
}
void throwCppExceptionIf(bool condition) {
if (!condition) {
return;
}
auto env = Environment::current();
if (env->ExceptionCheck() == JNI_TRUE) {
throwPendingJniExceptionAsCppException();
return;
}
throw JniException();
}
void throwNewJavaException(jthrowable throwable) {
throw JniException(wrap_alias(throwable));
}
void throwNewJavaException(const char* throwableName, const char* msg) {
// If anything of the fbjni calls fail, an exception of a suitable
// form will be thrown, which is what we want.
auto throwableClass = findClassLocal(throwableName);
auto throwable = throwableClass->newObject(
throwableClass->getConstructor<jthrowable(jstring)>(),
make_jstring(msg).release());
throwNewJavaException(throwable.get());
}
// jthrowable //////////////////////////////////////////////////////////////////////////////////////
local_ref<JThrowable> JThrowable::initCause(alias_ref<JThrowable> cause) {
static auto meth = javaClassStatic()->getMethod<javaobject(alias_ref<javaobject>)>("initCause");
return meth(self(), cause);
}
auto JThrowable::getStackTrace() -> local_ref<JStackTrace> {
static auto meth = javaClassStatic()->getMethod<JStackTrace::javaobject()>("getStackTrace");
return meth(self());
}
void JThrowable::setStackTrace(alias_ref<JStackTrace> stack) {
static auto meth = javaClassStatic()->getMethod<void(alias_ref<JStackTrace>)>("setStackTrace");
return meth(self(), stack);
}
auto JStackTraceElement::create(
const std::string& declaringClass, const std::string& methodName, const std::string& file, int line)
-> local_ref<javaobject> {
return newInstance(declaringClass, methodName, file, line);
}
std::string JStackTraceElement::getClassName() const {
static auto meth = javaClassStatic()->getMethod<local_ref<JString>()>("getClassName");
return meth(self())->toStdString();
}
std::string JStackTraceElement::getMethodName() const {
static auto meth = javaClassStatic()->getMethod<local_ref<JString>()>("getMethodName");
return meth(self())->toStdString();
}
std::string JStackTraceElement::getFileName() const {
static auto meth = javaClassStatic()->getMethod<local_ref<JString>()>("getFileName");
return meth(self())->toStdString();
}
int JStackTraceElement::getLineNumber() const {
static auto meth = javaClassStatic()->getMethod<jint()>("getLineNumber");
return meth(self());
}
// Translate C++ to Java Exception
namespace {
// For each exception in the chain of the exception_ptr argument, func
// will be called with that exception (in reverse order, i.e. innermost first).
#ifndef FBJNI_NO_EXCEPTION_PTR
void denest(const std::function<void(std::exception_ptr)>& func, std::exception_ptr ptr) {
FBJNI_ASSERT(ptr);
try {
std::rethrow_exception(ptr);
} catch (const std::nested_exception& e) {
denest(func, e.nested_ptr());
} catch (...) {
// ignored.
}
func(ptr);
}
#endif
} // namespace
#ifndef FBJNI_NO_EXCEPTION_PTR
local_ref<JStackTraceElement> createJStackTraceElement(const lyra::StackTraceElement& cpp) {
return JStackTraceElement::create(
"|lyra|{" + cpp.libraryName() + "}", cpp.functionName(), cpp.buildId(), cpp.libraryOffset());
}
void addCppStacktraceToJavaException(alias_ref<JThrowable> java, std::exception_ptr cpp) {
auto cppStack = lyra::getStackTraceSymbols(
(cpp == nullptr) ?
lyra::getStackTrace()
: lyra::getExceptionTrace(cpp));
auto javaStack = java->getStackTrace();
auto newStack = JThrowable::JStackTrace::newArray(javaStack->size() + cppStack.size());
size_t i = 0;
for (size_t j = 0; j < cppStack.size(); j++, i++) {
(*newStack)[i] = createJStackTraceElement(cppStack[j]);
}
for (size_t j = 0; j < javaStack->size(); j++, i++) {
(*newStack)[i] = (*javaStack)[j];
}
java->setStackTrace(newStack);
}
local_ref<JThrowable> convertCppExceptionToJavaException(std::exception_ptr ptr) {
FBJNI_ASSERT(ptr);
local_ref<JThrowable> current;
bool addCppStack = true;
try {
std::rethrow_exception(ptr);
addCppStack = false;
} catch (const JniException& ex) {
current = ex.getThrowable();
} catch (const std::ios_base::failure& ex) {
current = JIOException::create(ex.what());
} catch (const std::bad_alloc& ex) {
current = JOutOfMemoryError::create(ex.what());
} catch (const std::out_of_range& ex) {
current = JArrayIndexOutOfBoundsException::create(ex.what());
} catch (const std::system_error& ex) {
current = JCppSystemErrorException::create(ex);
} catch (const std::runtime_error& ex) {
current = JRuntimeException::create(ex.what());
} catch (const std::exception& ex) {
current = JCppException::create(ex.what());
} catch (const char* msg) {
current = JUnknownCppException::create(msg);
} catch (...) {
current = JUnknownCppException::create();
}
if (addCppStack) {
addCppStacktraceToJavaException(current, ptr);
}
return current;
}
#endif
local_ref<JThrowable> getJavaExceptionForCppBackTrace() {
return getJavaExceptionForCppBackTrace(nullptr);
}
local_ref<JThrowable> getJavaExceptionForCppBackTrace(const char* msg) {
local_ref<JThrowable> current =
msg ? JUnknownCppException::create(msg) : JUnknownCppException::create();
#ifndef FBJNI_NO_EXCEPTION_PTR
addCppStacktraceToJavaException(current, nullptr);
#endif
return current;
}
#ifndef FBJNI_NO_EXCEPTION_PTR
local_ref<JThrowable> getJavaExceptionForCppException(std::exception_ptr ptr) {
FBJNI_ASSERT(ptr);
local_ref<JThrowable> previous;
auto func = [&previous] (std::exception_ptr ptr) {
auto current = convertCppExceptionToJavaException(ptr);
if (previous) {
current->initCause(previous);
}
previous = current;
};
denest(func, ptr);
return previous;
}
#endif
void translatePendingCppExceptionToJavaException() {
try {
#ifndef FBJNI_NO_EXCEPTION_PTR
auto exc = getJavaExceptionForCppException(std::current_exception());
#else
auto exc = JUnknownCppException::create();
#endif
setJavaExceptionAndAbortOnFailure(exc);
} catch (...) {
#ifndef FBJNI_NO_EXCEPTION_PTR
FBJNI_LOGE(
"Unexpected error in translatePendingCppExceptionToJavaException(): %s",
lyra::toString(std::current_exception()).c_str());
#else
FBJNI_LOGE(
"Unexpected error in translatePendingCppExceptionToJavaException()");
#endif
std::terminate();
}
}
// JniException ////////////////////////////////////////////////////////////////////////////////////
const std::string JniException::kExceptionMessageFailure_ = "Unable to get exception message.";
JniException::JniException() : JniException(JRuntimeException::create()) { }
JniException::JniException(alias_ref<jthrowable> throwable) : isMessageExtracted_(false) {
throwable_ = make_global(throwable);
}
JniException::JniException(JniException &&rhs)
: throwable_(std::move(rhs.throwable_)),
what_(std::move(rhs.what_)),
isMessageExtracted_(rhs.isMessageExtracted_) {
}
JniException::JniException(const JniException &rhs)
: what_(rhs.what_), isMessageExtracted_(rhs.isMessageExtracted_) {
throwable_ = make_global(rhs.throwable_);
}
JniException::~JniException() {
try {
ThreadScope ts;
throwable_.reset();
} catch (...) {
FBJNI_LOGE("Exception in ~JniException()");
std::terminate();
}
}
local_ref<JThrowable> JniException::getThrowable() const noexcept {
return make_local(throwable_);
}
// TODO 6900503: consider making this thread-safe.
void JniException::populateWhat() const noexcept {
try {
ThreadScope ts;
what_ = throwable_->toString();
isMessageExtracted_ = true;
} catch(...) {
what_ = kExceptionMessageFailure_;
}
}
const char* JniException::what() const noexcept {
if (!isMessageExtracted_) {
populateWhat();
}
return what_.c_str();
}
void JniException::setJavaException() const noexcept {
setJavaExceptionAndAbortOnFailure(throwable_);
}
}}

View File

@@ -1,8 +1,17 @@
/* /**
* Copyright (c) 2015-present, Facebook, Inc. * Copyright 2018-present, Facebook, Inc.
* *
* This source code is licensed under the MIT license found in the * Licensed under the Apache License, Version 2.0 (the "License");
* LICENSE file in the root directory of this source tree. * 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.
*/ */
/** /**
@@ -24,12 +33,15 @@
#include <jni.h> #include <jni.h>
#include <fb/visibility.h>
#include "Common.h" #include "Common.h"
#include "References.h" #include "References.h"
#include "CoreClasses.h" #include "CoreClasses.h"
#if defined(__ANDROID__) && defined(__ARM_ARCH_5TE__) && !defined(FBJNI_NO_EXCEPTION_PTR)
// ARMv5 NDK does not support exception_ptr so we cannot use that when building for it.
#define FBJNI_NO_EXCEPTION_PTR
#endif
namespace facebook { namespace facebook {
namespace jni { namespace jni {
@@ -58,7 +70,7 @@ class JCppException : public JavaClass<JCppException, JThrowable> {
* *
* Note: the what() method of this class is not thread-safe (t6900503). * Note: the what() method of this class is not thread-safe (t6900503).
*/ */
class FBEXPORT JniException : public std::exception { class JniException : public std::exception {
public: public:
JniException(); JniException();
~JniException(); ~JniException();
@@ -106,11 +118,23 @@ template<typename... Args>
} }
// Identifies any pending C++ exception and throws it as a Java exception. If the exception can't // 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. // be thrown, it aborts the program.
FBEXPORT void translatePendingCppExceptionToJavaException() noexcept; void translatePendingCppExceptionToJavaException();
#ifndef FBJNI_NO_EXCEPTION_PTR
local_ref<JThrowable> getJavaExceptionForCppException(std::exception_ptr ptr);
#endif
/***
* The stack returned may include build ids. It may be beneficial to
* call lyra::setLibraryIdentifierFunction before calling this if
* build ids are desirable.
*/
local_ref<JThrowable> getJavaExceptionForCppBackTrace();
local_ref<JThrowable> getJavaExceptionForCppBackTrace(const char* msg);
// For convenience, some exception names in java.lang are available here. // For convenience, some exception names in java.lang are available here.
const char* const gJavaLangIllegalArgumentException = "java/lang/IllegalArgumentException"; const char* const gJavaLangIllegalArgumentException = "java/lang/IllegalArgumentException";
}} }}

View File

@@ -0,0 +1,42 @@
/**
* Copyright 2018-present, Facebook, Inc.
*
* 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.
*/
#include <fbjni/fbjni.h>
namespace facebook {
namespace jni {
namespace detail {
local_ref<HybridData> HybridData::create() {
return newInstance();
}
}
namespace {
void deleteNative(alias_ref<jclass>, jlong ptr) {
delete reinterpret_cast<detail::BaseHybridClass*>(ptr);
}
}
void HybridDataOnLoad() {
registerNatives("com/facebook/jni/HybridData$Destructor", {
makeNativeMethod("deleteNative", deleteNative),
});
}
}}

View File

@@ -1,8 +1,17 @@
/* /**
* Copyright (c) 2015-present, Facebook, Inc. * Copyright 2018-present, Facebook, Inc.
* *
* This source code is licensed under the MIT license found in the * Licensed under the Apache License, Version 2.0 (the "License");
* LICENSE file in the root directory of this source tree. * 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.
*/ */
#pragma once #pragma once
@@ -10,9 +19,6 @@
#include <memory> #include <memory>
#include <type_traits> #include <type_traits>
#include <fb/assert.h>
#include <fb/visibility.h>
#include "CoreClasses.h" #include "CoreClasses.h"
namespace facebook { namespace facebook {
@@ -25,13 +31,45 @@ public:
virtual ~BaseHybridClass() {} virtual ~BaseHybridClass() {}
}; };
struct FBEXPORT HybridData : public JavaClass<HybridData> { struct HybridData : public JavaClass<HybridData> {
constexpr static auto kJavaDescriptor = "Lcom/facebook/jni/HybridData;"; constexpr static auto kJavaDescriptor = "Lcom/facebook/jni/HybridData;";
void setNativePointer(std::unique_ptr<BaseHybridClass> new_value);
BaseHybridClass* getNativePointer();
static local_ref<HybridData> create(); static local_ref<HybridData> create();
}; };
class HybridDestructor : public JavaClass<HybridDestructor> {
public:
static auto constexpr kJavaDescriptor = "Lcom/facebook/jni/HybridData$Destructor;";
detail::BaseHybridClass* getNativePointer();
void setNativePointer(std::unique_ptr<detail::BaseHybridClass> new_value);
};
template<typename T>
detail::BaseHybridClass* getNativePointer(T t) {
return getHolder(t)->getNativePointer();
}
template<typename T>
void setNativePointer(T t, std::unique_ptr<detail::BaseHybridClass> new_value) {
getHolder(t)->setNativePointer(std::move(new_value));
}
template<typename T>
local_ref<HybridDestructor> getHolder(T t) {
static auto holderField = t->getClass()->template getField<HybridDestructor::javaobject>("mDestructor");
return t->getFieldValue(holderField);
}
// JavaClass for HybridClassBase
struct HybridClassBase : public JavaClass<HybridClassBase> {
constexpr static auto kJavaDescriptor = "Lcom/facebook/jni/HybridClassBase;";
static bool isHybridClassBase(alias_ref<jclass> jclass) {
return HybridClassBase::javaClassStatic()->isAssignableFrom(jclass);
}
};
template <typename Base, typename Enabled = void> template <typename Base, typename Enabled = void>
struct HybridTraits { struct HybridTraits {
// This static assert should actually always fail if we don't use one of the // This static assert should actually always fail if we don't use one of the
@@ -66,7 +104,7 @@ struct HybridTraits<
// convert to HybridClass* from jhybridobject // convert to HybridClass* from jhybridobject
template <typename T> template <typename T>
struct FBEXPORT Convert< struct Convert<
T, typename std::enable_if< T, typename std::enable_if<
std::is_base_of<BaseHybridClass, typename std::remove_pointer<T>::type>::value>::type> { std::is_base_of<BaseHybridClass, typename std::remove_pointer<T>::type>::value>::type> {
typedef typename std::remove_pointer<T>::type::jhybridobject jniType; typedef typename std::remove_pointer<T>::type::jhybridobject jniType;
@@ -91,7 +129,7 @@ struct RefReprType<T, typename std::enable_if<std::is_base_of<BaseHybridClass, T
} }
template <typename T, typename Base = detail::BaseHybridClass> template <typename T, typename Base = detail::BaseHybridClass>
class FBEXPORT HybridClass : public detail::HybridTraits<Base>::CxxBase { class HybridClass : public detail::HybridTraits<Base>::CxxBase {
public: public:
struct JavaPart : JavaClass<JavaPart, typename detail::HybridTraits<Base>::JavaBase> { struct JavaPart : JavaClass<JavaPart, typename detail::HybridTraits<Base>::JavaBase> {
// At this point, T is incomplete, and so we cannot access // At this point, T is incomplete, and so we cannot access
@@ -108,6 +146,7 @@ public:
T* cthis(); T* cthis();
friend class HybridClass; friend class HybridClass;
friend T;
}; };
using jhybridobject = typename JavaPart::javaobject; using jhybridobject = typename JavaPart::javaobject;
@@ -137,7 +176,7 @@ protected:
static local_ref<detail::HybridData> makeHybridData(std::unique_ptr<T> cxxPart) { static local_ref<detail::HybridData> makeHybridData(std::unique_ptr<T> cxxPart) {
auto hybridData = detail::HybridData::create(); auto hybridData = detail::HybridData::create();
hybridData->setNativePointer(std::move(cxxPart)); setNativePointer(hybridData, std::move(cxxPart));
return hybridData; return hybridData;
} }
@@ -146,6 +185,11 @@ protected:
return makeHybridData(std::unique_ptr<T>(new T(std::forward<Args>(args)...))); return makeHybridData(std::unique_ptr<T>(new T(std::forward<Args>(args)...)));
} }
template <typename... Args>
static void setCxxInstance(alias_ref<jhybridobject> o, Args&&... args) {
setNativePointer(o, std::unique_ptr<T>(new T(std::forward<Args>(args)...)));
}
public: public:
// Factory method for creating a hybrid object where the arguments // Factory method for creating a hybrid object where the arguments
// are used to initialize the C++ part directly without passing them // are used to initialize the C++ part directly without passing them
@@ -159,11 +203,23 @@ public:
// C++ object fails, or any JNI methods throw. // C++ object fails, or any JNI methods throw.
template <typename... Args> template <typename... Args>
static local_ref<JavaPart> newObjectCxxArgs(Args&&... args) { static local_ref<JavaPart> newObjectCxxArgs(Args&&... args) {
auto hybridData = makeCxxInstance(std::forward<Args>(args)...); static bool isHybrid = detail::HybridClassBase::isHybridClassBase(javaClassStatic());
return JavaPart::newInstance(hybridData); auto cxxPart = std::unique_ptr<T>(new T(std::forward<Args>(args)...));
local_ref<JavaPart> result;
if (isHybrid) {
result = JavaPart::newInstance();
setNativePointer(result, std::move(cxxPart));
}
else {
auto hybridData = makeHybridData(std::move(cxxPart));
result = JavaPart::newInstance(hybridData);
}
return result;
} }
// TODO? Create reusable interface for Allocatable classes and use it to // TODO? Create reusable interface for Allocatable classes and use it to
// strengthen type-checking (and possibly provide a default // strengthen type-checking (and possibly provide a default
// implementation of allocate().) // implementation of allocate().)
template <typename... Args> template <typename... Args>
@@ -193,17 +249,23 @@ public:
template <typename T, typename B> template <typename T, typename B>
inline T* HybridClass<T, B>::JavaPart::cthis() { inline T* HybridClass<T, B>::JavaPart::cthis() {
static auto field = detail::BaseHybridClass* result = 0;
HybridClass<T, B>::JavaPart::javaClassStatic()->template getField<detail::HybridData::javaobject>("mHybridData"); static bool isHybrid = detail::HybridClassBase::isHybridClassBase(this->getClass());
auto hybridData = this->getFieldValue(field); if (isHybrid) {
if (!hybridData) { result = getNativePointer(this);
throwNewJavaException("java/lang/NullPointerException", "java.lang.NullPointerException"); } else {
static auto field =
HybridClass<T, B>::JavaPart::javaClassStatic()->template getField<detail::HybridData::javaobject>("mHybridData");
auto hybridData = this->getFieldValue(field);
if (!hybridData) {
throwNewJavaException("java/lang/NullPointerException", "java.lang.NullPointerException");
}
result = getNativePointer(hybridData);
} }
// I'd like to use dynamic_cast here, but -fno-rtti is the default. // I'd like to use dynamic_cast here, but -fno-rtti is the default.
T* value = static_cast<T*>(hybridData->getNativePointer()); return static_cast<T*>(result);
// This would require some serious programmer error.
FBASSERTMSGF(value != 0, "Incorrect C++ type in hybrid field");
return value;
}; };
template <typename T, typename B> template <typename T, typename B>

View File

@@ -1,8 +1,17 @@
/* /**
* Copyright (c) 2015-present, Facebook, Inc. * Copyright 2018-present, Facebook, Inc.
* *
* This source code is licensed under the MIT license found in the * Licensed under the Apache License, Version 2.0 (the "License");
* LICENSE file in the root directory of this source tree. * 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.
*/ */
#pragma once #pragma once
@@ -33,7 +42,7 @@ struct IteratorHelper : public JavaClass<IteratorHelper<E>> {
value_type next() { value_type next() {
static auto elementField = static auto elementField =
JavaBase_::javaClassStatic()->template getField<jobject>("mElement"); JavaBase_::javaClassStatic()->template getField<jobject>("mElement");
return dynamic_ref_cast<E>(JavaBase_::getFieldValue(elementField)); return dynamic_ref_cast<JniType<E>>(JavaBase_::getFieldValue(elementField));
} }
static void reset(value_type& v) { static void reset(value_type& v) {

View File

@@ -1,8 +1,17 @@
/* /**
* Copyright (c) 2015-present, Facebook, Inc. * Copyright 2018-present, Facebook, Inc.
* *
* This source code is licensed under the MIT license found in the * Licensed under the Apache License, Version 2.0 (the "License");
* LICENSE file in the root directory of this source tree. * 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.
*/ */
#pragma once #pragma once

View File

@@ -0,0 +1,49 @@
/**
* Copyright 2004-present, Facebook, Inc.
*
* 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.
*/
#pragma once
#include "CoreClasses.h"
namespace facebook {
namespace jni {
/**
* Wrap Java's WeakReference instead of using JNI WeakGlobalRefs.
* A WeakGlobalRef can yield a strong reference even after the object has been
* finalized. See comment in the djinni library.
* https://github.com/dropbox/djinni/blob/master/support-lib/jni/djinni_support.hpp
*/
template<typename T = jobject>
class JWeakReference : public JavaClass<JWeakReference<T>> {
typedef JavaClass<JWeakReference<T>> JavaBase_;
public:
static constexpr const char* kJavaDescriptor = "Ljava/lang/ref/WeakReference;";
static local_ref<JWeakReference<T>> newInstance(alias_ref<T> object) {
return JavaBase_::newInstance(static_ref_cast<jobject>(object));
}
local_ref<T> get() const {
static const auto method = JavaBase_::javaClassStatic()->template getMethod<jobject()>("get");
return static_ref_cast<T>(method(JavaBase_::self()));
}
};
}
}

View File

@@ -0,0 +1,77 @@
/**
* Copyright 2018-present, Facebook, Inc.
*
* 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.
*/
/** @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 jni {
namespace log_ {
// the weird name of this namespace is to avoid a conflict with the
// function named log.
inline void loge(const char* tag, const char* msg) noexcept {
__android_log_write(ANDROID_LOG_ERROR, tag, msg);
}
template<typename... ARGS>
inline void loge(const char* tag, const char* msg, ARGS... args) noexcept {
__android_log_print(ANDROID_LOG_ERROR, tag, msg, args...);
}
inline void logf(const char* tag, const char* msg) noexcept {
__android_log_write(ANDROID_LOG_FATAL, tag, msg);
}
template<typename... ARGS>
inline void logf(const char* tag, const char* msg, ARGS... args) noexcept {
__android_log_print(ANDROID_LOG_FATAL, tag, msg, args...);
}
template<typename... ARGS>
[[noreturn]]
inline void logassert(const char* tag, const char* msg, ARGS... args) noexcept {
__android_log_assert(0, tag, msg, args...);
}
#ifdef LOG_TAG
# define FBJNI_LOGE(...) ::facebook::jni::log_::loge(LOG_TAG, __VA_ARGS__)
# define FBJNI_LOGF(...) ::facebook::jni::log_::logf(LOG_TAG, __VA_ARGS__)
# define FBJNI_ASSERT(cond) do { if (!(cond)) ::facebook::jni::log_::logassert(LOG_TAG, "%s", #cond); } while(0)
#else
# define FBJNI_LOGE(...) ::facebook::jni::log_::loge("log", __VA_ARGS__)
# define FBJNI_LOGF(...) ::facebook::jni::log_::logf("log", __VA_ARGS__)
# define FBJNI_ASSERT(cond) do { if (!(cond)) ::facebook::jni::log_::logassert("log", "%s", #cond); } while(0)
#endif
}}}
#else
#include <stdlib.h>
# define FBJNI_LOGE(...) ((void)0)
# define FBJNI_LOGF(...) (abort())
# define FBJNI_ASSERT(cond) ((void)0)
#endif

View File

@@ -0,0 +1,43 @@
/**
* Copyright 2018-present, Facebook, Inc.
*
* 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.
*/
#pragma once
namespace facebook {
namespace jni {
template<typename F>
class JMethod;
template<typename F>
class JStaticMethod;
template<typename F>
class JNonvirtualMethod;
template<typename F>
struct JConstructor;
template<typename F>
class JField;
template<typename F>
class JStaticField;
/// Type traits for Java types (currently providing Java type descriptors)
template<typename T>
struct jtype_traits;
/// Type traits for Java methods (currently providing Java type descriptors)
template<typename F>
struct jmethod_traits;
}}

View File

@@ -1,8 +1,17 @@
/* /**
* Copyright (c) 2015-present, Facebook, Inc. * Copyright 2018-present, Facebook, Inc.
* *
* This source code is licensed under the MIT license found in the * Licensed under the Apache License, Version 2.0 (the "License");
* LICENSE file in the root directory of this source tree. * 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.
*/ */
#pragma once #pragma once
@@ -15,10 +24,6 @@
#include "References.h" #include "References.h"
#include "Boxed.h" #include "Boxed.h"
#if defined(__ANDROID__)
#include <sys/system_properties.h>
#endif
namespace facebook { namespace facebook {
namespace jni { namespace jni {
@@ -63,31 +68,10 @@ local_ref<JArrayClass<jobject>::javaobject> makeArgsArray(Args... args) {
return arr; return arr;
} }
inline bool needsSlowPath(alias_ref<jobject> obj) {
#if defined(__ANDROID__)
// On Android 6.0, art crashes when attempting to call a function on a Proxy.
// So, when we detect that case we must use the safe, slow workaround. That is,
// we resolve the method id to the corresponding java.lang.reflect.Method object
// and make the call via it's invoke() method.
static auto android_sdk = ([] {
char sdk_version_str[PROP_VALUE_MAX];
__system_property_get("ro.build.version.sdk", sdk_version_str);
return atoi(sdk_version_str);
})();
static auto is_bad_android = android_sdk == 23;
if (!is_bad_android) return false;
static auto proxy_class = findClassStatic("java/lang/reflect/Proxy");
return obj->isInstanceOf(proxy_class);
#else
return false;
#endif
}
} }
template<typename... Args> template<typename... Args>
inline void JMethod<void(Args...)>::operator()(alias_ref<jobject> self, Args... args) { inline void JMethod<void(Args...)>::operator()(alias_ref<jobject> self, Args... args) const {
const auto env = Environment::current(); const auto env = Environment::current();
env->CallVoidMethod( env->CallVoidMethod(
self.get(), self.get(),
@@ -98,10 +82,10 @@ inline void JMethod<void(Args...)>::operator()(alias_ref<jobject> self, Args...
#pragma push_macro("DEFINE_PRIMITIVE_CALL") #pragma push_macro("DEFINE_PRIMITIVE_CALL")
#undef DEFINE_PRIMITIVE_CALL #undef DEFINE_PRIMITIVE_CALL
#define DEFINE_PRIMITIVE_CALL(TYPE, METHOD, CLASS) \ #define DEFINE_PRIMITIVE_CALL(TYPE, METHOD) \
template<typename... Args> \ template<typename... Args> \
inline TYPE JMethod<TYPE(Args...)>::operator()(alias_ref<jobject> self, Args... args) { \ inline TYPE JMethod<TYPE(Args...)>::operator()(alias_ref<jobject> self, Args... args) const { \
const auto env = internal::getEnv(); \ const auto env = Environment::current(); \
auto result = env->Call ## METHOD ## Method( \ auto result = env->Call ## METHOD ## Method( \
self.get(), \ self.get(), \
getId(), \ getId(), \
@@ -110,14 +94,14 @@ inline TYPE JMethod<TYPE(Args...)>::operator()(alias_ref<jobject> self, Args...
return result; \ return result; \
} }
DEFINE_PRIMITIVE_CALL(jboolean, Boolean, JBoolean) DEFINE_PRIMITIVE_CALL(jboolean, Boolean)
DEFINE_PRIMITIVE_CALL(jbyte, Byte, JByte) DEFINE_PRIMITIVE_CALL(jbyte, Byte)
DEFINE_PRIMITIVE_CALL(jchar, Char, JCharacter) DEFINE_PRIMITIVE_CALL(jchar, Char)
DEFINE_PRIMITIVE_CALL(jshort, Short, JShort) DEFINE_PRIMITIVE_CALL(jshort, Short)
DEFINE_PRIMITIVE_CALL(jint, Int, JInteger) DEFINE_PRIMITIVE_CALL(jint, Int)
DEFINE_PRIMITIVE_CALL(jlong, Long, JLong) DEFINE_PRIMITIVE_CALL(jlong, Long)
DEFINE_PRIMITIVE_CALL(jfloat, Float, JFloat) DEFINE_PRIMITIVE_CALL(jfloat, Float)
DEFINE_PRIMITIVE_CALL(jdouble, Double, JDouble) DEFINE_PRIMITIVE_CALL(jdouble, Double)
#pragma pop_macro("DEFINE_PRIMITIVE_CALL") #pragma pop_macro("DEFINE_PRIMITIVE_CALL")
/// JMethod specialization for references that wraps the return value in a @ref local_ref /// JMethod specialization for references that wraps the return value in a @ref local_ref
@@ -132,13 +116,13 @@ class JMethod<R(Args...)> : public JMethodBase {
JMethod(const JMethod& other) noexcept = default; JMethod(const JMethod& other) noexcept = default;
/// Invoke a method and return a local reference wrapping the result /// Invoke a method and return a local reference wrapping the result
local_ref<JniRet> operator()(alias_ref<jobject> self, Args... args); local_ref<JniRet> operator()(alias_ref<jobject> self, Args... args) const;
friend class JClass; friend class JClass;
}; };
template<typename R, typename... Args> template<typename R, typename... Args>
inline auto JMethod<R(Args...)>::operator()(alias_ref<jobject> self, Args... args) -> local_ref<JniRet> { inline auto JMethod<R(Args...)>::operator()(alias_ref<jobject> self, Args... args) const -> local_ref<JniRet> {
const auto env = Environment::current(); const auto env = Environment::current();
auto result = env->CallObjectMethod( auto result = env->CallObjectMethod(
self.get(), self.get(),
@@ -149,8 +133,8 @@ inline auto JMethod<R(Args...)>::operator()(alias_ref<jobject> self, Args... arg
} }
template<typename... Args> template<typename... Args>
inline void JStaticMethod<void(Args...)>::operator()(alias_ref<jclass> cls, Args... args) { inline void JStaticMethod<void(Args...)>::operator()(alias_ref<jclass> cls, Args... args) const {
const auto env = internal::getEnv(); const auto env = Environment::current();
env->CallStaticVoidMethod( env->CallStaticVoidMethod(
cls.get(), cls.get(),
getId(), getId(),
@@ -162,8 +146,8 @@ inline void JStaticMethod<void(Args...)>::operator()(alias_ref<jclass> cls, Args
#undef DEFINE_PRIMITIVE_STATIC_CALL #undef DEFINE_PRIMITIVE_STATIC_CALL
#define DEFINE_PRIMITIVE_STATIC_CALL(TYPE, METHOD) \ #define DEFINE_PRIMITIVE_STATIC_CALL(TYPE, METHOD) \
template<typename... Args> \ template<typename... Args> \
inline TYPE JStaticMethod<TYPE(Args...)>::operator()(alias_ref<jclass> cls, Args... args) { \ inline TYPE JStaticMethod<TYPE(Args...)>::operator()(alias_ref<jclass> cls, Args... args) const { \
const auto env = internal::getEnv(); \ const auto env = Environment::current(); \
auto result = env->CallStatic ## METHOD ## Method( \ auto result = env->CallStatic ## METHOD ## Method( \
cls.get(), \ cls.get(), \
getId(), \ getId(), \
@@ -194,8 +178,8 @@ class JStaticMethod<R(Args...)> : public JMethodBase {
JStaticMethod(const JStaticMethod& other) noexcept = default; JStaticMethod(const JStaticMethod& other) noexcept = default;
/// Invoke a method and return a local reference wrapping the result /// Invoke a method and return a local reference wrapping the result
local_ref<JniRet> operator()(alias_ref<jclass> cls, Args... args) { local_ref<JniRet> operator()(alias_ref<jclass> cls, Args... args) const {
const auto env = internal::getEnv(); const auto env = Environment::current();
auto result = env->CallStaticObjectMethod( auto result = env->CallStaticObjectMethod(
cls.get(), cls.get(),
getId(), getId(),
@@ -209,8 +193,8 @@ class JStaticMethod<R(Args...)> : public JMethodBase {
template<typename... Args> template<typename... Args>
inline void inline void
JNonvirtualMethod<void(Args...)>::operator()(alias_ref<jobject> self, alias_ref<jclass> cls, Args... args) { JNonvirtualMethod<void(Args...)>::operator()(alias_ref<jobject> self, alias_ref<jclass> cls, Args... args) const {
const auto env = internal::getEnv(); const auto env = Environment::current();
env->CallNonvirtualVoidMethod( env->CallNonvirtualVoidMethod(
self.get(), self.get(),
cls.get(), cls.get(),
@@ -224,8 +208,8 @@ JNonvirtualMethod<void(Args...)>::operator()(alias_ref<jobject> self, alias_ref<
#define DEFINE_PRIMITIVE_NON_VIRTUAL_CALL(TYPE, METHOD) \ #define DEFINE_PRIMITIVE_NON_VIRTUAL_CALL(TYPE, METHOD) \
template<typename... Args> \ template<typename... Args> \
inline TYPE \ inline TYPE \
JNonvirtualMethod<TYPE(Args...)>::operator()(alias_ref<jobject> self, alias_ref<jclass> cls, Args... args) { \ JNonvirtualMethod<TYPE(Args...)>::operator()(alias_ref<jobject> self, alias_ref<jclass> cls, Args... args) const { \
const auto env = internal::getEnv(); \ const auto env = Environment::current(); \
auto result = env->CallNonvirtual ## METHOD ## Method( \ auto result = env->CallNonvirtual ## METHOD ## Method( \
self.get(), \ self.get(), \
cls.get(), \ cls.get(), \
@@ -256,8 +240,8 @@ class JNonvirtualMethod<R(Args...)> : public JMethodBase {
JNonvirtualMethod(const JNonvirtualMethod& other) noexcept = default; JNonvirtualMethod(const JNonvirtualMethod& other) noexcept = default;
/// Invoke a method and return a local reference wrapping the result /// Invoke a method and return a local reference wrapping the result
local_ref<JniRet> operator()(alias_ref<jobject> self, alias_ref<jclass> cls, Args... args){ local_ref<JniRet> operator()(alias_ref<jobject> self, alias_ref<jclass> cls, Args... args) const {
const auto env = internal::getEnv(); const auto env = Environment::current();
auto result = env->CallNonvirtualObjectMethod( auto result = env->CallNonvirtualObjectMethod(
self.get(), self.get(),
cls.get(), cls.get(),
@@ -306,13 +290,13 @@ inline jfieldID JField<T>::getId() const noexcept {
#define DEFINE_FIELD_PRIMITIVE_GET_SET(TYPE, METHOD) \ #define DEFINE_FIELD_PRIMITIVE_GET_SET(TYPE, METHOD) \
template<> \ template<> \
inline TYPE JField<TYPE>::get(jobject object) const noexcept { \ inline TYPE JField<TYPE>::get(jobject object) const noexcept { \
const auto env = internal::getEnv(); \ const auto env = Environment::current(); \
return env->Get ## METHOD ## Field(object, field_id_); \ return env->Get ## METHOD ## Field(object, field_id_); \
} \ } \
\ \
template<> \ template<> \
inline void JField<TYPE>::set(jobject object, TYPE value) noexcept { \ inline void JField<TYPE>::set(jobject object, TYPE value) noexcept { \
const auto env = internal::getEnv(); \ const auto env = Environment::current(); \
env->Set ## METHOD ## Field(object, field_id_, value); \ env->Set ## METHOD ## Field(object, field_id_, value); \
} }
@@ -328,12 +312,12 @@ DEFINE_FIELD_PRIMITIVE_GET_SET(jdouble, Double)
template<typename T> template<typename T>
inline T JField<T>::get(jobject object) const noexcept { inline T JField<T>::get(jobject object) const noexcept {
return static_cast<T>(internal::getEnv()->GetObjectField(object, field_id_)); return static_cast<T>(Environment::current()->GetObjectField(object, field_id_));
} }
template<typename T> template<typename T>
inline void JField<T>::set(jobject object, T value) noexcept { inline void JField<T>::set(jobject object, T value) noexcept {
internal::getEnv()->SetObjectField(object, field_id_, static_cast<jobject>(value)); Environment::current()->SetObjectField(object, field_id_, static_cast<jobject>(value));
} }
// JStaticField<T> ///////////////////////////////////////////////////////////////////////////////// // JStaticField<T> /////////////////////////////////////////////////////////////////////////////////
@@ -358,13 +342,13 @@ inline jfieldID JStaticField<T>::getId() const noexcept {
#define DEFINE_STATIC_FIELD_PRIMITIVE_GET_SET(TYPE, METHOD) \ #define DEFINE_STATIC_FIELD_PRIMITIVE_GET_SET(TYPE, METHOD) \
template<> \ template<> \
inline TYPE JStaticField<TYPE>::get(jclass jcls) const noexcept { \ inline TYPE JStaticField<TYPE>::get(jclass jcls) const noexcept { \
const auto env = internal::getEnv(); \ const auto env = Environment::current(); \
return env->GetStatic ## METHOD ## Field(jcls, field_id_); \ return env->GetStatic ## METHOD ## Field(jcls, field_id_); \
} \ } \
\ \
template<> \ template<> \
inline void JStaticField<TYPE>::set(jclass jcls, TYPE value) noexcept { \ inline void JStaticField<TYPE>::set(jclass jcls, TYPE value) noexcept { \
const auto env = internal::getEnv(); \ const auto env = Environment::current(); \
env->SetStatic ## METHOD ## Field(jcls, field_id_, value); \ env->SetStatic ## METHOD ## Field(jcls, field_id_, value); \
} }
@@ -380,13 +364,13 @@ DEFINE_STATIC_FIELD_PRIMITIVE_GET_SET(jdouble, Double)
template<typename T> template<typename T>
inline T JStaticField<T>::get(jclass jcls) const noexcept { inline T JStaticField<T>::get(jclass jcls) const noexcept {
const auto env = internal::getEnv(); const auto env = Environment::current();
return static_cast<T>(env->GetStaticObjectField(jcls, field_id_)); return static_cast<T>(env->GetStaticObjectField(jcls, field_id_));
} }
template<typename T> template<typename T>
inline void JStaticField<T>::set(jclass jcls, T value) noexcept { inline void JStaticField<T>::set(jclass jcls, T value) noexcept {
internal::getEnv()->SetStaticObjectField(jcls, field_id_, value); Environment::current()->SetStaticObjectField(jcls, field_id_, value);
} }

View File

@@ -1,8 +1,17 @@
/* /**
* Copyright (c) 2015-present, Facebook, Inc. * Copyright 2018-present, Facebook, Inc.
* *
* This source code is licensed under the MIT license found in the * Licensed under the Apache License, Version 2.0 (the "License");
* LICENSE file in the root directory of this source tree. * 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.
*/ */
/** @file meta.h /** @file meta.h
@@ -81,7 +90,7 @@ class JMethod<TYPE(Args...)> : public JMethodBase {
JMethod() noexcept {}; \ JMethod() noexcept {}; \
JMethod(const JMethod& other) noexcept = default; \ JMethod(const JMethod& other) noexcept = default; \
\ \
TYPE operator()(alias_ref<jobject> self, Args... args); \ TYPE operator()(alias_ref<jobject> self, Args... args) const; \
\ \
friend class JClass; \ friend class JClass; \
} }
@@ -131,7 +140,7 @@ class JStaticMethod<TYPE(Args...)> : public JMethodBase { \
JStaticMethod() noexcept {}; \ JStaticMethod() noexcept {}; \
JStaticMethod(const JStaticMethod& other) noexcept = default; \ JStaticMethod(const JStaticMethod& other) noexcept = default; \
\ \
TYPE operator()(alias_ref<jclass> cls, Args... args); \ TYPE operator()(alias_ref<jclass> cls, Args... args) const; \
\ \
friend class JClass; \ friend class JClass; \
} }
@@ -171,7 +180,7 @@ class JNonvirtualMethod<TYPE(Args...)> : public JMethodBase { \
JNonvirtualMethod() noexcept {}; \ JNonvirtualMethod() noexcept {}; \
JNonvirtualMethod(const JNonvirtualMethod& other) noexcept = default; \ JNonvirtualMethod(const JNonvirtualMethod& other) noexcept = default; \
\ \
TYPE operator()(alias_ref<jobject> self, alias_ref<jclass> cls, Args... args); \ TYPE operator()(alias_ref<jobject> self, alias_ref<jclass> cls, Args... args) const; \
\ \
friend class JClass; \ friend class JClass; \
} }

View File

@@ -1,4 +1,18 @@
// Copyright 2004-present Facebook. All Rights Reserved. /**
* Copyright 2004-present, Facebook, Inc.
*
* 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.
*/
#pragma once #pragma once
@@ -95,7 +109,21 @@ template <typename T>
struct Convert<global_ref<T>> { struct Convert<global_ref<T>> {
typedef JniType<T> jniType; typedef JniType<T> jniType;
// No automatic synthesis of global_ref // No automatic synthesis of global_ref
static jniType toJniRet(global_ref<jniType> t) { static jniType toJniRet(global_ref<jniType>&& t) {
// If this gets called, ownership the global_ref was passed in here. (It's
// probably a copy of a persistent global_ref made when a function was
// declared to return a global_ref, but it could moved out or otherwise not
// referenced elsewhere. Doesn't matter.) Either way, the only safe way
// to return it is to make a local_ref, release it, and return the
// underlying local jobject.
auto ret = make_local(t);
return ret.release();
}
static jniType toJniRet(const global_ref<jniType>& t) {
// If this gets called, the function was declared to return const&. We
// have a ref to a global_ref whose lifetime will exceed this call, so we
// can just get the underlying jobject and return it to java without
// needing to make a local_ref.
return t.get(); return t.get();
} }
static jniType toCall(global_ref<jniType> t) { static jniType toCall(global_ref<jniType> t) {

View File

@@ -1,8 +1,17 @@
/* /**
* Copyright (c) 2015-present, Facebook, Inc. * Copyright 2018-present, Facebook, Inc.
* *
* This source code is licensed under the MIT license found in the * Licensed under the Apache License, Version 2.0 (the "License");
* LICENSE file in the root directory of this source tree. * 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.
*/ */
#pragma once #pragma once
@@ -11,6 +20,8 @@
#include <new> #include <new>
#include <atomic> #include <atomic>
#include "Environment.h"
namespace facebook { namespace facebook {
namespace jni { namespace jni {
@@ -19,7 +30,8 @@ namespace internal {
// Statistics mostly provided for test (only updated if FBJNI_DEBUG_REFS is defined) // Statistics mostly provided for test (only updated if FBJNI_DEBUG_REFS is defined)
struct ReferenceStats { struct ReferenceStats {
std::atomic_uint locals_deleted, globals_deleted, weaks_deleted; std::atomic_uint locals_created, globals_created, weaks_created,
locals_deleted, globals_deleted, weaks_deleted;
void reset() noexcept; void reset() noexcept;
}; };
@@ -33,7 +45,10 @@ extern ReferenceStats g_reference_stats;
inline jobject LocalReferenceAllocator::newReference(jobject original) const { inline jobject LocalReferenceAllocator::newReference(jobject original) const {
internal::dbglog("Local new: %p", original); internal::dbglog("Local new: %p", original);
auto ref = internal::getEnv()->NewLocalRef(original); #ifdef FBJNI_DEBUG_REFS
++internal::g_reference_stats.locals_created;
#endif
auto ref = Environment::current()->NewLocalRef(original);
FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
return ref; return ref;
} }
@@ -46,15 +61,12 @@ inline void LocalReferenceAllocator::deleteReference(jobject reference) const no
++internal::g_reference_stats.locals_deleted; ++internal::g_reference_stats.locals_deleted;
#endif #endif
assert(verifyReference(reference)); assert(verifyReference(reference));
internal::getEnv()->DeleteLocalRef(reference); Environment::current()->DeleteLocalRef(reference);
} }
} }
inline bool LocalReferenceAllocator::verifyReference(jobject reference) const noexcept { inline bool LocalReferenceAllocator::verifyReference(jobject reference) const noexcept {
if (!reference || !internal::doesGetObjectRefTypeWork()) { return isObjectRefType(reference, JNILocalRefType);
return true;
}
return internal::getEnv()->GetObjectRefType(reference) == JNILocalRefType;
} }
@@ -62,7 +74,10 @@ inline bool LocalReferenceAllocator::verifyReference(jobject reference) const no
inline jobject GlobalReferenceAllocator::newReference(jobject original) const { inline jobject GlobalReferenceAllocator::newReference(jobject original) const {
internal::dbglog("Global new: %p", original); internal::dbglog("Global new: %p", original);
auto ref = internal::getEnv()->NewGlobalRef(original); #ifdef FBJNI_DEBUG_REFS
++internal::g_reference_stats.globals_created;
#endif
auto ref = Environment::current()->NewGlobalRef(original);
FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
return ref; return ref;
} }
@@ -75,15 +90,12 @@ inline void GlobalReferenceAllocator::deleteReference(jobject reference) const n
++internal::g_reference_stats.globals_deleted; ++internal::g_reference_stats.globals_deleted;
#endif #endif
assert(verifyReference(reference)); assert(verifyReference(reference));
internal::getEnv()->DeleteGlobalRef(reference); Environment::current()->DeleteGlobalRef(reference);
} }
} }
inline bool GlobalReferenceAllocator::verifyReference(jobject reference) const noexcept { inline bool GlobalReferenceAllocator::verifyReference(jobject reference) const noexcept {
if (!reference || !internal::doesGetObjectRefTypeWork()) { return isObjectRefType(reference, JNIGlobalRefType);
return true;
}
return internal::getEnv()->GetObjectRefType(reference) == JNIGlobalRefType;
} }
@@ -91,7 +103,10 @@ inline bool GlobalReferenceAllocator::verifyReference(jobject reference) const n
inline jobject WeakGlobalReferenceAllocator::newReference(jobject original) const { inline jobject WeakGlobalReferenceAllocator::newReference(jobject original) const {
internal::dbglog("Weak global new: %p", original); internal::dbglog("Weak global new: %p", original);
auto ref = internal::getEnv()->NewWeakGlobalRef(original); #ifdef FBJNI_DEBUG_REFS
++internal::g_reference_stats.weaks_created;
#endif
auto ref = Environment::current()->NewWeakGlobalRef(original);
FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
return ref; return ref;
} }
@@ -104,15 +119,12 @@ inline void WeakGlobalReferenceAllocator::deleteReference(jobject reference) con
++internal::g_reference_stats.weaks_deleted; ++internal::g_reference_stats.weaks_deleted;
#endif #endif
assert(verifyReference(reference)); assert(verifyReference(reference));
internal::getEnv()->DeleteWeakGlobalRef(reference); Environment::current()->DeleteWeakGlobalRef(reference);
} }
} }
inline bool WeakGlobalReferenceAllocator::verifyReference(jobject reference) const noexcept { inline bool WeakGlobalReferenceAllocator::verifyReference(jobject reference) const noexcept {
if (!reference || !internal::doesGetObjectRefTypeWork()) { return isObjectRefType(reference, JNIWeakGlobalRefType);
return true;
}
return internal::getEnv()->GetObjectRefType(reference) == JNIWeakGlobalRefType;
} }
}} }}

View File

@@ -0,0 +1,64 @@
/**
* Copyright 2018-present, Facebook, Inc.
*
* 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.
*/
/**
* @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;
};
/**
* @return Helper based on GetObjectRefType. Since this isn't defined
* on all versions of Java or Android, if the type can't be
* determined, this returns true. If reference is nullptr, returns
* true.
*/
bool isObjectRefType(jobject reference, jobjectRefType refType);
}}
#include "ReferenceAllocators-inl.h"

View File

@@ -1,8 +1,17 @@
/* /**
* Copyright (c) 2015-present, Facebook, Inc. * Copyright 2018-present, Facebook, Inc.
* *
* This source code is licensed under the MIT license found in the * Licensed under the Apache License, Version 2.0 (the "License");
* LICENSE file in the root directory of this source tree. * 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.
*/ */
#pragma once #pragma once

View File

@@ -1,8 +1,17 @@
/* /**
* Copyright (c) 2015-present, Facebook, Inc. * Copyright 2018-present, Facebook, Inc.
* *
* This source code is licensed under the MIT license found in the * Licensed under the Apache License, Version 2.0 (the "License");
* LICENSE file in the root directory of this source tree. * 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.
*/ */
#pragma once #pragma once
@@ -484,22 +493,25 @@ template<typename T, typename RefType>
auto dynamic_ref_cast(const RefType& ref) -> auto dynamic_ref_cast(const RefType& ref) ->
enable_if_t<IsPlainJniReference<T>(), decltype(static_ref_cast<T>(ref))> enable_if_t<IsPlainJniReference<T>(), decltype(static_ref_cast<T>(ref))>
{ {
if (! ref) { if (!ref) {
return decltype(static_ref_cast<T>(ref))(); return decltype(static_ref_cast<T>(ref))();
} }
std::string target_class_name{jtype_traits<T>::base_name()}; static alias_ref<jclass> target_class = findClassStatic(jtype_traits<T>::base_name().c_str());
if (!target_class) {
throwNewJavaException("java/lang/ClassCastException",
"Could not find class %s.",
jtype_traits<T>::base_name().c_str());
// If not found, will throw an exception. }
alias_ref<jclass> target_class = findClassStatic(target_class_name.c_str());
local_ref<jclass> source_class = ref->getClass(); local_ref<jclass> source_class = ref->getClass();
if ( ! source_class->isAssignableFrom(target_class)) { if (!target_class->isAssignableFrom(source_class)) {
throwNewJavaException("java/lang/ClassCastException", throwNewJavaException("java/lang/ClassCastException",
"Tried to cast from %s to %s.", "Tried to cast from %s to %s.",
source_class->toString().c_str(), source_class->toString().c_str(),
target_class_name.c_str()); jtype_traits<T>::base_name().c_str());
} }
return static_ref_cast<T>(ref); return static_ref_cast<T>(ref);

View File

@@ -0,0 +1,75 @@
/**
* Copyright 2018-present, Facebook, Inc.
*
* 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.
*/
#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 {
#ifdef __ANDROID__
int32_t getAndroidApiLevel() {
auto cls = findClassLocal("android/os/Build$VERSION");
auto fld = cls->getStaticField<int32_t>("SDK_INT");
if (fld) {
return cls->getStaticFieldValue(fld);
}
return 0;
}
bool doesGetObjectRefTypeWork() {
auto level = getAndroidApiLevel();
return level >= 14;
}
#else
bool doesGetObjectRefTypeWork() {
auto jni_version = Environment::current()->GetVersion();
return jni_version >= JNI_VERSION_1_6;
}
#endif
}
bool isObjectRefType(jobject reference, jobjectRefType refType) {
static bool getObjectRefTypeWorks = doesGetObjectRefTypeWork();
return
!reference ||
!getObjectRefTypeWorks ||
Environment::current()->GetObjectRefType(reference) == refType;
}
}
}

View File

@@ -1,8 +1,17 @@
/* /**
* Copyright (c) 2015-present, Facebook, Inc. * Copyright 2018-present, Facebook, Inc.
* *
* This source code is licensed under the MIT license found in the * Licensed under the Apache License, Version 2.0 (the "License");
* LICENSE file in the root directory of this source tree. * 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.
*/ */
@@ -75,8 +84,6 @@
#include <jni.h> #include <jni.h>
#include <fb/visibility.h>
#include "ReferenceAllocators.h" #include "ReferenceAllocators.h"
#include "TypeTraits.h" #include "TypeTraits.h"
#include "References-forward.h" #include "References-forward.h"
@@ -338,7 +345,7 @@ class weak_ref : public base_owned_ref<T, WeakGlobalReferenceAllocator> {
: base_owned_ref<T, Allocator>{} {} : base_owned_ref<T, Allocator>{} {}
/// Create a null reference /// Create a null reference
explicit weak_ref(std::nullptr_t) noexcept /* implicit */ weak_ref(std::nullptr_t) noexcept
: base_owned_ref<T, Allocator>{nullptr} {} : base_owned_ref<T, Allocator>{nullptr} {}
/// Copy constructor (note creates a new reference) /// Copy constructor (note creates a new reference)
@@ -407,7 +414,7 @@ class basic_strong_ref : public base_owned_ref<T, Alloc> {
: base_owned_ref<T, Alloc>{} {} : base_owned_ref<T, Alloc>{} {}
/// Create a null reference /// Create a null reference
explicit basic_strong_ref(std::nullptr_t) noexcept /* implicit */ basic_strong_ref(std::nullptr_t) noexcept
: base_owned_ref<T, Alloc>{nullptr} {} : base_owned_ref<T, Alloc>{nullptr} {}
/// Copy constructor (note creates a new reference) /// Copy constructor (note creates a new reference)
@@ -494,7 +501,7 @@ class alias_ref {
alias_ref() noexcept; alias_ref() noexcept;
/// Create a null reference /// Create a null reference
alias_ref(std::nullptr_t) noexcept; /* implicit */ alias_ref(std::nullptr_t) noexcept;
/// Copy constructor /// Copy constructor
alias_ref(const alias_ref& other) noexcept; alias_ref(const alias_ref& other) noexcept;
@@ -555,7 +562,7 @@ class alias_ref {
* This is useful when you have a call which is initiated from C++-land, and therefore * 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. * doesn't automatically get a local JNI frame managed for you by the JNI framework.
*/ */
class FBEXPORT JniLocalScope { class JniLocalScope {
public: public:
JniLocalScope(JNIEnv* p_env, jint capacity); JniLocalScope(JNIEnv* p_env, jint capacity);
~JniLocalScope(); ~JniLocalScope();

View File

@@ -0,0 +1,156 @@
/**
* Copyright 2018-present, Facebook, Inc.
*
* 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.
*/
#pragma once
#include "Exceptions.h"
#include "Hybrid.h"
namespace facebook {
namespace jni {
namespace detail {
#ifdef __i386__
// X86 ABI forces 16 byte stack allignment on calls. Unfortunately
// sometimes Dalvik chooses not to obey the ABI:
// - https://code.google.com/p/android/issues/detail?id=61012
// - https://android.googlesource.com/platform/ndk/+/81696d2%5E!/
// Therefore, we tell the compiler to re-align the stack on entry
// to our JNI functions.
#define JNI_ENTRY_POINT __attribute__((force_align_arg_pointer))
#else
#define JNI_ENTRY_POINT
#endif
template <typename R>
struct CreateDefault {
static R create() {
return R{};
}
};
template <>
struct CreateDefault<void> {
static void create() {}
};
template <typename R>
using Converter = Convert<typename std::decay<R>::type>;
template <typename F, F func, typename R, typename... Args>
struct WrapForVoidReturn {
static typename Converter<R>::jniType call(Args&&... args) {
return Converter<R>::toJniRet(func(std::forward<Args>(args)...));
}
};
template <typename F, F func, typename... Args>
struct WrapForVoidReturn<F, func, void, Args...> {
static void call(Args&&... args) {
func(std::forward<Args>(args)...);
}
};
// registration wrapper for legacy JNI-style functions
template<typename F, F func, typename C, typename R, typename... Args>
struct BareJniWrapper {
JNI_ENTRY_POINT static R call(JNIEnv* env, jobject obj, Args... args) {
detail::JniEnvCacher jec(env);
try {
return (*func)(env, static_cast<JniType<C>>(obj), args...);
} catch (...) {
translatePendingCppExceptionToJavaException();
return CreateDefault<R>::create();
}
}
};
// registration wrappers for functions, with autoconversion of arguments.
template<typename F, F func, typename C, typename R, typename... Args>
struct FunctionWrapper {
using jniRet = typename Converter<R>::jniType;
JNI_ENTRY_POINT static jniRet call(JNIEnv* env, jobject obj, typename Converter<Args>::jniType... args) {
detail::JniEnvCacher jec(env);
try {
return WrapForVoidReturn<F, func, R, JniType<C>, Args...>::call(
static_cast<JniType<C>>(obj), Converter<Args>::fromJni(args)...);
} catch (...) {
translatePendingCppExceptionToJavaException();
return CreateDefault<jniRet>::create();
}
}
};
// registration wrappers for non-static methods, with autoconvertion of arguments.
template<typename M, M method, typename C, typename R, typename... Args>
struct MethodWrapper {
using jhybrid = typename C::jhybridobject;
static R dispatch(alias_ref<jhybrid> ref, Args&&... args) {
try {
// This is usually a noop, but if the hybrid object is a
// base class of other classes which register JNI methods,
// this will get the right type for the registered method.
auto cobj = static_cast<C*>(ref->cthis());
return (cobj->*method)(std::forward<Args>(args)...);
} catch (const std::exception& ex) {
C::mapException(ex);
throw;
}
}
JNI_ENTRY_POINT static typename Converter<R>::jniType call(
JNIEnv* env, jobject obj, typename Converter<Args>::jniType... args) {
return FunctionWrapper<R(*)(alias_ref<jhybrid>, Args&&...), dispatch, jhybrid, R, Args...>::call(env, obj, args...);
}
};
template<typename F, F func, typename C, typename R, typename... Args>
inline NativeMethodWrapper* exceptionWrapJNIMethod(R (*)(JNIEnv*, C, Args... args)) {
// This intentionally erases the real type; JNI will do it anyway
return reinterpret_cast<NativeMethodWrapper*>(&(BareJniWrapper<F, func, C, R, Args...>::call));
}
template<typename F, F func, typename C, typename R, typename... Args>
inline NativeMethodWrapper* exceptionWrapJNIMethod(R (*)(alias_ref<C>, Args... args)) {
// This intentionally erases the real type; JNI will do it anyway
return reinterpret_cast<NativeMethodWrapper*>(&(FunctionWrapper<F, func, C, R, Args...>::call));
}
template<typename M, M method, typename C, typename R, typename... Args>
inline NativeMethodWrapper* exceptionWrapJNIMethod(R (C::*method0)(Args... args)) {
// This intentionally erases the real type; JNI will do it anyway
return reinterpret_cast<NativeMethodWrapper*>(&(MethodWrapper<M, method, C, R, Args...>::call));
}
template<typename R, typename C, typename... Args>
inline std::string makeDescriptor(R (*)(JNIEnv*, C, Args... args)) {
return jmethod_traits<R(Args...)>::descriptor();
}
template<typename R, typename C, typename... Args>
inline std::string makeDescriptor(R (*)(alias_ref<C>, Args... args)) {
return jmethod_traits_from_cxx<R(Args...)>::descriptor();
}
template<typename R, typename C, typename... Args>
inline std::string makeDescriptor(R (C::*)(Args... args)) {
return jmethod_traits_from_cxx<R(Args...)>::descriptor();
}
}
}}

View File

@@ -1,8 +1,17 @@
/* /**
* Copyright (c) 2015-present, Facebook, Inc. * Copyright 2018-present, Facebook, Inc.
* *
* This source code is licensed under the MIT license found in the * Licensed under the Apache License, Version 2.0 (the "License");
* LICENSE file in the root directory of this source tree. * 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.
*/ */
#pragma once #pragma once
@@ -18,28 +27,14 @@ namespace detail {
// This uses the real JNI function as a non-type template parameter to // This uses the real JNI function as a non-type template parameter to
// cause a (static member) function to exist with the same signature, // cause a (static member) function to exist with the same signature,
// but with try/catch exception translation. // 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> template<typename F, F func, typename C, typename R, typename... Args>
NativeMethodWrapper* exceptionWrapJNIMethod(R (*func0)(JNIEnv*, jobject, Args... args)); NativeMethodWrapper* exceptionWrapJNIMethod(R (*func0)(JNIEnv*, jobject, Args... args));
// Automatically wrap object argument, and don't take env explicitly. // 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> template<typename F, F func, typename C, typename R, typename... Args>
NativeMethodWrapper* exceptionWrapJNIMethod(R (*func0)(alias_ref<C>, Args... 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, // 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> template<typename M, M method, typename C, typename R, typename... Args>
NativeMethodWrapper* exceptionWrapJNIMethod(R (C::*method0)(Args... args)); NativeMethodWrapper* exceptionWrapJNIMethod(R (C::*method0)(Args... args));

View File

@@ -1,8 +1,17 @@
/* /**
* Copyright (c) 2015-present, Facebook, Inc. * Copyright 2018-present, Facebook, Inc.
* *
* This source code is licensed under the MIT license found in the * Licensed under the Apache License, Version 2.0 (the "License");
* LICENSE file in the root directory of this source tree. * 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.
*/ */
#pragma once #pragma once

View File

@@ -1,15 +1,22 @@
/* /**
* Copyright (c) 2015-present, Facebook, Inc. * Copyright 2018-present, Facebook, Inc.
* *
* This source code is licensed under the MIT license found in the * Licensed under the Apache License, Version 2.0 (the "License");
* LICENSE file in the root directory of this source tree. * 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.
*/ */
#include <jni/LocalString.h> #include "utf8.h"
#include <fb/Environment.h>
#include <fb/assert.h>
#include <vector> #include "Log.h"
namespace facebook { namespace facebook {
namespace jni { namespace jni {
@@ -23,7 +30,9 @@ const uint16_t kUtf16HighSubHighBoundary = 0xDC00;
const uint16_t kUtf16LowSubHighBoundary = 0xE000; const uint16_t kUtf16LowSubHighBoundary = 0xE000;
inline void encode3ByteUTF8(char32_t code, uint8_t* out) { 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"); if ((code & 0xffff0000) != 0) {
FBJNI_LOGF("3 byte utf-8 encodings only valid for up to 16 bits");
}
out[0] = 0xE0 | (code >> 12); out[0] = 0xE0 | (code >> 12);
out[1] = 0x80 | ((code >> 6) & 0x3F); out[1] = 0x80 | ((code >> 6) & 0x3F);
@@ -37,7 +46,9 @@ inline char32_t decode3ByteUTF8(const uint8_t* in) {
} }
inline void encode4ByteUTF8(char32_t code, std::string& out, size_t offset) { 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"); if ((code & 0xfff80000) != 0) {
FBJNI_LOGF("4 byte utf-8 encodings only valid for up to 21 bits");
}
out[offset] = (char) (0xF0 | (code >> 18)); out[offset] = (char) (0xF0 | (code >> 18));
out[offset + 1] = (char) (0x80 | ((code >> 12) & 0x3F)); out[offset + 1] = (char) (0x80 | ((code >> 12) & 0x3F));
@@ -101,9 +112,13 @@ void utf8ToModifiedUTF8(const uint8_t* utf8, size_t len, uint8_t* modified, size
{ {
size_t j = 0; size_t j = 0;
for (size_t i = 0; i < len; ) { for (size_t i = 0; i < len; ) {
FBASSERTMSGF(j < modifiedBufLen, "output buffer is too short"); if (j >= modifiedBufLen) {
FBJNI_LOGF("output buffer is too short");
}
if (utf8[i] == 0) { if (utf8[i] == 0) {
FBASSERTMSGF(j + 1 < modifiedBufLen, "output buffer is too short"); if (j + 1 >= modifiedBufLen) {
FBJNI_LOGF("output buffer is too short");
}
modified[j] = 0xc0; modified[j] = 0xc0;
modified[j + 1] = 0x80; modified[j + 1] = 0x80;
i += 1; i += 1;
@@ -143,14 +158,18 @@ void utf8ToModifiedUTF8(const uint8_t* utf8, size_t len, uint8_t* modified, size
} }
// encode each as a 3 byte surrogate value // encode each as a 3 byte surrogate value
FBASSERTMSGF(j + 5 < modifiedBufLen, "output buffer is too short"); if (j + 5 >= modifiedBufLen) {
FBJNI_LOGF("output buffer is too short");
}
encode3ByteUTF8(first, modified + j); encode3ByteUTF8(first, modified + j);
encode3ByteUTF8(second, modified + j + 3); encode3ByteUTF8(second, modified + j + 3);
i += 4; i += 4;
j += 6; j += 6;
} }
FBASSERTMSGF(j < modifiedBufLen, "output buffer is too short"); if (j >= modifiedBufLen) {
FBJNI_LOGF("output buffer is too short");
}
modified[j++] = '\0'; modified[j++] = '\0';
} }
@@ -265,46 +284,5 @@ std::string utf16toUTF8(const uint16_t* utf16String, size_t utf16StringLen) noex
} }
} }
LocalString::LocalString(const std::string& str)
{
size_t modlen = detail::modifiedLength(str);
if (modlen == str.size()) {
// no supplementary characters, build jstring from input buffer
m_string = Environment::current()->NewStringUTF(str.data());
return;
}
auto modified = std::vector<char>(modlen + 1); // allocate extra byte for \0
detail::utf8ToModifiedUTF8(
reinterpret_cast<const uint8_t*>(str.data()), str.size(),
reinterpret_cast<uint8_t*>(modified.data()), modified.size());
m_string = Environment::current()->NewStringUTF(modified.data());
} }
LocalString::LocalString(const char* str)
{
size_t len;
size_t modlen = detail::modifiedLength(reinterpret_cast<const uint8_t*>(str), &len);
if (modlen == len) {
// no supplementary characters, build jstring from input buffer
m_string = Environment::current()->NewStringUTF(str);
return;
}
auto modified = std::vector<char>(modlen + 1); // allocate extra byte for \0
detail::utf8ToModifiedUTF8(
reinterpret_cast<const uint8_t*>(str), len,
reinterpret_cast<uint8_t*>(modified.data()), modified.size());
m_string = Environment::current()->NewStringUTF(modified.data());
} }
LocalString::~LocalString() {
Environment::current()->DeleteLocalRef(m_string);
}
std::string fromJString(JNIEnv* env, jstring str) {
auto utf16String = JStringUtf16Extractor(env, str);
auto length = env->GetStringLength(str);
return detail::utf16toUTF8(utf16String, length);
}
} }

View File

@@ -1,8 +1,17 @@
/* /**
* Copyright (c) 2015-present, Facebook, Inc. * Copyright 2018-present, Facebook, Inc.
* *
* This source code is licensed under the MIT license found in the * Licensed under the Apache License, Version 2.0 (the "License");
* LICENSE file in the root directory of this source tree. * 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.
*/ */
#pragma once #pragma once
@@ -11,8 +20,6 @@
#include <jni.h> #include <jni.h>
#include <fb/visibility.h>
namespace facebook { namespace facebook {
namespace jni { namespace jni {
@@ -43,28 +50,18 @@ std::string utf16toUTF8(const uint16_t* utf16Bytes, size_t len) noexcept;
// - http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/functions.html // - 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 // - https://docs.oracle.com/javase/6/docs/api/java/io/DataInput.html#modified-utf-8
class FBEXPORT LocalString { // JString to UTF16 extractor using RAII idiom. Note that the
public: // ctor/dtor use GetStringCritical/ReleaseStringCritical, so this
// Assumes UTF8 encoding and make a required convertion to modified UTF-8 when the string // class is subject to the restrictions imposed by those functions.
// contains unicode supplementary characters.
explicit LocalString(const std::string& str);
explicit LocalString(const char* str);
jstring string() const {
return m_string;
}
~LocalString();
private:
jstring m_string;
};
// JString to UTF16 extractor using RAII idiom
class JStringUtf16Extractor { class JStringUtf16Extractor {
public: public:
JStringUtf16Extractor(JNIEnv* env, jstring javaString) JStringUtf16Extractor(JNIEnv* env, jstring javaString)
: env_(env) : env_(env)
, javaString_(javaString) , javaString_(javaString)
, length_(0)
, utf16String_(nullptr) { , utf16String_(nullptr) {
if (env_ && javaString_) { if (env_ && javaString_) {
length_ = env_->GetStringLength(javaString_);
utf16String_ = env_->GetStringCritical(javaString_, nullptr); utf16String_ = env_->GetStringCritical(javaString_, nullptr);
} }
} }
@@ -75,18 +72,20 @@ public:
} }
} }
operator const jchar* () const { const jsize length() const {
return length_;
}
const jchar* chars() const {
return utf16String_; return utf16String_;
} }
private: private:
JNIEnv* env_; JNIEnv* env_;
jstring javaString_; jstring javaString_;
jsize length_;
const jchar* utf16String_; const jchar* utf16String_;
}; };
// The string from JNI is converted to standard UTF-8 if the string contains supplementary }
// characters. }
FBEXPORT std::string fromJString(JNIEnv* env, jstring str);
} }

View File

@@ -1,40 +1,48 @@
/* /**
* Copyright (c) 2015-present, Facebook, Inc. * Copyright 2018-present, Facebook, Inc.
* *
* This source code is licensed under the MIT license found in the * Licensed under the Apache License, Version 2.0 (the "License");
* LICENSE file in the root directory of this source tree. * 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.
*/ */
#include <fb/fbjni.h> #include <fbjni/fbjni.h>
#include <mutex> #include <mutex>
#include <vector> #include <vector>
#include <jni/LocalString.h>
#include <fb/log.h> #include <fbjni/detail/utf8.h>
namespace facebook { namespace facebook {
namespace jni { namespace jni {
jint initialize(JavaVM* vm, std::function<void()>&& init_fn) noexcept { jint initialize(JavaVM* vm, std::function<void()>&& init_fn) noexcept {
static std::once_flag flag{};
// TODO (t7832883): DTRT when we have exception pointers // TODO (t7832883): DTRT when we have exception pointers
static auto error_msg = std::string{"Failed to initialize fbjni"}; static auto error_msg = std::string{"Failed to initialize fbjni"};
static auto error_occured = false; static bool error_occured = [vm] {
bool retVal = false;
std::call_once(flag, [vm] {
try {
Environment::initialize(vm);
} catch (std::exception& ex) {
error_occured = true;
try { try {
error_msg = std::string{"Failed to initialize fbjni: "} + ex.what(); Environment::initialize(vm);
} catch (std::exception& ex) {
retVal = true;
try {
error_msg = std::string{"Failed to initialize fbjni: "} + ex.what();
} catch (...) {
// Ignore, we already have a fall back message
}
} catch (...) { } catch (...) {
// Ignore, we already have a fall back message retVal = true;
} }
} catch (...) { return retVal;
error_occured = true; }();
}
});
try { try {
if (error_occured) { if (error_occured) {
@@ -43,7 +51,7 @@ jint initialize(JavaVM* vm, std::function<void()>&& init_fn) noexcept {
init_fn(); init_fn();
} catch (const std::exception& e) { } catch (const std::exception& e) {
FBLOGE("error %s", e.what()); FBJNI_LOGE("error %s", e.what());
translatePendingCppExceptionToJavaException(); translatePendingCppExceptionToJavaException();
} catch (...) { } catch (...) {
translatePendingCppExceptionToJavaException(); translatePendingCppExceptionToJavaException();
@@ -54,19 +62,19 @@ jint initialize(JavaVM* vm, std::function<void()>&& init_fn) noexcept {
} }
alias_ref<JClass> findClassStatic(const char* name) { alias_ref<JClass> findClassStatic(const char* name) {
const auto env = internal::getEnv(); const auto env = detail::currentOrNull();
if (!env) { if (!env) {
throw std::runtime_error("Unable to retrieve JNIEnv*."); throw std::runtime_error("Unable to retrieve JNIEnv*.");
} }
auto cls = env->FindClass(name); local_ref<jclass> cls = adopt_local(env->FindClass(name));
FACEBOOK_JNI_THROW_EXCEPTION_IF(!cls); FACEBOOK_JNI_THROW_EXCEPTION_IF(!cls);
auto leaking_ref = (jclass)env->NewGlobalRef(cls); auto leaking_ref = (jclass)env->NewGlobalRef(cls.get());
FACEBOOK_JNI_THROW_EXCEPTION_IF(!leaking_ref); FACEBOOK_JNI_THROW_EXCEPTION_IF(!leaking_ref);
return wrap_alias(leaking_ref); return wrap_alias(leaking_ref);
} }
local_ref<JClass> findClassLocal(const char* name) { local_ref<JClass> findClassLocal(const char* name) {
const auto env = internal::getEnv(); const auto env = detail::currentOrNull();
if (!env) { if (!env) {
throw std::runtime_error("Unable to retrieve JNIEnv*."); throw std::runtime_error("Unable to retrieve JNIEnv*.");
} }
@@ -79,17 +87,25 @@ local_ref<JClass> findClassLocal(const char* name) {
// jstring ///////////////////////////////////////////////////////////////////////////////////////// // jstring /////////////////////////////////////////////////////////////////////////////////////////
std::string JString::toStdString() const { std::string JString::toStdString() const {
const auto env = internal::getEnv(); const auto env = Environment::current();
auto utf16String = JStringUtf16Extractor(env, self()); auto utf16String = JStringUtf16Extractor(env, self());
auto length = env->GetStringLength(self()); return detail::utf16toUTF8(utf16String.chars(), utf16String.length());
return detail::utf16toUTF8(utf16String, length); }
std::u16string JString::toU16String() const {
const auto env = Environment::current();
auto utf16String = JStringUtf16Extractor(env, self());
if (!utf16String.chars() || utf16String.length() == 0) {
return {};
}
return std::u16string(reinterpret_cast<const char16_t*>(utf16String.chars()), utf16String.length());
} }
local_ref<JString> make_jstring(const char* utf8) { local_ref<JString> make_jstring(const char* utf8) {
if (!utf8) { if (!utf8) {
return {}; return {};
} }
const auto env = internal::getEnv(); const auto env = Environment::current();
size_t len; size_t len;
size_t modlen = detail::modifiedLength(reinterpret_cast<const uint8_t*>(utf8), &len); size_t modlen = detail::modifiedLength(reinterpret_cast<const uint8_t*>(utf8), &len);
jstring result; jstring result;
@@ -112,6 +128,18 @@ local_ref<JString> make_jstring(const char* utf8) {
return adopt_local(result); return adopt_local(result);
} }
local_ref<JString> make_jstring(const std::u16string& utf16) {
if (utf16.empty()) {
return {};
}
const auto env = Environment::current();
static_assert(
sizeof(jchar) == sizeof(std::u16string::value_type),
"Expecting jchar to be the same size as std::u16string::CharT");
jstring result = env->NewString(reinterpret_cast<const jchar*>(utf16.c_str()), utf16.size());
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
return adopt_local(result);
}
// JniPrimitiveArrayFunctions ////////////////////////////////////////////////////////////////////// // JniPrimitiveArrayFunctions //////////////////////////////////////////////////////////////////////
@@ -120,50 +148,44 @@ local_ref<JString> make_jstring(const char* utf8) {
#define DEFINE_PRIMITIVE_METHODS(TYPE, NAME, SMALLNAME) \ #define DEFINE_PRIMITIVE_METHODS(TYPE, NAME, SMALLNAME) \
\ \
template<> \ template<> \
FBEXPORT \
TYPE* JPrimitiveArray<TYPE ## Array>::getElements(jboolean* isCopy) { \ TYPE* JPrimitiveArray<TYPE ## Array>::getElements(jboolean* isCopy) { \
auto env = internal::getEnv(); \ auto env = Environment::current(); \
TYPE* res = env->Get ## NAME ## ArrayElements(self(), isCopy); \ TYPE* res = env->Get ## NAME ## ArrayElements(self(), isCopy); \
FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); \ FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); \
return res; \ return res; \
} \ } \
\ \
template<> \ template<> \
FBEXPORT \
void JPrimitiveArray<TYPE ## Array>::releaseElements( \ void JPrimitiveArray<TYPE ## Array>::releaseElements( \
TYPE* elements, jint mode) { \ TYPE* elements, jint mode) { \
auto env = internal::getEnv(); \ auto env = Environment::current(); \
env->Release ## NAME ## ArrayElements(self(), elements, mode); \ env->Release ## NAME ## ArrayElements(self(), elements, mode); \
FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); \ FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); \
} \ } \
\ \
template<> \ template<> \
FBEXPORT \
void JPrimitiveArray<TYPE ## Array>::getRegion( \ void JPrimitiveArray<TYPE ## Array>::getRegion( \
jsize start, jsize length, TYPE* buf) { \ jsize start, jsize length, TYPE* buf) { \
auto env = internal::getEnv(); \ auto env = Environment::current(); \
env->Get ## NAME ## ArrayRegion(self(), start, length, buf); \ env->Get ## NAME ## ArrayRegion(self(), start, length, buf); \
FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); \ FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); \
} \ } \
\ \
template<> \ template<> \
FBEXPORT \
void JPrimitiveArray<TYPE ## Array>::setRegion( \ void JPrimitiveArray<TYPE ## Array>::setRegion( \
jsize start, jsize length, const TYPE* elements) { \ jsize start, jsize length, const TYPE* elements) { \
auto env = internal::getEnv(); \ auto env = Environment::current(); \
env->Set ## NAME ## ArrayRegion(self(), start, length, elements); \ env->Set ## NAME ## ArrayRegion(self(), start, length, elements); \
FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); \ FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); \
} \ } \
\ \
FBEXPORT \
local_ref<TYPE ## Array> make_ ## SMALLNAME ## _array(jsize size) { \ local_ref<TYPE ## Array> make_ ## SMALLNAME ## _array(jsize size) { \
auto array = internal::getEnv()->New ## NAME ## Array(size); \ auto array = Environment::current()->New ## NAME ## Array(size); \
FACEBOOK_JNI_THROW_EXCEPTION_IF(!array); \ FACEBOOK_JNI_THROW_EXCEPTION_IF(!array); \
return adopt_local(array); \ return adopt_local(array); \
} \ } \
\ \
template<> \ template<> \
FBEXPORT \
local_ref<TYPE ## Array> JArray ## NAME::newArray(size_t count) { \ local_ref<TYPE ## Array> JArray ## NAME::newArray(size_t count) { \
return make_ ## SMALLNAME ## _array(count); \ return make_ ## SMALLNAME ## _array(count); \
} \ } \
@@ -179,13 +201,37 @@ DEFINE_PRIMITIVE_METHODS(jfloat, Float, float)
DEFINE_PRIMITIVE_METHODS(jdouble, Double, double) DEFINE_PRIMITIVE_METHODS(jdouble, Double, double)
#pragma pop_macro("DEFINE_PRIMITIVE_METHODS") #pragma pop_macro("DEFINE_PRIMITIVE_METHODS")
namespace detail {
detail::BaseHybridClass* HybridDestructor::getNativePointer() {
static auto pointerField = javaClassStatic()->getField<jlong>("mNativePointer");
auto* value = reinterpret_cast<detail::BaseHybridClass*>(getFieldValue(pointerField));
if (!value) {
throwNewJavaException("java/lang/NullPointerException", "java.lang.NullPointerException");
}
return value;
}
void HybridDestructor::setNativePointer(
std::unique_ptr<detail::BaseHybridClass> new_value) {
static auto pointerField = javaClassStatic()->getField<jlong>("mNativePointer");
auto old_value = std::unique_ptr<detail::BaseHybridClass>(
reinterpret_cast<detail::BaseHybridClass*>(getFieldValue(pointerField)));
if (new_value && old_value) {
FBJNI_LOGF("Attempt to set C++ native pointer twice");
}
setFieldValue(pointerField, reinterpret_cast<jlong>(new_value.release()));
}
}
// Internal debug ///////////////////////////////////////////////////////////////////////////////// // Internal debug /////////////////////////////////////////////////////////////////////////////////
namespace internal { namespace internal {
FBEXPORT ReferenceStats g_reference_stats; ReferenceStats g_reference_stats;
FBEXPORT void facebook::jni::internal::ReferenceStats::reset() noexcept { void facebook::jni::internal::ReferenceStats::reset() noexcept {
locals_deleted = globals_deleted = weaks_deleted = 0; locals_deleted = globals_deleted = weaks_deleted = 0;
} }

View File

@@ -0,0 +1,32 @@
/**
* Copyright 2018-present, Facebook, Inc.
*
* 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.
*/
#pragma once
#include <jni.h>
#include <fbjni/detail/Environment.h>
#include <fbjni/detail/Log.h>
#include <fbjni/detail/Common.h>
#include <fbjni/detail/Exceptions.h>
#include <fbjni/detail/ReferenceAllocators.h>
#include <fbjni/detail/References.h>
#include <fbjni/detail/Meta.h>
#include <fbjni/detail/CoreClasses.h>
#include <fbjni/detail/Iterator.h>
#include <fbjni/detail/Hybrid.h>
#include <fbjni/detail/Registration.h>
#include <fbjni/detail/JWeakReference.h>

View File

@@ -0,0 +1,116 @@
/**
* Copyright 2004-present, Facebook, Inc.
*
* 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.
*/
#include <atomic>
#include <stdexcept>
#include <cxxabi.h>
#include <unwind.h>
#include <cassert>
#include <lyra/lyra_exceptions.h>
namespace facebook {
namespace lyra {
namespace {
std::atomic<bool> enableBacktraces{true};
}
void enableCxaThrowHookBacktraces(bool enable) {
enableBacktraces.store(enable, std::memory_order_relaxed);
}
[[gnu::noreturn]] void (*original_cxa_throw)(void*, const std::type_info*, void (*) (void *));
#if defined(_LIBCPP_VERSION)
[[noreturn]] void cxa_throw(void* obj, const std::type_info* type, void (*destructor) (void *)) {
// lyra doesn't have support yet for libc++.
original_cxa_throw(obj, type, destructor);
}
#else
using namespace detail;
namespace {
const auto traceHolderType =
static_cast<const abi::__class_type_info*>(&typeid(ExceptionTraceHolder));
// lyra's __cxa_throw attaches stack trace information to thrown exceptions. It basically does:
// 1. capture stack trace
// 2. construct a new type_info struct that:
// a. holds the ExceptionTraceHolder
// b. supports upcasting to lyra::ExceptionTraceHolder* (by just returning the holder member)
// c. acts like the original exception type_info otherwise
// 3. call original __cxa_throw() with original exception pointer, the
// HijackedExceptionTypeInfo, and HijackedExceptionTypeInfo::destructor
// (which will both delete the constructed type info and call the original
// destructor).
struct HijackedExceptionTypeInfo : public abi::__class_type_info {
HijackedExceptionTypeInfo(void* obj, const std::type_info* base, void(*destructor)(void*))
: abi::__class_type_info{base->name()}, base_{base}, orig_dest_{destructor} {
}
bool __is_pointer_p() const override {
return base_->__is_pointer_p();
}
bool __is_function_p() const override {
return base_->__is_function_p();
}
bool __do_catch(const type_info *__thr_type, void **__thr_obj, unsigned __outer) const override {
return base_->__do_catch(__thr_type, __thr_obj, __outer);
}
bool __do_upcast(const abi::__class_type_info *__target, void **__obj_ptr) const override {
if (__target == traceHolderType) {
*__obj_ptr = (void*)&stack_;
return true;
}
return base_->__do_upcast(__target, __obj_ptr);
}
static void destructor(void* obj) {
auto exc_ptr = reinterpret_cast<std::exception_ptr*>(&obj);
auto info = reinterpret_cast<const::std::type_info*>(exc_ptr->__cxa_exception_type());
auto mutable_info = static_cast<HijackedExceptionTypeInfo*>(const_cast<std::type_info*>(info));
mutable_info->orig_dest_(obj);
delete mutable_info;
}
private:
const std::type_info* base_;
void (*orig_dest_)(void*);
ExceptionTraceHolder stack_;
};
} // namespace
[[noreturn]] void cxa_throw(void* obj, const std::type_info* type, void (*destructor) (void *)) {
if (enableBacktraces.load(std::memory_order_relaxed)) {
if (!type->__do_upcast(traceHolderType, &obj)) {
type = new HijackedExceptionTypeInfo(obj, type, destructor);
destructor = HijackedExceptionTypeInfo::destructor;
}
}
original_cxa_throw(obj, type, destructor);
}
#endif // libc++
} // namespace lyra
} // namespace facebook

View File

@@ -1,14 +1,33 @@
// Copyright 2004-present Facebook. All Rights Reserved. /**
* Copyright 2004-present, Facebook, Inc.
*
* 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.
*/
#include <fb/lyra.h> #include <lyra/lyra.h>
#include <atomic>
#include <ios> #include <ios>
#include <ostream>
#include <iomanip>
#include <memory> #include <memory>
#include <vector> #include <vector>
#include <dlfcn.h> #include <dlfcn.h>
#include <unwind.h> #include <unwind.h>
#include <fbjni/detail/Log.h>
using namespace std; using namespace std;
namespace facebook { namespace facebook {
@@ -63,6 +82,27 @@ void captureBacktrace(size_t skip, vector<InstructionPointer>& stackTrace) {
BacktraceState state = {skip, stackTrace}; BacktraceState state = {skip, stackTrace};
_Unwind_Backtrace(unwindCallback, &state); _Unwind_Backtrace(unwindCallback, &state);
} }
// this is a pointer to a function
std::atomic<LibraryIdentifierFunctionType> gLibraryIdentifierFunction{nullptr};
}
void setLibraryIdentifierFunction(LibraryIdentifierFunctionType func) {
gLibraryIdentifierFunction.store(func, std::memory_order_relaxed);
}
std::string StackTraceElement::buildId() const {
if (!hasBuildId_) {
auto getBuildId = gLibraryIdentifierFunction.load(std::memory_order_relaxed);
if (getBuildId) {
buildId_ = getBuildId(libraryName());
} else {
buildId_ = "<unimplemented>";
}
hasBuildId_ = true;
}
return buildId_;
} }
void getStackTrace(vector<InstructionPointer>& stackTrace, size_t skip) { void getStackTrace(vector<InstructionPointer>& stackTrace, size_t skip) {
@@ -89,7 +129,6 @@ void getStackTraceSymbols(vector<StackTraceElement>& symbols,
ostream& operator<<(ostream& out, const StackTraceElement& elm) { ostream& operator<<(ostream& out, const StackTraceElement& elm) {
IosFlagsSaver flags{out}; IosFlagsSaver flags{out};
// TODO(t10748683): Add build id to the output
out << "{dso=" << elm.libraryName() << " offset=" << hex out << "{dso=" << elm.libraryName() << " offset=" << hex
<< showbase << elm.libraryOffset(); << showbase << elm.libraryOffset();
@@ -97,7 +136,7 @@ ostream& operator<<(ostream& out, const StackTraceElement& elm) {
out << " func=" << elm.functionName() << "()+" << elm.functionOffset(); out << " func=" << elm.functionName() << "()+" << elm.functionOffset();
} }
out << " build-id=" << hex << setw(8) << 0 out << " build-id=" << hex << setw(8) << elm.buildId()
<< "}"; << "}";
return out; return out;
@@ -116,5 +155,28 @@ ostream& operator<<(ostream& out, const vector<StackTraceElement>& trace) {
return out; return out;
} }
void logStackTrace(const vector<StackTraceElement>& trace) {
auto i = 0;
FBJNI_LOGE("Backtrace:");
for (auto& elm : trace) {
if (!elm.functionName().empty()) {
FBJNI_LOGE(" #%02d |lyra|{dso=%s offset=%#x func=%s+%#x build-id=%s}",
i++,
elm.libraryName().c_str(),
elm.libraryOffset(),
elm.functionName().c_str(),
elm.functionOffset(),
elm.buildId().c_str());
} else {
FBJNI_LOGE(" #%02d |lyra|{dso=%s offset=%#x build-id=%s}",
i++,
elm.libraryName().c_str(),
elm.libraryOffset(),
elm.buildId().c_str());
}
}
}
} }
} }

View File

@@ -1,14 +1,25 @@
// Copyright 2004-present Facebook. All Rights Reserved. /**
* Copyright 2004-present, Facebook, Inc.
*
* 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.
*/
#pragma once #pragma once
#include <iomanip> #include <iomanip>
#include <iostream>
#include <string> #include <string>
#include <vector> #include <vector>
#include <fb/visibility.h>
namespace facebook { namespace facebook {
namespace lyra { namespace lyra {
@@ -16,17 +27,21 @@ constexpr size_t kDefaultLimit = 64;
using InstructionPointer = const void*; using InstructionPointer = const void*;
class FBEXPORT StackTraceElement { class StackTraceElement {
public: public:
StackTraceElement(InstructionPointer absoluteProgramCounter, StackTraceElement(InstructionPointer absoluteProgramCounter,
InstructionPointer libraryBase, InstructionPointer libraryBase,
InstructionPointer functionAddress, std::string libraryName, InstructionPointer functionAddress,
std::string libraryName,
std::string functionName) std::string functionName)
: absoluteProgramCounter_{absoluteProgramCounter}, : absoluteProgramCounter_{absoluteProgramCounter},
libraryBase_{libraryBase}, libraryBase_{libraryBase},
functionAddress_{functionAddress}, functionAddress_{functionAddress},
libraryName_{std::move(libraryName)}, libraryName_{std::move(libraryName)},
functionName_{std::move(functionName)} {} functionName_{std::move(functionName)},
hasBuildId_{false},
buildId_{}
{}
InstructionPointer libraryBase() const noexcept { return libraryBase_; } InstructionPointer libraryBase() const noexcept { return libraryBase_; }
@@ -63,14 +78,28 @@ class FBEXPORT StackTraceElement {
return absoluteabsoluteProgramCounter - absoluteSymbol; return absoluteabsoluteProgramCounter - absoluteSymbol;
} }
std::string buildId() const;
private: private:
const InstructionPointer absoluteProgramCounter_; const InstructionPointer absoluteProgramCounter_;
const InstructionPointer libraryBase_; const InstructionPointer libraryBase_;
const InstructionPointer functionAddress_; const InstructionPointer functionAddress_;
const std::string libraryName_; const std::string libraryName_;
const std::string functionName_; const std::string functionName_;
mutable bool hasBuildId_;
mutable std::string buildId_;
}; };
/**
* If a library identifier function is set, it is passed a libraryName
* for the frame, and returns a library build id string, which will be
* included in the logged stack trace. The most common use for this
* will be correlating stack traces with breakpad identifiers.
*/
typedef std::string (*LibraryIdentifierFunctionType)(const std::string&);
void setLibraryIdentifierFunction(LibraryIdentifierFunctionType func);
/** /**
* Populate the vector with the current stack trace * Populate the vector with the current stack trace
* *
@@ -87,8 +116,7 @@ class FBEXPORT StackTraceElement {
* *
* @param skip The number of frames to skip before capturing the trace * @param skip The number of frames to skip before capturing the trace
*/ */
FBEXPORT void getStackTrace(std::vector<InstructionPointer>& stackTrace, void getStackTrace(std::vector<InstructionPointer>& stackTrace, size_t skip = 0);
size_t skip = 0);
/** /**
* Creates a vector and populates it with the current stack trace * Creates a vector and populates it with the current stack trace
@@ -104,7 +132,7 @@ FBEXPORT void getStackTrace(std::vector<InstructionPointer>& stackTrace,
* *
* @limit The maximum number of frames captured * @limit The maximum number of frames captured
*/ */
FBEXPORT inline std::vector<InstructionPointer> getStackTrace( inline std::vector<InstructionPointer> getStackTrace(
size_t skip = 0, size_t skip = 0,
size_t limit = kDefaultLimit) { size_t limit = kDefaultLimit) {
auto stackTrace = std::vector<InstructionPointer>{}; auto stackTrace = std::vector<InstructionPointer>{};
@@ -121,15 +149,15 @@ FBEXPORT inline std::vector<InstructionPointer> getStackTrace(
* *
* @param stackTrace The input stack trace * @param stackTrace The input stack trace
*/ */
FBEXPORT void getStackTraceSymbols(std::vector<StackTraceElement>& symbols, void getStackTraceSymbols(std::vector<StackTraceElement>& symbols,
const std::vector<InstructionPointer>& trace); const std::vector<InstructionPointer>& trace);
/** /**
* Symbolicates a stack trace into a new vector * Symbolicates a stack trace into a new vector
* *
* @param stackTrace The input stack trace * @param stackTrace The input stack trace
*/ */
FBEXPORT inline std::vector<StackTraceElement> getStackTraceSymbols( inline std::vector<StackTraceElement> getStackTraceSymbols(
const std::vector<InstructionPointer>& trace) { const std::vector<InstructionPointer>& trace) {
auto symbols = std::vector<StackTraceElement>{}; auto symbols = std::vector<StackTraceElement>{};
getStackTraceSymbols(symbols, trace); getStackTraceSymbols(symbols, trace);
@@ -148,7 +176,7 @@ FBEXPORT inline std::vector<StackTraceElement> getStackTraceSymbols(
* *
* @param limit The maximum number of frames captured * @param limit The maximum number of frames captured
*/ */
FBEXPORT inline std::vector<StackTraceElement> getStackTraceSymbols( inline std::vector<StackTraceElement> getStackTraceSymbols(
size_t skip = 0, size_t skip = 0,
size_t limit = kDefaultLimit) { size_t limit = kDefaultLimit) {
return getStackTraceSymbols(getStackTrace(skip + 1, limit)); return getStackTraceSymbols(getStackTrace(skip + 1, limit));
@@ -157,12 +185,21 @@ FBEXPORT inline std::vector<StackTraceElement> getStackTraceSymbols(
/** /**
* Formatting a stack trace element * Formatting a stack trace element
*/ */
FBEXPORT std::ostream& operator<<(std::ostream& out, const StackTraceElement& elm); std::ostream& operator<<(std::ostream& out, const StackTraceElement& elm);
/** /**
* Formatting a stack trace * Formatting a stack trace
*/ */
FBEXPORT std::ostream& operator<<(std::ostream& out, std::ostream& operator<<(std::ostream& out,
const std::vector<StackTraceElement>& trace); const std::vector<StackTraceElement>& trace);
/**
* Log stack trace
*
* Makes it possible to log a trace without using a temporary stream when the
* underlying log API is not stream based.
*/
void logStackTrace(const std::vector<StackTraceElement>& trace);
} }
} }

View File

@@ -0,0 +1,32 @@
/**
* Copyright 2004-present, Facebook, Inc.
*
* 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.
*/
#include <lyra/lyra.h>
namespace facebook {
namespace lyra {
/**
* This can be overridden by an implementation capable of looking up
* the breakpad id for logging purposes.
*/
__attribute__((weak))
std::string getBreakpadId(const std::string& library) {
return "<unimplemented>";
}
}
}

View File

@@ -0,0 +1,98 @@
/**
* Copyright 2004-present, Facebook, Inc.
*
* 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.
*/
#include <lyra/lyra_exceptions.h>
#include <cstdlib>
#include <exception>
#include <sstream>
#include <typeinfo>
#include <fbjni/detail/Log.h>
namespace facebook {
namespace lyra {
using namespace detail;
namespace {
std::terminate_handler gTerminateHandler;
const ExceptionTraceHolder* getExceptionTraceHolder(std::exception_ptr ptr) {
try {
std::rethrow_exception(ptr);
} catch (const ExceptionTraceHolder& holder) {
return &holder;
} catch (...) {
return nullptr;
}
}
void logExceptionAndAbort() {
if (auto ptr = std::current_exception()) {
FBJNI_LOGE("Uncaught exception: %s", toString(ptr).c_str());
auto trace = getExceptionTraceHolder(ptr);
if (trace) {
logStackTrace(getStackTraceSymbols(trace->stackTrace_));
}
}
if (gTerminateHandler) {
gTerminateHandler();
} else {
FBJNI_LOGF("Uncaught exception and no gTerminateHandler set");
}
}
const std::vector<InstructionPointer> emptyTrace;
} // namespace
ExceptionTraceHolder::~ExceptionTraceHolder() {}
detail::ExceptionTraceHolder::ExceptionTraceHolder() {
// TODO(cjhopman): This should be done more safely (i.e. use preallocated space, etc.).
stackTrace_.reserve(128);
getStackTrace(stackTrace_, 1);
}
void ensureRegisteredTerminateHandler() {
static auto initializer = (gTerminateHandler = std::set_terminate(logExceptionAndAbort));
(void)initializer;
}
const std::vector<InstructionPointer>& getExceptionTrace(std::exception_ptr ptr) {
auto holder = getExceptionTraceHolder(ptr);
return holder ? holder->stackTrace_ : emptyTrace;
}
std::string toString(std::exception_ptr ptr) {
if (!ptr) {
return "No exception";
}
try {
std::rethrow_exception(ptr);
} catch (std::exception& e) {
std::stringstream ss;
ss << typeid(e).name() << ": " << e.what();
return ss.str();
} catch (...) {
return "Unknown exception";
}
}
}
}

View File

@@ -0,0 +1,94 @@
/**
* Copyright 2004-present, Facebook, Inc.
*
* 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.
*/
#pragma once
#include <exception>
#include <typeinfo>
#include <vector>
#include <lyra/lyra.h>
namespace facebook {
namespace lyra {
namespace detail {
struct ExceptionTraceHolder {
ExceptionTraceHolder();
// Need some virtual function to make this a polymorphic type.
virtual ~ExceptionTraceHolder();
ExceptionTraceHolder(const ExceptionTraceHolder&) = delete;
ExceptionTraceHolder(ExceptionTraceHolder&&) = default;
std::vector<InstructionPointer> stackTrace_;
};
template <typename E, bool hasTraceHolder>
struct Holder : E, ExceptionTraceHolder {
Holder(E&& e) : E{std::forward<E>(e)}, ExceptionTraceHolder{} {}
};
template <typename E>
struct Holder<E, true> : E {
Holder(E&& e) : E{std::forward<E>(e)} {}
};
}
/**
* Retrieves the stack trace of an exception
*/
const std::vector<InstructionPointer>& getExceptionTrace(std::exception_ptr ptr);
/**
* Throw an exception and store the stack trace. This works like
* std::throw_with_nested in that it will actually throw a type that is
* publicly derived from both E and detail::ExceptionTraceHolder.
*/
template <class E>
[[noreturn]] void fbthrow(E&& exception) {
throw detail::Holder<E, std::is_base_of<detail::ExceptionTraceHolder, E>::value>{std::forward<E>(exception)};
}
/**
* Ensure that a terminate handler that logs traces is installed.
* setLibraryIdentifierFunction should be called first if the stack
* trace should log build ids for libraries.
*/
void ensureRegisteredTerminateHandler();
/**
* Helper to convert an exception to a string
*/
std::string toString(std::exception_ptr exceptionPointer);
/**
* lyra's cxa_throw will delegate to the original cxa throw. That pointer must
* be set before lyra::cxa_throw is called.
*
* One example use would be to statically compile against something that overrides __cxa_throw.
* That would look something like:
*
* [[noreturn]] void __cxa_throw(void* obj, const std::type_info* type, void (*destructor) (void*)) {
* static auto initializer = lyra::original_cxa_throw = lookupOriginalCxaThrow();
* lyra::cxa_throw(obj, type, destructor);
* }
*/
[[gnu::noreturn]] extern void (*original_cxa_throw)(void*, const std::type_info*, void (*) (void*));
[[noreturn]] void cxa_throw(void* obj, const std::type_info* type, void (*destructor) (void *));
void enableCxaThrowHookBacktraces(bool enable);
}
}

View File

@@ -0,0 +1,27 @@
/**
* Copyright 2018-present, Facebook, Inc.
*
* 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.
*/
package com.facebook.jni;
import com.facebook.jni.annotations.DoNotStrip;
@DoNotStrip
public class CppException extends RuntimeException {
@DoNotStrip
public CppException(String message) {
super(message);
}
}

View File

@@ -0,0 +1,34 @@
/**
* Copyright 2018-present, Facebook, Inc.
*
* 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.
*/
package com.facebook.jni;
import com.facebook.jni.annotations.DoNotStrip;
@DoNotStrip
public class CppSystemErrorException extends CppException {
int errorCode;
@DoNotStrip
public CppSystemErrorException(String message, int errorCode) {
super(message);
this.errorCode = errorCode;
}
public int getErrorCode() {
return errorCode;
}
}

View File

@@ -0,0 +1,148 @@
/**
* Copyright 2004-present, Facebook, Inc.
*
* 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.
*/
package com.facebook.jni;
import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
import java.util.concurrent.atomic.AtomicReference;
/**
* A thread which invokes the "destruct" routine for objects after they have been garbage collected.
*
* <p>An object which needs to be destructed should create a static subclass of {@link Destructor}.
* Once the referent object is garbage collected, the DestructorThread will callback to the {@link
* Destructor#destruct()} method.
*
* <p>The underlying thread in DestructorThread starts when the first Destructor is constructed and
* then runs indefinitely.
*/
public class DestructorThread {
/**
* N.B The Destructor <b>SHOULD NOT</b> refer back to its referent object either explicitly or
* implicitly (for example, as a non-static inner class). This will create a reference cycle where
* the referent object will never be garbage collected.
*/
public abstract static class Destructor extends PhantomReference<Object> {
private Destructor next;
private Destructor previous;
Destructor(Object referent) {
super(referent, sReferenceQueue);
sDestructorStack.push(this);
}
private Destructor() {
super(null, sReferenceQueue);
}
/** Callback which is invoked when the original object has been garbage collected. */
abstract void destruct();
}
/** A list to keep all active Destructors in memory confined to the Destructor thread. */
private static DestructorList sDestructorList;
/** A thread safe stack where new Destructors are placed before being add to sDestructorList. */
private static DestructorStack sDestructorStack;
private static ReferenceQueue sReferenceQueue;
private static Thread sThread;
static {
sDestructorStack = new DestructorStack();
sReferenceQueue = new ReferenceQueue();
sDestructorList = new DestructorList();
sThread =
new Thread("HybridData DestructorThread") {
@Override
public void run() {
while (true) {
try {
Destructor current = (Destructor) sReferenceQueue.remove();
current.destruct();
// If current is in the sDestructorStack,
// transfer all the Destructors in the stack to the list.
if (current.previous == null) {
sDestructorStack.transferAllToList();
}
DestructorList.drop(current);
} catch (InterruptedException e) {
// Continue. This thread should never be terminated.
}
}
}
};
sThread.start();
}
private static class Terminus extends Destructor {
@Override
void destruct() {
throw new IllegalStateException("Cannot destroy Terminus Destructor.");
}
}
/** This is a thread safe, lock-free Treiber-like Stack of Destructors. */
private static class DestructorStack {
private AtomicReference<Destructor> mHead = new AtomicReference<>();
public void push(Destructor newHead) {
Destructor oldHead;
do {
oldHead = mHead.get();
newHead.next = oldHead;
} while (!mHead.compareAndSet(oldHead, newHead));
}
public void transferAllToList() {
Destructor current = mHead.getAndSet(null);
while (current != null) {
Destructor next = current.next;
sDestructorList.enqueue(current);
current = next;
}
}
}
/** A doubly-linked list of Destructors. */
private static class DestructorList {
private Destructor mHead;
public DestructorList() {
mHead = new Terminus();
mHead.next = new Terminus();
mHead.next.previous = mHead;
}
public void enqueue(Destructor current) {
current.next = mHead.next;
mHead.next = current;
current.next.previous = current;
current.previous = mHead;
}
private static void drop(Destructor current) {
current.next.previous = current.previous;
current.previous.next = current.next;
}
}
}

View File

@@ -0,0 +1,22 @@
/**
* Copyright 2004-present, Facebook, Inc.
*
* 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.
*/
package com.facebook.jni;
import com.facebook.jni.annotations.DoNotStrip;
@DoNotStrip
public abstract class HybridClassBase extends HybridData {}

View File

@@ -0,0 +1,87 @@
/**
* Copyright 2004-present, Facebook, Inc.
*
* 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.
*/
package com.facebook.jni;
import com.facebook.jni.annotations.DoNotStrip;
import com.facebook.soloader.SoLoader;
/**
* This object holds a native C++ member for hybrid Java/C++ objects.
*
* <p>NB: THREAD SAFETY
*
* <p>{@link #resetNative} deletes the corresponding native object synchronously on whatever thread
* the method is called on. Otherwise, deletion will occur on the {@link DestructorThread} thread.
*/
@DoNotStrip
public class HybridData {
static {
SoLoader.loadLibrary("fbjni");
}
@DoNotStrip private Destructor mDestructor = new Destructor(this);
/**
* To explicitly delete the instance, call resetNative(). If the C++ instance is referenced after
* this is called, a NullPointerException will be thrown. resetNative() may be called multiple
* times safely. Because the {@link DestructorThread} also calls resetNative, the instance will
* not leak if this is not called, but timing of deletion and the thread the C++ dtor is called on
* will be at the whim of the Java GC. If you want to control the thread and timing of the
* destructor, you should call resetNative() explicitly.
*/
public synchronized void resetNative() {
mDestructor.destruct();
}
/**
* N.B. Thread safety. If you call isValid from a different thread than {@link #resetNative()}
* then be sure to do so while synchronizing on the hybrid. For example:
*
* <pre><code>
* synchronized(hybrid) {
* if (hybrid.isValid) {
* // Do stuff.
* }
* }
* </code></pre>
*/
public boolean isValid() {
return mDestructor.mNativePointer != 0;
}
public static class Destructor extends DestructorThread.Destructor {
// Private C++ instance
@DoNotStrip private long mNativePointer;
Destructor(Object referent) {
super(referent);
}
@Override
void destruct() {
// When invoked from the DestructorThread instead of resetNative,
// the DestructorThread has exclusive ownership of the HybridData
// so synchronization is not necessary.
deleteNative(mNativePointer);
mNativePointer = 0;
}
static native void deleteNative(long pointer);
}
}

View File

@@ -0,0 +1,58 @@
/**
* Copyright 2018-present, Facebook, Inc.
*
* 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.
*/
package com.facebook.jni;
import com.facebook.jni.annotations.DoNotStrip;
import java.util.Iterator;
import javax.annotation.Nullable;
/**
* To iterate over an Iterator from C++ requires two calls per entry: hasNext() and next(). This
* helper reduces it to one call and one field get per entry. It does not use a generic argument,
* since in C++, the types will be erased, anyway. This is *not* a {@link java.util.Iterator}.
*/
@DoNotStrip
public class IteratorHelper {
private final Iterator mIterator;
// This is private, but accessed via JNI.
@DoNotStrip private @Nullable Object mElement;
@DoNotStrip
public IteratorHelper(Iterator iterator) {
mIterator = iterator;
}
@DoNotStrip
public IteratorHelper(Iterable iterable) {
mIterator = iterable.iterator();
}
/**
* Moves the helper to the next entry in the map, if any. Returns true iff there is an entry to
* read.
*/
@DoNotStrip
boolean hasNext() {
if (mIterator.hasNext()) {
mElement = mIterator.next();
return true;
} else {
mElement = null;
return false;
}
}
}

View File

@@ -0,0 +1,57 @@
/**
* Copyright 2018-present, Facebook, Inc.
*
* 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.
*/
package com.facebook.jni;
import com.facebook.jni.annotations.DoNotStrip;
import java.util.Iterator;
import java.util.Map;
import javax.annotation.Nullable;
/**
* To iterate over a Map from C++ requires four calls per entry: hasNext(), next(), getKey(),
* getValue(). This helper reduces it to one call and two field gets per entry. It does not use a
* generic argument, since in C++, the types will be erased, anyway. This is *not* a {@link
* java.util.Iterator}.
*/
@DoNotStrip
public class MapIteratorHelper {
@DoNotStrip private final Iterator<Map.Entry> mIterator;
@DoNotStrip private @Nullable Object mKey;
@DoNotStrip private @Nullable Object mValue;
@DoNotStrip
public MapIteratorHelper(Map map) {
mIterator = map.entrySet().iterator();
}
/**
* Moves the helper to the next entry in the map, if any. Returns true iff there is an entry to
* read.
*/
@DoNotStrip
boolean hasNext() {
if (mIterator.hasNext()) {
Map.Entry entry = mIterator.next();
mKey = entry.getKey();
mValue = entry.getValue();
return true;
} else {
mKey = null;
mValue = null;
return false;
}
}
}

View File

@@ -0,0 +1,31 @@
/**
* Copyright 2018-present, Facebook, Inc.
*
* 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.
*/
package com.facebook.jni;
import com.facebook.jni.annotations.DoNotStrip;
/** A Runnable that has a native run implementation. */
@DoNotStrip
public class NativeRunnable implements Runnable {
private final HybridData mHybridData;
private NativeRunnable(HybridData hybridData) {
mHybridData = hybridData;
}
public native void run();
}

View File

@@ -0,0 +1,36 @@
/**
* Copyright 2004-present, Facebook, Inc.
*
* 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.
*/
package com.facebook.jni;
import com.facebook.jni.annotations.DoNotStrip;
import com.facebook.soloader.SoLoader;
@DoNotStrip
public class ThreadScopeSupport {
static {
SoLoader.loadLibrary("fbjni");
}
// This is just used for ThreadScope::withClassLoader to have a java function
// in the stack so that jni has access to the correct classloader.
@DoNotStrip
private static void runStdFunction(long ptr) {
runStdFunctionImpl(ptr);
}
private static native void runStdFunctionImpl(long ptr);
}

View File

@@ -0,0 +1,32 @@
/**
* Copyright 2018-present, Facebook, Inc.
*
* 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.
*/
package com.facebook.jni;
import com.facebook.jni.annotations.DoNotStrip;
@DoNotStrip
public class UnknownCppException extends CppException {
@DoNotStrip
public UnknownCppException() {
super("Unknown");
}
@DoNotStrip
public UnknownCppException(String message) {
super(message);
}
}

View File

@@ -0,0 +1,33 @@
/**
* Copyright 2004-present, Facebook, Inc.
*
* 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.
*/
package com.facebook.jni.annotations;
import static java.lang.annotation.RetentionPolicy.CLASS;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
/**
* Add this annotation to a class, method, or field to instruct Proguard to not strip it out.
*
* This is useful for methods called via reflection that could appear as unused to Proguard.
*/
@Target({ ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.CONSTRUCTOR })
@Retention(CLASS)
public @interface DoNotStrip {
}

View File

@@ -1,14 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (c) 2014-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.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.facebook.libfb">
</manifest>

View File

@@ -1,33 +0,0 @@
#
# Copyright (c) 2014-present, Facebook, Inc.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
#
cmake_minimum_required(VERSION 3.6.0)
set(CMAKE_VERBOSE_MAKEFILE on)
add_compile_options(
-fno-omit-frame-pointer
-fexceptions
-Wall
-std=c++11
-DDISABLE_CPUCAP
-DDISABLE_XPLAT)
file(GLOB fb_SRC
*.cpp
jni/*.cpp
lyra/*.cpp)
set(libjnihack_DIR ../../../../jni-hack/)
add_library(fb SHARED
${fb_SRC})
target_include_directories(fb PRIVATE
include ${libjnihack_DIR})
target_link_libraries(fb log)

View File

@@ -1,39 +0,0 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#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

@@ -1,81 +0,0 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/** @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

@@ -1,45 +0,0 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <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

@@ -1,18 +0,0 @@
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

@@ -1,48 +0,0 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <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

@@ -1,272 +0,0 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <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

@@ -1,38 +0,0 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <fb/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

@@ -1,116 +0,0 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <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

@@ -1,34 +0,0 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#ifndef FBASSERT_H
#define FBASSERT_H
#include <fb/visibility.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__)
FBEXPORT 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

@@ -1,23 +0,0 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <jni.h>
#include <fb/Environment.h>
#include <fb/ALog.h>
#include <fb/fbjni/Common.h>
#include <fb/fbjni/Exceptions.h>
#include <fb/fbjni/JThrowable.h>
#include <fb/fbjni/ReferenceAllocators.h>
#include <fb/fbjni/References.h>
#include <fb/fbjni/Meta.h>
#include <fb/fbjni/CoreClasses.h>
#include <fb/fbjni/Iterator.h>
#include <fb/fbjni/Hybrid.h>
#include <fb/fbjni/Registration.h>

View File

@@ -1,32 +0,0 @@
/*
* Copyright (c) 2016-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <fb/visibility.h>
#include "CoreClasses.h"
#include "References-forward.h"
namespace facebook {
namespace jni {
// JNI's NIO support has some awkward preconditions and error reporting. This
// class provides much more user-friendly access.
class FBEXPORT JByteBuffer : public JavaClass<JByteBuffer> {
public:
static constexpr const char* kJavaDescriptor = "Ljava/nio/ByteBuffer;";
static local_ref<JByteBuffer> wrapBytes(uint8_t* data, size_t size);
bool isDirect() const;
uint8_t* getDirectBytes() const;
size_t getDirectSize() const;
};
}}

View File

@@ -1,33 +0,0 @@
/*
* Copyright (c) 2016-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include "CoreClasses.h"
#include "File.h"
namespace facebook {
namespace jni {
class AContext : public JavaClass<AContext> {
public:
static constexpr const char* kJavaDescriptor = "Landroid/content/Context;";
// Define a method that calls into the represented Java class
local_ref<JFile::javaobject> getCacheDir() {
static auto method = getClass()->getMethod<JFile::javaobject()>("getCacheDir");
return method(self());
}
local_ref<JFile::javaobject> getFilesDir() {
static auto method = getClass()->getMethod<JFile::javaobject()>("getFilesDir");
return method(self());
}
};
}
}

View File

@@ -1,28 +0,0 @@
/*
* Copyright (c) 2016-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include "CoreClasses.h"
namespace facebook {
namespace jni {
class JFile : public JavaClass<JFile> {
public:
static constexpr const char* kJavaDescriptor = "Ljava/io/File;";
// Define a method that calls into the represented Java class
std::string getAbsolutePath() {
static auto method = getClass()->getMethod<jstring()>("getAbsolutePath");
return method(self())->toStdString();
}
};
}
}

View File

@@ -1,37 +0,0 @@
/*
* Copyright (c) 2016-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include "CoreClasses.h"
#include "NativeRunnable.h"
namespace facebook {
namespace jni {
class JThread : public JavaClass<JThread> {
public:
static constexpr const char* kJavaDescriptor = "Ljava/lang/Thread;";
void start() {
static auto method = javaClassStatic()->getMethod<void()>("start");
method(self());
}
void join() {
static auto method = javaClassStatic()->getMethod<void()>("join");
method(self());
}
static local_ref<JThread> create(std::function<void()>&& runnable) {
auto jrunnable = JNativeRunnable::newObjectCxxArgs(std::move(runnable));
return newInstance(static_ref_cast<JRunnable::javaobject>(jrunnable));
}
};
}
}

View File

@@ -1,14 +0,0 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include "CoreClasses.h"
struct JThrowable : public facebook::jni::JavaClass<JThrowable, facebook::jni::JThrowable> {
constexpr static auto kJavaDescriptor = "Ljava/lang/Throwable;";
std::string getStackTrace() const;
};

View File

@@ -1,34 +0,0 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
namespace facebook {
namespace jni {
template<typename F>
class JMethod;
template<typename F>
class JStaticMethod;
template<typename F>
class JNonvirtualMethod;
template<typename F>
struct JConstructor;
template<typename F>
class JField;
template<typename F>
class JStaticField;
/// Type traits for Java types (currently providing Java type descriptors)
template<typename T>
struct jtype_traits;
/// Type traits for Java methods (currently providing Java type descriptors)
template<typename F>
struct jmethod_traits;
}}

View File

@@ -1,60 +0,0 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* @file ReferenceAllocators.h
*
* Reference allocators are used to create and delete various classes of JNI references (local,
* global, and weak global).
*/
#pragma once
#include <fb/visibility.h>
#include "Common.h"
namespace facebook { namespace jni {
/// Allocator that handles local references
class FBEXPORT LocalReferenceAllocator {
public:
jobject newReference(jobject original) const;
void deleteReference(jobject reference) const noexcept;
bool verifyReference(jobject reference) const noexcept;
};
/// Allocator that handles global references
class FBEXPORT GlobalReferenceAllocator {
public:
jobject newReference(jobject original) const;
void deleteReference(jobject reference) const noexcept;
bool verifyReference(jobject reference) const noexcept;
};
/// Allocator that handles weak global references
class FBEXPORT WeakGlobalReferenceAllocator {
public:
jobject newReference(jobject original) const;
void deleteReference(jobject reference) const noexcept;
bool verifyReference(jobject reference) const noexcept;
};
/// @cond INTERNAL
namespace internal {
/**
* @return true iff env->GetObjectRefType is expected to work properly.
*/
FBEXPORT bool doesGetObjectRefTypeWork();
}
/// @endcond
}}
#include "ReferenceAllocators-inl.h"

View File

@@ -1,184 +0,0 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include "Exceptions.h"
#include "Hybrid.h"
namespace facebook {
namespace jni {
namespace detail {
#ifdef __i386__
// X86 ABI forces 16 byte stack allignment on calls. Unfortunately
// sometimes Dalvik chooses not to obey the ABI:
// - https://code.google.com/p/android/issues/detail?id=61012
// - https://android.googlesource.com/platform/ndk/+/81696d2%5E!/
// Therefore, we tell the compiler to re-align the stack on entry
// to our JNI functions.
#define JNI_ENTRY_POINT __attribute__((force_align_arg_pointer))
#else
#define JNI_ENTRY_POINT
#endif
// 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 {
JNI_ENTRY_POINT 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 {
JNI_ENTRY_POINT static R call(JNIEnv* env, jobject obj, Args... args) {
try {
return (*func)(env, static_cast<JniType<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 {
JNI_ENTRY_POINT static void call(JNIEnv*, jobject obj,
typename Convert<typename std::decay<Args>::type>::jniType... args) {
try {
(*func)(static_cast<JniType<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 {
JNI_ENTRY_POINT static typename Convert<typename std::decay<R>::type>::jniType 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<JniType<C>>(obj), Convert<typename std::decay<Args>::type>::fromJni(args)...));
} catch (...) {
using jniRet = typename Convert<typename std::decay<R>::type>::jniType;
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 {
JNI_ENTRY_POINT 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 {
JNI_ENTRY_POINT static typename Convert<typename std::decay<R>::type>::jniType 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 (...) {
using jniRet = typename Convert<typename std::decay<R>::type>::jniType;
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)) {
return jmethod_traits_from_cxx<R(Args...)>::descriptor();
}
template<typename R, typename C, typename... Args>
inline std::string makeDescriptor(R (C::*)(Args... args)) {
return jmethod_traits_from_cxx<R(Args...)>::descriptor();
}
}
}}

View File

@@ -1,346 +0,0 @@
/*
* 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
#include <fb/visibility.h>
#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);
FBEXPORT void setLogHandler(LogHandler logHandler);
/*
* ===========================================================================
*
* The stuff in the rest of this file should not be used directly.
*/
FBEXPORT 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

@@ -1,19 +0,0 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
namespace facebook {
struct noncopyable {
noncopyable(const noncopyable&) = delete;
noncopyable& operator=(const noncopyable&) = delete;
protected:
noncopyable() = default;
};
}

View File

@@ -1,19 +0,0 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
namespace facebook {
struct nonmovable {
nonmovable(nonmovable&&) = delete;
nonmovable& operator=(nonmovable&&) = delete;
protected:
nonmovable() = default;
};
}

View File

@@ -1,10 +0,0 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#define FBEXPORT __attribute__((visibility("default")))

View File

@@ -1,34 +0,0 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <jni.h>
#include <fb/Countable.h>
#include <fb/RefPtr.h>
#include <fb/visibility.h>
namespace facebook {
namespace jni {
FBEXPORT const RefPtr<Countable>& countableFromJava(JNIEnv* env, jobject obj);
template <typename T> RefPtr<T> extractRefPtr(JNIEnv* env, jobject obj) {
return static_cast<RefPtr<T>>(countableFromJava(env, obj));
}
template <typename T> RefPtr<T> extractPossiblyNullRefPtr(JNIEnv* env, jobject obj) {
return obj ? extractRefPtr<T>(env, obj) : nullptr;
}
FBEXPORT void setCountableForJava(JNIEnv* env, jobject obj, RefPtr<Countable>&& countable);
void CountableOnLoad(JNIEnv* env);
} }

View File

@@ -1,89 +0,0 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <memory>
#include <type_traits>
#include <jni.h>
#include <fb/Environment.h>
namespace facebook { namespace jni {
template<typename T>
class GlobalReference {
static_assert(std::is_convertible<T, jobject>::value,
"GlobalReference<T> instantiated with type that is not "
"convertible to jobject");
public:
explicit GlobalReference(T globalReference) :
reference_(globalReference? Environment::current()->NewGlobalRef(globalReference) : nullptr) {
}
~GlobalReference() {
reset();
}
GlobalReference() :
reference_(nullptr) {
}
// enable move constructor and assignment
GlobalReference(GlobalReference&& rhs) :
reference_(std::move(rhs.reference_)) {
rhs.reference_ = nullptr;
}
GlobalReference& operator=(GlobalReference&& rhs) {
if (this != &rhs) {
reset();
reference_ = std::move(rhs.reference_);
rhs.reference_ = nullptr;
}
return *this;
}
GlobalReference(const GlobalReference<T>& rhs) :
reference_{} {
reset(rhs.get());
}
GlobalReference& operator=(const GlobalReference<T>& rhs) {
if (this == &rhs) {
return *this;
}
reset(rhs.get());
return *this;
}
explicit operator bool() const {
return (reference_ != nullptr);
}
T get() const {
return reinterpret_cast<T>(reference_);
}
void reset(T globalReference = nullptr) {
if (reference_) {
Environment::current()->DeleteGlobalRef(reference_);
}
if (globalReference) {
reference_ = Environment::current()->NewGlobalRef(globalReference);
} else {
reference_ = nullptr;
}
}
private:
jobject reference_;
};
}}

View File

@@ -1,35 +0,0 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <memory>
#include <type_traits>
#include <jni.h>
#include <fb/Environment.h>
namespace facebook {
namespace jni {
template<class T>
struct LocalReferenceDeleter {
static_assert(std::is_convertible<T, jobject>::value,
"LocalReferenceDeleter<T> instantiated with type that is not convertible to jobject");
void operator()(T localReference) {
if (localReference != nullptr) {
Environment::current()->DeleteLocalRef(localReference);
}
}
};
template<class T>
using LocalReference =
std::unique_ptr<typename std::remove_pointer<T>::type, LocalReferenceDeleter<T>>;
} }

View File

@@ -1,25 +0,0 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <jni.h>
#include <initializer_list>
#include <fb/assert.h>
namespace facebook {
namespace jni {
static inline void registerNatives(JNIEnv* env, jclass cls, std::initializer_list<JNINativeMethod> methods) {
auto result = env->RegisterNatives(cls, methods.begin(), methods.size());
FBASSERT(result == 0);
}
static inline void registerNatives(JNIEnv* env, const char* cls, std::initializer_list<JNINativeMethod> list) {
registerNatives(env, env->FindClass(cls), list);
}
} }

View File

@@ -1,53 +0,0 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <string>
#include <jni.h>
#include <fb/noncopyable.h>
#include <fb/Countable.h>
#include <fb/visibility.h>
namespace facebook {
namespace jni {
class FBEXPORT WeakReference : public Countable {
public:
typedef RefPtr<WeakReference> Ptr;
WeakReference(jobject strongRef);
~WeakReference();
jweak weakRef() {
return m_weakReference;
}
private:
jweak m_weakReference;
};
// This class is intended to take a weak reference and turn it into a strong
// local reference. Consequently, it should only be allocated on the stack.
class FBEXPORT ResolvedWeakReference : public noncopyable {
public:
ResolvedWeakReference(jobject weakRef);
ResolvedWeakReference(const RefPtr<WeakReference>& weakRef);
~ResolvedWeakReference();
operator jobject () {
return m_strongReference;
}
explicit operator bool () {
return m_strongReference != nullptr;
}
private:
jobject m_strongReference;
};
} }

View File

@@ -1,137 +0,0 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <jni.h>
#include <fb/visibility.h>
namespace facebook {
/**
* Instructs the JNI environment to throw an exception.
*
* @param pEnv JNI environment
* @param szClassName class name to throw
* @param szFmt sprintf-style format string
* @param ... sprintf-style args
* @return 0 on success; a negative value on failure
*/
FBEXPORT jint throwException(JNIEnv* pEnv, const char* szClassName, const char* szFmt, va_list va_args);
/**
* Instructs the JNI environment to throw a NoClassDefFoundError.
*
* @param pEnv JNI environment
* @param szFmt sprintf-style format string
* @param ... sprintf-style args
* @return 0 on success; a negative value on failure
*/
FBEXPORT jint throwNoClassDefError(JNIEnv* pEnv, const char* szFmt, ...);
/**
* Instructs the JNI environment to throw a RuntimeException.
*
* @param pEnv JNI environment
* @param szFmt sprintf-style format string
* @param ... sprintf-style args
* @return 0 on success; a negative value on failure
*/
FBEXPORT jint throwRuntimeException(JNIEnv* pEnv, const char* szFmt, ...);
/**
* Instructs the JNI environment to throw a IllegalArgumentException.
*
* @param pEnv JNI environment
* @param szFmt sprintf-style format string
* @param ... sprintf-style args
* @return 0 on success; a negative value on failure
*/
FBEXPORT jint throwIllegalArgumentException(JNIEnv* pEnv, const char* szFmt, ...);
/**
* Instructs the JNI environment to throw a IllegalStateException.
*
* @param pEnv JNI environment
* @param szFmt sprintf-style format string
* @param ... sprintf-style args
* @return 0 on success; a negative value on failure
*/
FBEXPORT jint throwIllegalStateException(JNIEnv* pEnv, const char* szFmt, ...);
/**
* Instructs the JNI environment to throw an IOException.
*
* @param pEnv JNI environment
* @param szFmt sprintf-style format string
* @param ... sprintf-style args
* @return 0 on success; a negative value on failure
*/
FBEXPORT jint throwIOException(JNIEnv* pEnv, const char* szFmt, ...);
/**
* Instructs the JNI environment to throw an AssertionError.
*
* @param pEnv JNI environment
* @param szFmt sprintf-style format string
* @param ... sprintf-style args
* @return 0 on success; a negative value on failure
*/
FBEXPORT jint throwAssertionError(JNIEnv* pEnv, const char* szFmt, ...);
/**
* Instructs the JNI environment to throw an OutOfMemoryError.
*
* @param pEnv JNI environment
* @param szFmt sprintf-style format string
* @param ... sprintf-style args
* @return 0 on success; a negative value on failure
*/
FBEXPORT jint throwOutOfMemoryError(JNIEnv* pEnv, const char* szFmt, ...);
/**
* Finds the specified class. If it's not found, instructs the JNI environment to throw an
* exception.
*
* @param pEnv JNI environment
* @param szClassName the classname to find in JNI format (e.g. "java/lang/String")
* @return the class or NULL if not found (in which case a pending exception will be queued). This
* returns a global reference (JNIEnv::NewGlobalRef).
*/
FBEXPORT jclass findClassOrThrow(JNIEnv *pEnv, const char* szClassName);
/**
* Finds the specified field of the specified class. If it's not found, instructs the JNI
* environment to throw an exception.
*
* @param pEnv JNI environment
* @param clazz the class to lookup the field in
* @param szFieldName the name of the field to find
* @param szSig the signature of the field
* @return the field or NULL if not found (in which case a pending exception will be queued)
*/
FBEXPORT jfieldID getFieldIdOrThrow(JNIEnv* pEnv, jclass clazz, const char* szFieldName, const char* szSig);
/**
* Finds the specified method of the specified class. If it's not found, instructs the JNI
* environment to throw an exception.
*
* @param pEnv JNI environment
* @param clazz the class to lookup the method in
* @param szMethodName the name of the method to find
* @param szSig the signature of the method
* @return the method or NULL if not found (in which case a pending exception will be queued)
*/
FBEXPORT jmethodID getMethodIdOrThrow(
JNIEnv* pEnv,
jclass clazz,
const char* szMethodName,
const char* szSig);
} // namespace facebook

View File

@@ -1,67 +0,0 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include <cstdint>
#include <jni/Countable.h>
#include <fb/Environment.h>
#include <jni/Registration.h>
namespace facebook {
namespace jni {
static jfieldID gCountableNativePtr;
static RefPtr<Countable>* rawCountableFromJava(JNIEnv* env, jobject obj) {
FBASSERT(obj);
return reinterpret_cast<RefPtr<Countable>*>(env->GetLongField(obj, gCountableNativePtr));
}
const RefPtr<Countable>& countableFromJava(JNIEnv* env, jobject obj) {
FBASSERT(obj);
return *rawCountableFromJava(env, obj);
}
void setCountableForJava(JNIEnv* env, jobject obj, RefPtr<Countable>&& countable) {
int oldValue = env->GetLongField(obj, gCountableNativePtr);
FBASSERTMSGF(oldValue == 0, "Cannot reinitialize object; expected nullptr, got %x", oldValue);
FBASSERT(countable);
uintptr_t fieldValue = (uintptr_t) new RefPtr<Countable>(std::move(countable));
env->SetLongField(obj, gCountableNativePtr, fieldValue);
}
/**
* NB: THREAD SAFETY (this comment also exists at Countable.java)
*
* This method deletes the corresponding native object on whatever thread the method is called
* on. In the common case when this is called by Countable#finalize(), this will be called on the
* system finalizer thread. If you manually call dispose on the Java object, the native object
* will be deleted synchronously on that thread.
*/
void dispose(JNIEnv* env, jobject obj) {
// Grab the pointer
RefPtr<Countable>* countable = rawCountableFromJava(env, obj);
if (!countable) {
// That was easy.
return;
}
// Clear out the old value to avoid double-frees
env->SetLongField(obj, gCountableNativePtr, 0);
delete countable;
}
void CountableOnLoad(JNIEnv* env) {
jclass countable = env->FindClass("com/facebook/jni/Countable");
gCountableNativePtr = env->GetFieldID(countable, "mInstance", "J");
registerNatives(env, countable, {
{ "dispose", "()V", (void*) dispose },
});
}
} }

View File

@@ -1,130 +0,0 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include <pthread.h>
#include <fb/log.h>
#include <fb/StaticInitialized.h>
#include <fb/ThreadLocal.h>
#include <fb/Environment.h>
#include <fb/fbjni/CoreClasses.h>
#include <fb/fbjni/NativeRunnable.h>
#include <functional>
namespace facebook {
namespace jni {
namespace {
StaticInitialized<ThreadLocal<JNIEnv>> g_env;
JavaVM* g_vm = nullptr;
struct JThreadScopeSupport : JavaClass<JThreadScopeSupport> {
static auto constexpr kJavaDescriptor = "Lcom/facebook/jni/ThreadScopeSupport;";
// These reinterpret_casts are a totally dangerous pattern. Don't use them. Use HybridData instead.
static void runStdFunction(std::function<void()>&& func) {
static auto method = javaClassStatic()->getStaticMethod<void(jlong)>("runStdFunction");
method(javaClassStatic(), reinterpret_cast<jlong>(&func));
}
static void runStdFunctionImpl(alias_ref<JClass>, jlong ptr) {
(*reinterpret_cast<std::function<void()>*>(ptr))();
}
static void OnLoad() {
// We need the javaClassStatic so that the class lookup is cached and that
// runStdFunction can be called from a ThreadScope-attached thread.
javaClassStatic()->registerNatives({
makeNativeMethod("runStdFunctionImpl", runStdFunctionImpl),
});
}
};
}
/* static */
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");
// TODO(cjhopman): This should throw an exception.
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();
}
}
/* static */
void ThreadScope::OnLoad() {
// These classes are required for ScopeWithClassLoader. Ensure they are looked up when loading.
JThreadScopeSupport::OnLoad();
}
/* static */
void ThreadScope::WithClassLoader(std::function<void()>&& runnable) {
// TODO(cjhopman): If the classloader is already available in this scope, we
// shouldn't have to jump through java.
ThreadScope ts;
JThreadScopeSupport::runStdFunction(std::move(runnable));
}
} }

View File

@@ -1,283 +0,0 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include <fb/fbjni/CoreClasses.h>
#include <fb/assert.h>
#include <fb/log.h>
#include <alloca.h>
#include <cstdlib>
#include <ios>
#include <stdexcept>
#include <stdio.h>
#include <string>
#include <system_error>
#include <jni.h>
namespace facebook {
namespace jni {
namespace {
class JRuntimeException : public JavaClass<JRuntimeException, JThrowable> {
public:
static auto constexpr kJavaDescriptor = "Ljava/lang/RuntimeException;";
static local_ref<JRuntimeException> create(const char* str) {
return newInstance(make_jstring(str));
}
static local_ref<JRuntimeException> create() {
return newInstance();
}
};
class JIOException : public JavaClass<JIOException, JThrowable> {
public:
static auto constexpr kJavaDescriptor = "Ljava/io/IOException;";
static local_ref<JIOException> create(const char* str) {
return newInstance(make_jstring(str));
}
};
class JOutOfMemoryError : public JavaClass<JOutOfMemoryError, JThrowable> {
public:
static auto constexpr kJavaDescriptor = "Ljava/lang/OutOfMemoryError;";
static local_ref<JOutOfMemoryError> create(const char* str) {
return newInstance(make_jstring(str));
}
};
class JArrayIndexOutOfBoundsException : public JavaClass<JArrayIndexOutOfBoundsException, JThrowable> {
public:
static auto constexpr kJavaDescriptor = "Ljava/lang/ArrayIndexOutOfBoundsException;";
static local_ref<JArrayIndexOutOfBoundsException> create(const char* str) {
return newInstance(make_jstring(str));
}
};
class JUnknownCppException : public JavaClass<JUnknownCppException, JThrowable> {
public:
static auto constexpr kJavaDescriptor = "Lcom/facebook/jni/UnknownCppException;";
static local_ref<JUnknownCppException> create() {
return newInstance();
}
static local_ref<JUnknownCppException> create(const char* str) {
return newInstance(make_jstring(str));
}
};
class JCppSystemErrorException : public JavaClass<JCppSystemErrorException, JThrowable> {
public:
static auto constexpr kJavaDescriptor = "Lcom/facebook/jni/CppSystemErrorException;";
static local_ref<JCppSystemErrorException> create(const std::system_error& e) {
return newInstance(make_jstring(e.what()), e.code().value());
}
};
// Exception throwing & translating functions //////////////////////////////////////////////////////
// Functions that throw Java exceptions
void setJavaExceptionAndAbortOnFailure(alias_ref<JThrowable> throwable) {
auto env = Environment::current();
if (throwable) {
env->Throw(throwable.get());
}
if (env->ExceptionCheck() != JNI_TRUE) {
std::abort();
}
}
}
// Functions that throw C++ exceptions
// TODO(T6618159) Take a stack dump here to save context if it results in a crash when propagated
void throwPendingJniExceptionAsCppException() {
JNIEnv* env = Environment::current();
if (env->ExceptionCheck() == JNI_FALSE) {
return;
}
auto throwable = adopt_local(env->ExceptionOccurred());
if (!throwable) {
throw std::runtime_error("Unable to get pending JNI exception.");
}
env->ExceptionClear();
throw JniException(throwable);
}
void throwCppExceptionIf(bool condition) {
if (!condition) {
return;
}
auto env = Environment::current();
if (env->ExceptionCheck() == JNI_TRUE) {
throwPendingJniExceptionAsCppException();
return;
}
throw JniException();
}
void throwNewJavaException(jthrowable throwable) {
throw JniException(wrap_alias(throwable));
}
void throwNewJavaException(const char* throwableName, const char* msg) {
// If anything of the fbjni calls fail, an exception of a suitable
// form will be thrown, which is what we want.
auto throwableClass = findClassLocal(throwableName);
auto throwable = throwableClass->newObject(
throwableClass->getConstructor<jthrowable(jstring)>(),
make_jstring(msg).release());
throwNewJavaException(throwable.get());
}
// Translate C++ to Java Exception
namespace {
// The implementation std::rethrow_if_nested uses a dynamic_cast to determine
// if the exception is a nested_exception. If the exception is from a library
// built with -fno-rtti, then that will crash. This avoids that.
void rethrow_if_nested() {
try {
throw;
} catch (const std::nested_exception& e) {
e.rethrow_nested();
} catch (...) {
}
}
// For each exception in the chain of the currently handled exception, func
// will be called with that exception as the currently handled exception (in
// reverse order, i.e. innermost first).
void denest(std::function<void()> func) {
try {
throw;
} catch (const std::exception& e) {
try {
rethrow_if_nested();
} catch (...) {
denest(func);
}
func();
} catch (...) {
func();
}
}
}
void translatePendingCppExceptionToJavaException() noexcept {
local_ref<JThrowable> previous;
auto func = [&previous] () {
local_ref<JThrowable> current;
try {
throw;
} catch(const JniException& ex) {
current = ex.getThrowable();
} catch(const std::ios_base::failure& ex) {
current = JIOException::create(ex.what());
} catch(const std::bad_alloc& ex) {
current = JOutOfMemoryError::create(ex.what());
} catch(const std::out_of_range& ex) {
current = JArrayIndexOutOfBoundsException::create(ex.what());
} catch(const std::system_error& ex) {
current = JCppSystemErrorException::create(ex);
} catch(const std::runtime_error& ex) {
current = JRuntimeException::create(ex.what());
} catch(const std::exception& ex) {
current = JCppException::create(ex.what());
} catch(const char* msg) {
current = JUnknownCppException::create(msg);
} catch(...) {
current = JUnknownCppException::create();
}
if (previous) {
current->initCause(previous);
}
previous = current;
};
try {
denest(func);
setJavaExceptionAndAbortOnFailure(previous);
} catch (std::exception& e) {
FBLOGE("unexpected exception in translatePendingCppExceptionToJavaException: %s", e.what());
// rethrow the exception and let the noexcept handling abort.
throw;
} catch (...) {
FBLOGE("unexpected exception in translatePendingCppExceptionToJavaException");
throw;
}
}
// JniException ////////////////////////////////////////////////////////////////////////////////////
const std::string JniException::kExceptionMessageFailure_ = "Unable to get exception message.";
JniException::JniException() : JniException(JRuntimeException::create()) { }
JniException::JniException(alias_ref<jthrowable> throwable) : isMessageExtracted_(false) {
throwable_ = make_global(throwable);
}
JniException::JniException(JniException &&rhs)
: throwable_(std::move(rhs.throwable_)),
what_(std::move(rhs.what_)),
isMessageExtracted_(rhs.isMessageExtracted_) {
}
JniException::JniException(const JniException &rhs)
: what_(rhs.what_), isMessageExtracted_(rhs.isMessageExtracted_) {
throwable_ = make_global(rhs.throwable_);
}
JniException::~JniException() {
ThreadScope ts;
throwable_.reset();
}
local_ref<JThrowable> JniException::getThrowable() const noexcept {
return make_local(throwable_);
}
// TODO 6900503: consider making this thread-safe.
void JniException::populateWhat() const noexcept {
ThreadScope ts;
try {
what_ = throwable_->toString();
isMessageExtracted_ = true;
} catch(...) {
what_ = kExceptionMessageFailure_;
}
}
const char* JniException::what() const noexcept {
if (!isMessageExtracted_) {
populateWhat();
}
return what_.c_str();
}
void JniException::setJavaException() const noexcept {
setJavaExceptionAndAbortOnFailure(throwable_);
}
}}

View File

@@ -1,63 +0,0 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "fb/fbjni.h"
namespace facebook {
namespace jni {
namespace detail {
void HybridData::setNativePointer(std::unique_ptr<BaseHybridClass> new_value) {
static auto pointerField = getClass()->getField<jlong>("mNativePointer");
auto* old_value = reinterpret_cast<BaseHybridClass*>(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
// resetNative has not already been called.
setFieldValue(pointerField, reinterpret_cast<jlong>(new_value.release()));
}
BaseHybridClass* HybridData::getNativePointer() {
static auto pointerField = getClass()->getField<jlong>("mNativePointer");
auto* value = reinterpret_cast<BaseHybridClass*>(getFieldValue(pointerField));
if (!value) {
throwNewJavaException("java/lang/NullPointerException", "java.lang.NullPointerException");
}
return value;
}
local_ref<HybridData> HybridData::create() {
return newInstance();
}
}
namespace {
void resetNative(alias_ref<detail::HybridData> jthis) {
jthis->setNativePointer(nullptr);
}
}
void HybridDataOnLoad() {
registerNatives("com/facebook/jni/HybridData", {
makeNativeMethod("resetNative", resetNative),
});
}
}}

View File

@@ -1,23 +0,0 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "fb/fbjni.h"
#include <sstream>
std::string JThrowable::getStackTrace() const {
static auto getStackTraceMethod = javaClassStatic()
->getMethod<facebook::jni::local_ref<facebook::jni::JThrowable::JStackTrace>()>("getStackTrace");
std::ostringstream os;
auto stackTrace = getStackTraceMethod(self());
for (size_t i = 0; i < stackTrace->size(); ++i) {
os << facebook::jni::adopt_local((*stackTrace)[i])->toString() << ' ';
}
return os.str();
}

View File

@@ -1,20 +0,0 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include <jni/Countable.h>
#include <fb/Environment.h>
#include <fb/fbjni.h>
#include <fb/fbjni/NativeRunnable.h>
using namespace facebook::jni;
void initialize_fbjni() {
CountableOnLoad(Environment::current());
HybridDataOnLoad();
JNativeRunnable::OnLoad();
ThreadScope::OnLoad();
}

View File

@@ -1,39 +0,0 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include <fb/fbjni/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

@@ -1,41 +0,0 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include <fb/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);
}
} }

Some files were not shown because too many files have changed in this diff Show More