diff --git a/build.gradle b/build.gradle
index 6a444bec6..f18058433 100644
--- a/build.gradle
+++ b/build.gradle
@@ -27,6 +27,8 @@ subprojects {
repositories {
jcenter()
google()
+ mavenLocal()
+ mavenCentral()
}
}
diff --git a/libs/fbjni/ApplicationManifest.xml b/libs/fbjni/ApplicationManifest.xml
new file mode 100644
index 000000000..d09d5f057
--- /dev/null
+++ b/libs/fbjni/ApplicationManifest.xml
@@ -0,0 +1,4 @@
+
+
+
diff --git a/libs/fbjni/BUCK b/libs/fbjni/BUCK
index d44100c42..43eee3b67 100644
--- a/libs/fbjni/BUCK
+++ b/libs/fbjni/BUCK
@@ -1,9 +1,81 @@
-# BUILD FILE SYNTAX: SKYLARK
-# Copyright (c) 2017-present, Facebook, Inc.
-#
-# 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("//build_defs:fb_xplat_cxx_library.bzl", "fb_xplat_cxx_library")
+load("@xplat//build_defs:fb_java_library.bzl", "fb_java_library")
-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",
+ ],
+)
diff --git a/libs/fbjni/CMakeLists.txt b/libs/fbjni/CMakeLists.txt
new file mode 100644
index 000000000..ea30a3aee
--- /dev/null
+++ b/libs/fbjni/CMakeLists.txt
@@ -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)
diff --git a/libs/fbjni/build.gradle b/libs/fbjni/build.gradle
index 812e34e27..1c80c38fe 100644
--- a/libs/fbjni/build.gradle
+++ b/libs/fbjni/build.gradle
@@ -7,9 +7,16 @@ android {
defaultConfig {
minSdkVersion rootProject.minSdkVersion
targetSdkVersion rootProject.targetSdkVersion
-
+ sourceSets {
+ main {
+ manifest.srcFile './ApplicationManifest.xml'
+ java {
+ srcDir 'java'
+ }
+ }
+ }
ndk {
- abiFilters 'arm64-v8a', 'x86'
+ abiFilters 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a'
}
externalNativeBuild {
@@ -20,7 +27,7 @@ android {
}
externalNativeBuild {
cmake {
- path './src/main/cpp/CMakeLists.txt'
+ path './CMakeLists.txt'
}
}
}
@@ -30,4 +37,5 @@ dependencies {
compileOnly deps.jsr305
compileOnly deps.inferAnnotations
compileOnly 'com.facebook.litho:litho-annotations:0.15.0'
+ compileOnly 'com.facebook.soloader:soloader:0.5.0'
}
diff --git a/libs/fbjni/src/main/cpp/jni/ByteBuffer.cpp b/libs/fbjni/cxx/fbjni/ByteBuffer.cpp
similarity index 72%
rename from libs/fbjni/src/main/cpp/jni/ByteBuffer.cpp
rename to libs/fbjni/cxx/fbjni/ByteBuffer.cpp
index 5a5317f9f..82060d74d 100644
--- a/libs/fbjni/src/main/cpp/jni/ByteBuffer.cpp
+++ b/libs/fbjni/cxx/fbjni/ByteBuffer.cpp
@@ -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
- * LICENSE file in the root directory of this source tree.
+ * 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
+#include
#include
-#include
-
namespace facebook {
namespace jni {
@@ -22,6 +29,11 @@ local_ref createEmpty() {
}
}
+void JBuffer::rewind() const {
+ static auto meth = javaClassStatic()->getMethod()>("rewind");
+ meth(self());
+}
+
local_ref JByteBuffer::wrapBytes(uint8_t* data, size_t size) {
// env->NewDirectByteBuffer requires that size is positive. Android's
// dalvik returns an invalid result and Android's art aborts if size == 0.
diff --git a/libs/fbjni/cxx/fbjni/ByteBuffer.h b/libs/fbjni/cxx/fbjni/ByteBuffer.h
new file mode 100644
index 000000000..91beff2f5
--- /dev/null
+++ b/libs/fbjni/cxx/fbjni/ByteBuffer.h
@@ -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
+
+namespace facebook {
+namespace jni {
+
+class JBuffer : public JavaClass {
+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 {
+ public:
+ static constexpr const char* kJavaDescriptor = "Ljava/nio/ByteBuffer;";
+
+ static local_ref wrapBytes(uint8_t* data, size_t size);
+
+ bool isDirect() const;
+
+ uint8_t* getDirectBytes() const;
+ size_t getDirectSize() const;
+};
+
+}}
diff --git a/libs/fbjni/cxx/fbjni/Context.h b/libs/fbjni/cxx/fbjni/Context.h
new file mode 100644
index 000000000..ca75972fd
--- /dev/null
+++ b/libs/fbjni/cxx/fbjni/Context.h
@@ -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
+#include
+
+namespace facebook {
+namespace jni {
+
+class AContext : public JavaClass {
+ public:
+ static constexpr const char* kJavaDescriptor = "Landroid/content/Context;";
+
+ // Define a method that calls into the represented Java class
+ local_ref getCacheDir() {
+ static const auto method = getClass()->getMethod("getCacheDir");
+ return method(self());
+ }
+
+ local_ref getFilesDir() {
+ static const auto method = getClass()->getMethod("getFilesDir");
+ return method(self());
+ }
+};
+
+}
+}
diff --git a/libs/fbjni/cxx/fbjni/File.h b/libs/fbjni/cxx/fbjni/File.h
new file mode 100644
index 000000000..f9966c6ae
--- /dev/null
+++ b/libs/fbjni/cxx/fbjni/File.h
@@ -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
+
+namespace facebook {
+namespace jni {
+
+class JFile : public JavaClass {
+ 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("getAbsolutePath");
+ return method(self())->toStdString();
+ }
+
+};
+
+}
+}
diff --git a/libs/fbjni/cxx/fbjni/JThread.h b/libs/fbjni/cxx/fbjni/JThread.h
new file mode 100644
index 000000000..6bdb269f2
--- /dev/null
+++ b/libs/fbjni/cxx/fbjni/JThread.h
@@ -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
+#include
+
+namespace facebook {
+namespace jni {
+
+class JThread : public JavaClass {
+ public:
+ static constexpr const char* kJavaDescriptor = "Ljava/lang/Thread;";
+
+ void start() {
+ static const auto method = javaClassStatic()->getMethod("start");
+ method(self());
+ }
+
+ void join() {
+ static const auto method = javaClassStatic()->getMethod("join");
+ method(self());
+ }
+
+ static local_ref create(std::function&& runnable) {
+ auto jrunnable = JNativeRunnable::newObjectCxxArgs(std::move(runnable));
+ return newInstance(static_ref_cast(jrunnable));
+ }
+
+ static local_ref create(std::function&& runnable, std::string&& name) {
+ auto jrunnable = JNativeRunnable::newObjectCxxArgs(std::move(runnable));
+ return newInstance(static_ref_cast(jrunnable), make_jstring(std::move(name)));
+ }
+
+ static local_ref getCurrent() {
+ static const auto method = javaClassStatic()->getStaticMethod()>("currentThread");
+ return method(javaClassStatic());
+ }
+
+ int getPriority() {
+ static const auto method = getClass()->getMethod("getPriority");
+ return method(self());
+ }
+
+ void setPriority(int priority) {
+ static const auto method = getClass()->getMethod("setPriority");
+ method(self(), priority);
+ }
+};
+
+}
+}
diff --git a/libs/fbjni/src/main/cpp/include/fb/fbjni/NativeRunnable.h b/libs/fbjni/cxx/fbjni/NativeRunnable.h
similarity index 52%
rename from libs/fbjni/src/main/cpp/include/fb/fbjni/NativeRunnable.h
rename to libs/fbjni/cxx/fbjni/NativeRunnable.h
index 0502e36b5..d2f911c95 100644
--- a/libs/fbjni/src/main/cpp/include/fb/fbjni/NativeRunnable.h
+++ b/libs/fbjni/cxx/fbjni/NativeRunnable.h
@@ -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
- * LICENSE file in the root directory of this source tree.
+ * 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"
-#include "Hybrid.h"
-#include "Registration.h"
+#include
#include
diff --git a/libs/fbjni/cxx/fbjni/OnLoad.cpp b/libs/fbjni/cxx/fbjni/OnLoad.cpp
new file mode 100644
index 000000000..2282131ef
--- /dev/null
+++ b/libs/fbjni/cxx/fbjni/OnLoad.cpp
@@ -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
+#include
+
+using namespace facebook::jni;
+
+JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
+ return facebook::jni::initialize(vm, [] {
+ HybridDataOnLoad();
+ JNativeRunnable::OnLoad();
+ ThreadScope::OnLoad();
+ });
+}
diff --git a/libs/fbjni/cxx/fbjni/ReadableByteChannel.cpp b/libs/fbjni/cxx/fbjni/ReadableByteChannel.cpp
new file mode 100644
index 000000000..9970d2842
--- /dev/null
+++ b/libs/fbjni/cxx/fbjni/ReadableByteChannel.cpp
@@ -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
+
+namespace facebook {
+namespace jni {
+
+int JReadableByteChannel::read(alias_ref dest) const {
+ if (!self()) {
+ throwNewJavaException("java/lang/NullPointerException", "java.lang.NullPointerException");
+ }
+ static auto method = javaClassStatic()->getMethod)>("read");
+ return method(self(), dest);
+}
+
+}}
+
diff --git a/libs/fbjni/cxx/fbjni/ReadableByteChannel.h b/libs/fbjni/cxx/fbjni/ReadableByteChannel.h
new file mode 100644
index 000000000..cded4a603
--- /dev/null
+++ b/libs/fbjni/cxx/fbjni/ReadableByteChannel.h
@@ -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
+#include
+
+namespace facebook {
+namespace jni {
+
+class JReadableByteChannel : public JavaClass {
+public:
+ static constexpr const char* kJavaDescriptor = "Ljava/nio/channels/ReadableByteChannel;";
+
+ int read(alias_ref dest) const;
+};
+
+}}
diff --git a/libs/fbjni/src/main/cpp/include/fb/fbjni/Boxed.h b/libs/fbjni/cxx/fbjni/detail/Boxed.h
similarity index 67%
rename from libs/fbjni/src/main/cpp/include/fb/fbjni/Boxed.h
rename to libs/fbjni/cxx/fbjni/detail/Boxed.h
index b47996163..3628639bd 100644
--- a/libs/fbjni/src/main/cpp/include/fb/fbjni/Boxed.h
+++ b/libs/fbjni/cxx/fbjni/detail/Boxed.h
@@ -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
- * LICENSE file in the root directory of this source tree.
+ * 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
@@ -18,13 +27,13 @@ struct JPrimitive : JavaClass {
using typename JavaClass::javaobject;
using JavaClass::javaClassStatic;
static local_ref valueOf(jprim val) {
- static auto cls = javaClassStatic();
- static auto method =
+ static const auto cls = javaClassStatic();
+ static const auto method =
cls->template getStaticMethod("valueOf");
return method(cls, val);
}
jprim value() const {
- static auto method =
+ static const auto method =
javaClassStatic()->template getMethod(T::kValueMethod);
return method(this->self());
}
@@ -56,9 +65,12 @@ DEFINE_BOXED_PRIMITIVE(double, Double)
#undef DEFINE_BOXED_PRIMITIVE
+struct JVoid : public jni::JavaClass {
+ static auto constexpr kJavaDescriptor = "Ljava/lang/Void;";
+};
+
inline local_ref autobox(alias_ref val) {
return make_local(val);
}
}}
-
diff --git a/libs/fbjni/src/main/cpp/include/fb/fbjni/Common.h b/libs/fbjni/cxx/fbjni/detail/Common.h
similarity index 70%
rename from libs/fbjni/src/main/cpp/include/fb/fbjni/Common.h
rename to libs/fbjni/cxx/fbjni/detail/Common.h
index a7775db6d..b207cee84 100644
--- a/libs/fbjni/src/main/cpp/include/fb/fbjni/Common.h
+++ b/libs/fbjni/cxx/fbjni/detail/Common.h
@@ -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
- * LICENSE file in the root directory of this source tree.
+ * 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 Common.h
@@ -16,9 +25,6 @@
#include
-#include
-#include
-
#ifdef FBJNI_DEBUG_REFS
# ifdef __ANDROID__
# include
@@ -43,11 +49,11 @@
namespace facebook {
namespace jni {
-FBEXPORT void throwPendingJniExceptionAsCppException();
-FBEXPORT void throwCppExceptionIf(bool condition);
+void throwPendingJniExceptionAsCppException();
+void throwCppExceptionIf(bool condition);
-[[noreturn]] FBEXPORT void throwNewJavaException(jthrowable);
-[[noreturn]] FBEXPORT void throwNewJavaException(const char* throwableName, const char* msg);
+[[noreturn]] void throwNewJavaException(jthrowable);
+[[noreturn]] void throwNewJavaException(const char* throwableName, const char* msg);
template
[[noreturn]] void throwNewJavaException(const char* throwableName, const char* fmt, Args... args);
@@ -66,20 +72,10 @@ template
* unhelpful way (typically a segfault) while trying to handle an exception
* which occurs later.
*/
-FBEXPORT jint initialize(JavaVM*, std::function&&) noexcept;
+jint initialize(JavaVM*, std::function&&) noexcept;
namespace internal {
-/**
- * Retrieve a pointer the JNI environment of the current thread.
- *
- * @pre The current thread must be attached to the VM
- */
-inline JNIEnv* getEnv() noexcept {
- // TODO(T6594868) Benchmark against raw JNI access
- return Environment::current();
-}
-
// Define to get extremely verbose logging of references and to enable reference stats
#ifdef FBJNI_DEBUG_REFS
template
diff --git a/libs/fbjni/src/main/cpp/include/fb/fbjni/CoreClasses-inl.h b/libs/fbjni/cxx/fbjni/detail/CoreClasses-inl.h
similarity index 85%
rename from libs/fbjni/src/main/cpp/include/fb/fbjni/CoreClasses-inl.h
rename to libs/fbjni/cxx/fbjni/detail/CoreClasses-inl.h
index 5258a240b..6d6db77e6 100644
--- a/libs/fbjni/src/main/cpp/include/fb/fbjni/CoreClasses-inl.h
+++ b/libs/fbjni/cxx/fbjni/detail/CoreClasses-inl.h
@@ -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
- * LICENSE file in the root directory of this source tree.
+ * 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
@@ -22,15 +31,15 @@ namespace jni {
// jobject /////////////////////////////////////////////////////////////////////////////////////////
inline bool isSameObject(alias_ref lhs, alias_ref 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 JObject::getClass() const noexcept {
- return adopt_local(internal::getEnv()->GetObjectClass(self()));
+ return adopt_local(Environment::current()->GetObjectClass(self()));
}
inline bool JObject::isInstanceOf(alias_ref cls) const noexcept {
- return internal::getEnv()->IsInstanceOf(self(), cls.get()) != JNI_FALSE;
+ return Environment::current()->IsInstanceOf(self(), cls.get()) != JNI_FALSE;
}
template
@@ -49,7 +58,7 @@ inline void JObject::setFieldValue(JField field, T value) noexcept {
}
inline std::string JObject::toString() const {
- static auto method = findClassLocal("java/lang/Object")->getMethod("toString");
+ static const auto method = findClassLocal("java/lang/Object")->getMethod("toString");
return method(self())->toStdString();
}
@@ -78,13 +87,13 @@ MonitorLock::MonitorLock() noexcept : owned_(nullptr) {}
MonitorLock::MonitorLock(alias_ref object) noexcept
: owned_(object) {
- internal::getEnv()->MonitorEnter(object.get());
+ Environment::current()->MonitorEnter(object.get());
}
void MonitorLock::reset() noexcept {
if (owned_) {
- internal::getEnv()->MonitorExit(owned_.get());
- if (internal::getEnv()->ExceptionCheck()) {
+ Environment::current()->MonitorExit(owned_.get());
+ if (Environment::current()->ExceptionCheck()) {
abort(); // Lock mismatch
}
owned_ = nullptr;
@@ -127,7 +136,7 @@ namespace detail {
template
static local_ref newInstance(Args... args) {
static auto cls = JC::javaClassStatic();
- static auto constructor = cls->template getConstructor();
+ static const auto constructor = cls->template getConstructor();
return cls->newObject(constructor, args...);
}
}
@@ -155,17 +164,18 @@ struct NativeMethod {
};
inline local_ref 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 methods) {
- const auto env = internal::getEnv();
+ const auto env = Environment::current();
JNINativeMethod jnimethods[methods.size()];
size_t i = 0;
for (auto it = methods.begin(); it < methods.end(); ++it, ++i) {
- jnimethods[i].name = it->name;
- jnimethods[i].signature = it->descriptor.c_str();
+ // The JNI struct members are unnecessarily non-const.
+ jnimethods[i].name = const_cast(it->name);
+ jnimethods[i].signature = const_cast(it->descriptor.c_str());
jnimethods[i].fnPtr = reinterpret_cast(it->wrapper);
}
@@ -174,8 +184,13 @@ inline void JClass::registerNatives(std::initializer_list methods)
}
inline bool JClass::isAssignableFrom(alias_ref other) const noexcept {
- const auto env = internal::getEnv();
- const auto result = env->IsAssignableFrom(self(), other.get());
+ const auto env = Environment::current();
+ // 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;
}
@@ -199,7 +214,7 @@ template
inline JMethod JClass::getMethod(
const char* name,
const char* descriptor) const {
- const auto env = internal::getEnv();
+ const auto env = Environment::current();
const auto method = env->GetMethodID(self(), name, descriptor);
FACEBOOK_JNI_THROW_EXCEPTION_IF(!method);
return JMethod{method};
@@ -214,7 +229,7 @@ template
inline JStaticMethod JClass::getStaticMethod(
const char* name,
const char* descriptor) const {
- const auto env = internal::getEnv();
+ const auto env = Environment::current();
const auto method = env->GetStaticMethodID(self(), name, descriptor);
FACEBOOK_JNI_THROW_EXCEPTION_IF(!method);
return JStaticMethod{method};
@@ -229,7 +244,7 @@ template
inline JNonvirtualMethod JClass::getNonvirtualMethod(
const char* name,
const char* descriptor) const {
- const auto env = internal::getEnv();
+ const auto env = Environment::current();
const auto method = env->GetMethodID(self(), name, descriptor);
FACEBOOK_JNI_THROW_EXCEPTION_IF(!method);
return JNonvirtualMethod{method};
@@ -245,7 +260,7 @@ template
inline JField(), T>> JClass::getField(
const char* name,
const char* descriptor) const {
- const auto env = internal::getEnv();
+ const auto env = Environment::current();
auto field = env->GetFieldID(self(), name, descriptor);
FACEBOOK_JNI_THROW_EXCEPTION_IF(!field);
return JField{field};
@@ -261,7 +276,7 @@ template
inline JStaticField(), T>> JClass::getStaticField(
const char* name,
const char* descriptor) const {
- const auto env = internal::getEnv();
+ const auto env = Environment::current();
auto field = env->GetStaticFieldID(self(), name, descriptor);
FACEBOOK_JNI_THROW_EXCEPTION_IF(!field);
return JStaticField{field};
@@ -286,7 +301,7 @@ template
inline local_ref JClass::newObject(
JConstructor constructor,
Args... args) const {
- const auto env = internal::getEnv();
+ const auto env = Environment::current();
auto object = env->NewObject(self(), constructor.getId(),
detail::callToJni(
detail::Convert::type>::toCall(args))...);
@@ -339,46 +354,11 @@ struct Convert {
};
}
-// JStackTrace //////////////////////////////////////////////////////////////////////////////////////
-
-inline auto JStackTraceElement::create(
- const std::string& declaringClass, const std::string& methodName, const std::string& file, int line)
- -> local_ref {
- return newInstance(declaringClass, methodName, file, line);
-}
-
-inline std::string JStackTraceElement::getClassName() const {
- static auto meth = javaClassStatic()->getMethod()>("getClassName");
- return meth(self())->toStdString();
-}
-
-inline std::string JStackTraceElement::getMethodName() const {
- static auto meth = javaClassStatic()->getMethod()>("getMethodName");
- return meth(self())->toStdString();
-}
-
-inline std::string JStackTraceElement::getFileName() const {
- static auto meth = javaClassStatic()->getMethod()>("getFileName");
- return meth(self())->toStdString();
-}
-
-inline int JStackTraceElement::getLineNumber() const {
- static auto meth = javaClassStatic()->getMethod("getLineNumber");
- return meth(self());
-}
-
-// jthrowable //////////////////////////////////////////////////////////////////////////////////////
-
-inline local_ref JThrowable::initCause(alias_ref cause) {
- static auto meth = javaClassStatic()->getMethod("initCause");
- return meth(self(), cause.get());
-}
-
// jtypeArray //////////////////////////////////////////////////////////////////////////////////////
namespace detail {
inline size_t JArray::size() const noexcept {
- const auto env = internal::getEnv();
+ const auto env = Environment::current();
return env->GetArrayLength(self());
}
}
@@ -438,8 +418,8 @@ std::string JArrayClass::get_instantiated_base_name() {
template
auto JArrayClass::newArray(size_t size) -> local_ref {
- static auto elementClass = findClassStatic(jtype_traits::base_name().c_str());
- const auto env = internal::getEnv();
+ static const auto elementClass = findClassStatic(jtype_traits::base_name().c_str());
+ const auto env = Environment::current();
auto rawArray = env->NewObjectArray(size, elementClass.get(), nullptr);
FACEBOOK_JNI_THROW_EXCEPTION_IF(!rawArray);
return adopt_local(static_cast(rawArray));
@@ -447,13 +427,13 @@ auto JArrayClass::newArray(size_t size) -> local_ref {
template
inline void JArrayClass::setElement(size_t idx, const T& value) {
- const auto env = internal::getEnv();
+ const auto env = Environment::current();
env->SetObjectArrayElement(this->self(), idx, value);
}
template
inline local_ref JArrayClass::getElement(size_t idx) {
- const auto env = internal::getEnv();
+ const auto env = Environment::current();
auto rawElement = env->GetObjectArrayElement(this->self(), idx);
return adopt_local(static_cast(rawElement));
}
@@ -463,6 +443,11 @@ inline detail::ElementProxy> JArrayClass::operator[](size_t in
return detail::ElementProxy>(this, index);
}
+template
+local_ref::javaobject> adopt_local_array(jobjectArray ref) {
+ return adopt_local(static_cast::javaobject>(ref));
+}
+
// jarray /////////////////////////////////////////////////////////////////////////////////////////
template
@@ -537,7 +522,7 @@ class PinnedCriticalAlloc {
T** elements,
size_t* size,
jboolean* isCopy) {
- const auto env = internal::getEnv();
+ const auto env = Environment::current();
*elements = static_cast(env->GetPrimitiveArrayCritical(array.get(), isCopy));
FACEBOOK_JNI_THROW_EXCEPTION_IF(!elements);
*size = array->size();
@@ -548,7 +533,7 @@ class PinnedCriticalAlloc {
jint start,
jint size,
jint mode) {
- const auto env = internal::getEnv();
+ const auto env = Environment::current();
env->ReleasePrimitiveArrayCritical(array.get(), elements, mode);
}
};
diff --git a/libs/fbjni/src/main/cpp/include/fb/fbjni/CoreClasses.h b/libs/fbjni/cxx/fbjni/detail/CoreClasses.h
similarity index 90%
rename from libs/fbjni/src/main/cpp/include/fb/fbjni/CoreClasses.h
rename to libs/fbjni/cxx/fbjni/detail/CoreClasses.h
index baa2768e9..47c551a51 100644
--- a/libs/fbjni/src/main/cpp/include/fb/fbjni/CoreClasses.h
+++ b/libs/fbjni/cxx/fbjni/detail/CoreClasses.h
@@ -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
- * LICENSE file in the root directory of this source tree.
+ * 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
@@ -16,14 +25,11 @@
#include "References-forward.h"
#include "Meta-forward.h"
#include "TypeTraits.h"
-#include
#include
#include
-#include
-
namespace facebook {
namespace jni {
@@ -39,7 +45,7 @@ class JObject;
/// in a "static auto" variable, or a static global.
///
/// @return Returns a leaked global reference to the class
-FBEXPORT alias_ref findClassStatic(const char* name);
+alias_ref findClassStatic(const char* name);
/// Lookup a class by name. Note this functions returns a local reference,
/// which means that it must not be stored in a static variable.
@@ -48,12 +54,12 @@ FBEXPORT alias_ref findClassStatic(const char* name);
/// (like caching method ids).
///
/// @return Returns a global reference to the class
-FBEXPORT local_ref findClassLocal(const char* name);
+local_ref findClassLocal(const char* name);
/// Check to see if two references refer to the same object. Comparison with nullptr
/// returns true if and only if compared to another nullptr. A weak reference that
/// refers to a reclaimed object count as nullptr.
-FBEXPORT bool isSameObject(alias_ref lhs, alias_ref rhs) noexcept;
+bool isSameObject(alias_ref lhs, alias_ref rhs) noexcept;
// Together, these classes allow convenient use of any class with the fbjni
// helpers. To use:
@@ -72,7 +78,7 @@ FBEXPORT bool isSameObject(alias_ref lhs, alias_ref rhs) noexc
// constexpr static auto kJavaDescriptor = "Lcom/example/package/MyClass;";
//
// void foo() {
-// static auto method = javaClassStatic()->getMethod("foo");
+// static const auto method = javaClassStatic()->getMethod("foo");
// method(self());
// }
//
@@ -96,7 +102,7 @@ static local_ref newInstance(Args... args);
class MonitorLock;
-class FBEXPORT JObject : detail::JObjectBase {
+class JObject : detail::JObjectBase {
public:
static constexpr auto kJavaDescriptor = "Ljava/lang/Object;";
@@ -192,7 +198,7 @@ struct JTypeFor {
// jthrowable) to be used as javaobject. This should only be necessary for
// built-in jni types and not user-defined ones.
template
-class FBEXPORT JavaClass : public Base {
+class JavaClass : public Base {
using JObjType = typename detail::JTypeFor;
public:
using _javaobject = typename JObjType::_javaobject;
@@ -220,7 +226,7 @@ protected:
/// Wrapper to provide functionality to jclass references
struct NativeMethod;
-class FBEXPORT JClass : public JavaClass {
+class JClass : public JavaClass {
public:
/// Java type descriptor
static constexpr const char* kJavaDescriptor = "Ljava/lang/Class;";
@@ -325,19 +331,23 @@ private:
void registerNatives(const char* name, std::initializer_list methods);
/// Wrapper to provide functionality to jstring references
-class FBEXPORT JString : public JavaClass {
+class JString : public JavaClass {
public:
/// Java type descriptor
static constexpr const char* kJavaDescriptor = "Ljava/lang/String;";
/// Convenience method to convert a jstring object to a std::string
std::string toStdString() const;
+
+ /// Convenience 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
-/// jstring
-FBEXPORT local_ref make_jstring(const char* modifiedUtf8);
-FBEXPORT local_ref make_jstring(const std::string& modifiedUtf8);
+/// Convenience functions to convert a const char*, std::string, or std::u16string
+/// into a @ref local_ref to a jstring.
+local_ref make_jstring(const char* modifiedUtf8);
+local_ref make_jstring(const std::string& modifiedUtf8);
+local_ref make_jstring(const std::u16string& utf16);
namespace detail {
template
@@ -365,7 +375,7 @@ class ElementProxy {
}
namespace detail {
-class FBEXPORT JArray : public JavaClass {
+class JArray : public JavaClass {
public:
// This cannot be used in a scope that derives a descriptor (like in a method
// signature). Use a more derived type instead (like JArrayInt or
@@ -377,7 +387,7 @@ class FBEXPORT JArray : public JavaClass {
// This is used so that the JArrayClass javaobject extends jni's
// jobjectArray. This class should not be used directly. A general Object[]
// should use JArrayClass.
-class FBEXPORT JTypeArray : public JavaClass {
+class JTypeArray : public JavaClass {
// This cannot be used in a scope that derives a descriptor (like in a method
// signature).
static constexpr const char* kJavaDescriptor = nullptr;
@@ -427,36 +437,13 @@ template
using jtypeArray = typename JArrayClass::javaobject;
template
-local_ref::javaobject> adopt_local_array(jobjectArray ref) {
- return adopt_local(static_cast::javaobject>(ref));
-}
+local_ref::javaobject> adopt_local_array(jobjectArray ref);
template
local_ref adopt_local(detail::ElementProxy elementProxy) {
return static_cast>(elementProxy);
}
-
-struct FBEXPORT JStackTraceElement : JavaClass {
- static auto constexpr kJavaDescriptor = "Ljava/lang/StackTraceElement;";
-
- static local_ref 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 {
- public:
- static constexpr const char* kJavaDescriptor = "Ljava/lang/Throwable;";
- local_ref initCause(alias_ref cause);
- using JStackTrace = JArrayClass;
- local_ref getStackTrace();
-};
-
template
class PinnedPrimitiveArray;
@@ -468,7 +455,7 @@ template class PinnedCriticalAlloc;
/// This is an empty holder by itself. Construct a PinnedPrimitiveArray to actually interact with
/// the elements of the array.
template
-class FBEXPORT JPrimitiveArray :
+class JPrimitiveArray :
public JavaClass, detail::JArray, JArrayType> {
static_assert(is_jni_primitive_array(), "");
public:
@@ -508,14 +495,14 @@ private:
void releaseElements(T* elements, jint mode);
};
-FBEXPORT local_ref make_boolean_array(jsize size);
-FBEXPORT local_ref make_byte_array(jsize size);
-FBEXPORT local_ref make_char_array(jsize size);
-FBEXPORT local_ref make_short_array(jsize size);
-FBEXPORT local_ref make_int_array(jsize size);
-FBEXPORT local_ref make_long_array(jsize size);
-FBEXPORT local_ref make_float_array(jsize size);
-FBEXPORT local_ref make_double_array(jsize size);
+local_ref make_boolean_array(jsize size);
+local_ref make_byte_array(jsize size);
+local_ref make_char_array(jsize size);
+local_ref make_short_array(jsize size);
+local_ref make_int_array(jsize size);
+local_ref make_long_array(jsize size);
+local_ref make_float_array(jsize size);
+local_ref make_double_array(jsize size);
using JArrayBoolean = JPrimitiveArray;
using JArrayByte = JPrimitiveArray;
@@ -577,6 +564,29 @@ class PinnedPrimitiveArray {
friend class JPrimitiveArray::array_type>;
};
+struct JStackTraceElement : JavaClass {
+ static auto constexpr kJavaDescriptor = "Ljava/lang/StackTraceElement;";
+
+ static local_ref 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 {
+ public:
+ static constexpr const char* kJavaDescriptor = "Ljava/lang/Throwable;";
+
+ using JStackTrace = JArrayClass;
+
+ local_ref initCause(alias_ref cause);
+ local_ref getStackTrace();
+ void setStackTrace(alias_ref>);
+};
+
#pragma push_macro("PlainJniRefMap")
#undef PlainJniRefMap
#define PlainJniRefMap(rtype, jtype) \
diff --git a/libs/fbjni/cxx/fbjni/detail/Environment.cpp b/libs/fbjni/cxx/fbjni/detail/Environment.cpp
new file mode 100644
index 000000000..4911076cc
--- /dev/null
+++ b/libs/fbjni/cxx/fbjni/detail/Environment.cpp
@@ -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
+
+#include
+#include
+
+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
+struct AttachTraits;
+
+template <>
+struct AttachTraits {
+ using EnvType = JNIEnv*;
+};
+
+template <>
+struct AttachTraits {
+ using EnvType = void*;
+};
+
+JNIEnv* attachCurrentThread() {
+ JavaVMAttachArgs args{JNI_VERSION_1_6, nullptr, nullptr};
+ using AttachEnvType =
+ typename AttachTraits::EnvType;
+ AttachEnvType env;
+ auto result = g_vm->AttachCurrentThread(&env, &args);
+ FBJNI_ASSERT(result == JNI_OK);
+ return reinterpret_cast(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(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 {
+ 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&& func) {
+ static const auto method = javaClassStatic()->getStaticMethod("runStdFunction");
+ method(javaClassStatic(), reinterpret_cast(&func));
+ }
+
+ static void runStdFunctionImpl(alias_ref, jlong ptr) {
+ (*reinterpret_cast*>(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&& runnable) {
+ if (cachedOrNull() == nullptr) {
+ ThreadScope ts;
+ JThreadScopeSupport::runStdFunction(std::move(runnable));
+ } else {
+ runnable();
+ }
+}
+
+} }
diff --git a/libs/fbjni/src/main/cpp/include/fb/Environment.h b/libs/fbjni/cxx/fbjni/detail/Environment.h
similarity index 50%
rename from libs/fbjni/src/main/cpp/include/fb/Environment.h
rename to libs/fbjni/cxx/fbjni/detail/Environment.h
index e63db9cb2..e48fcbe89 100644
--- a/libs/fbjni/src/main/cpp/include/fb/Environment.h
+++ b/libs/fbjni/cxx/fbjni/detail/Environment.h
@@ -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
- * LICENSE file in the root directory of this source tree.
+ * 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
@@ -10,24 +19,69 @@
#include
#include
-#include
namespace facebook {
namespace jni {
// Keeps a thread-local reference to the current thread's JNIEnv.
struct Environment {
- // May be null if this thread isn't attached to the JVM
- FBEXPORT static JNIEnv* current();
+ // Throws a std::runtime_error if this thread isn't attached to the JVM
+ // TODO(T6594868) Benchmark against raw JNI access
+ static JNIEnv* current();
static void initialize(JavaVM* vm);
// There are subtle issues with calling the next functions directly. It is
// much better to always use a ThreadScope to manage attaching/detaching for
// you.
- FBEXPORT static JNIEnv* ensureCurrentThreadIsAttached();
- FBEXPORT static void detachCurrentThread();
+ static JNIEnv* ensureCurrentThreadIsAttached();
};
+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
* 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.
* (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 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:
ThreadScope();
ThreadScope(ThreadScope&) = delete;
@@ -68,8 +125,14 @@ class FBEXPORT ThreadScope {
static void WithClassLoader(std::function&& runnable);
static void OnLoad();
+
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_;
};
+
}
}
diff --git a/libs/fbjni/cxx/fbjni/detail/Exceptions.cpp b/libs/fbjni/cxx/fbjni/detail/Exceptions.cpp
new file mode 100644
index 000000000..6291d54ad
--- /dev/null
+++ b/libs/fbjni/cxx/fbjni/detail/Exceptions.cpp
@@ -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
+#include
+#endif
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+namespace facebook {
+namespace jni {
+
+namespace {
+class JRuntimeException : public JavaClass