diff --git a/.gitignore b/.gitignore index 0d1f02df0..9cebfcc68 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ website/build *.xcworkspace **/Pods/ **/xcuserdata/ +build/ yarn-error.log # Android / Intellij diff --git a/android/CMakeLists.txt b/android/CMakeLists.txt index 496069fe9..e8e3e1e1b 100644 --- a/android/CMakeLists.txt +++ b/android/CMakeLists.txt @@ -1,10 +1,13 @@ cmake_minimum_required (VERSION 3.6.0) -project(sonar CXX C) +project(sonar CXX) +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_VERBOSE_MAKEFILE on) set(PACKAGE_NAME "sonar") -add_compile_options(-DFOLLY_NO_CONFIG +add_compile_options(-DSONAR_OSS=1 + -DFOLLY_NO_CONFIG -DSONAR_JNI_EXTERNAL=1 -DFB_SONARKIT_ENABLED=1 -DFOLLY_HAVE_MEMRCHR @@ -14,7 +17,6 @@ add_compile_options(-DFOLLY_NO_CONFIG -DFOLLY_HAVE_PREADV=0 -frtti -fexceptions - -std=c++14 -Wno-error -Wno-unused-local-typedefs -Wno-unused-variable @@ -29,13 +31,13 @@ add_library(${PACKAGE_NAME} SHARED ${SOURCES}) target_include_directories(${PACKAGE_NAME} PUBLIC "./") set(libjnihack_DIR ${CMAKE_SOURCE_DIR}/../libs/jni-hack/) -set(libfbjni_DIR ${CMAKE_SOURCE_DIR}/../libs/fbjni/src/main/cpp/include/) +set(libfbjni_DIR ${CMAKE_SOURCE_DIR}/../libs/fbjni/) set(libsonar_DIR ${CMAKE_SOURCE_DIR}/../xplat/) -set(third_party_ndk build/third-party-ndk) +set(third_party_ndk ${PROJECT_SOURCE_DIR}/build/third-party-ndk) set(libfolly_DIR ${third_party_ndk}/folly/) set(glog_DIR ${third_party_ndk}/glog) set(BOOST_DIR ${third_party_ndk}/boost/boost_1_63_0/) - +set(LIBEVENT_DIR ${third_party_ndk}/LibEvent/libevent-release-2.1.9/) set(build_DIR ${CMAKE_SOURCE_DIR}/build) @@ -46,11 +48,14 @@ set(libfolly_build_DIR ${build_DIR}/libfolly/${ANDROID_ABI}) file(MAKE_DIRECTORY ${build_DIR}) add_subdirectory(${libsonar_DIR} ${libsonar_build_DIR}) -add_subdirectory(${libfbjni_DIR}/../ ${fbjni_build_DIR}) +add_subdirectory(${libfbjni_DIR} ${fbjni_build_DIR}) target_include_directories(${PACKAGE_NAME} PRIVATE ${libjnihack_DIR} - ${libfbjni_DIR} + ${libfbjni_DIR}/cxx/ + ${libfbjni_DIR}/cxx/fbjni + ${libfbjni_DIR}/cxx/fbjni/detail + ${libfbjni_DIR}/cxx/lyra ${libsonar_DIR} ${libfolly_DIR} ${glog_DIR} @@ -58,6 +63,9 @@ target_include_directories(${PACKAGE_NAME} PRIVATE ${glog_DIR}/glog-0.3.5/src/ ${BOOST_DIR} ${BOOST_DIR}/../ + ${LIBEVENT_DIR}/ + ${LIBEVENT_DIR}/include/ + ${LIBEVENT_DIR}/include/event2 ) -target_link_libraries(${PACKAGE_NAME} fb sonarcpp) +target_link_libraries(${PACKAGE_NAME} sonarfb sonarcpp) diff --git a/android/android/sonar.cpp b/android/android/sonar.cpp index f7a279e57..874c91774 100644 --- a/android/android/sonar.cpp +++ b/android/android/sonar.cpp @@ -8,7 +8,11 @@ #include +#ifdef SONAR_OSS +#include +#else #include +#endif #include #include @@ -275,10 +279,10 @@ class JSonarClient : public jni::HybridClass { SonarClient::init({ { - std::move(host), - std::move(os), - std::move(device), - std::move(deviceId), + std::move(host), + std::move(os), + std::move(device), + std::move(deviceId), std::move(app), std::move(appId), std::move(privateAppDirectory) diff --git a/android/build.gradle b/android/build.gradle index cd641166d..646f672ad 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -74,13 +74,13 @@ task prepareDoubleConversion(dependsOn: [downloadDoubleConversion], type: Copy) task downloadBoost(dependsOn: createNativeDepsDirectories, type: Download) { src 'https://github.com/react-native-community/boost-for-react-native/releases/download/v1.63.0-0/boost_1_63_0.tar.gz' onlyIfNewer true - overwrite false + overwrite true dest new File(downloadsDir, 'boost_1_63_0.tar.gz') } task prepareBoost(dependsOn: [downloadBoost], type: Copy) { from tarTree(resources.gzip(downloadBoost.dest)) - include 'boost_1_63_0/boost/**/*.hpp', 'boost/boost/**/*.hpp' + include 'boost_1_63_0/boost/**/*.hpp', 'boost_1_63_0/boost/**/*.h', 'boost/boost/**/*.hpp', 'boost/boost/**/*.h' includeEmptyDirs = false into "$thirdPartyNdkDir/boost" doLast { @@ -89,26 +89,102 @@ task prepareBoost(dependsOn: [downloadBoost], type: Copy) { } task downloadFolly(dependsOn: createNativeDepsDirectories, type: Download) { - src 'https://github.com/facebook/folly/archive/v2018.05.21.00.tar.gz' + src 'https://github.com/facebook/folly/archive/v2018.06.18.00.tar.gz' onlyIfNewer true overwrite false - dest new File(downloadsDir, 'folly-2018.05.21.00.tar.gz'); + dest new File(downloadsDir, 'folly-2018.06.18.00.tar.gz'); } task prepareFolly(dependsOn: [downloadFolly], type: Copy) { from tarTree(downloadFolly.dest) - from './third-party/folly/' - include 'folly-2018.05.21.00/folly/**/*', 'build.gradle', 'CMakeLists.txt', 'ApplicationManifest.xml' - eachFile {fname -> fname.path = (fname.path - "folly-2018.05.21.00/")} + from './third-party/Folly/' + include 'folly-2018.06.18.00/folly/**/*', 'build.gradle', 'CMakeLists.txt', 'ApplicationManifest.xml' + eachFile {fname -> fname.path = (fname.path - "folly-2018.06.18.00/")} includeEmptyDirs = false into "$thirdPartyNdkDir/folly" } +//TODO: Get rid off this hack. +task finalizeFolly(dependsOn: [prepareFolly], type: Copy) { + from './third-party/Folly/' + include 'AsyncServerSocket.cpp' + into "$thirdPartyNdkDir/folly/folly/io/async/" +} + +task downloadLibEvent(dependsOn: [], type: Download) { + src 'https://github.com/priteshrnandgaonkar/libevent/archive/release-2.1.9.tar.gz' + onlyIfNewer true + overwrite false + dest new File(downloadsDir, 'libevent-release-2.1.9.tar.gz'); +} + +task prepareLibEvent(dependsOn: [downloadLibEvent], type: Copy) { + from tarTree(downloadLibEvent.dest) + from './third-party/LibEvent/' + include 'libevent-release-2.1.9/**/*', 'build.gradle', 'ApplicationManifest.xml' + includeEmptyDirs = false + into "$thirdPartyNdkDir/LibEvent" +} + +task finalizeEvent(dependsOn: [prepareLibEvent], type: Copy) { + from './third-party/LibEvent/' + include 'event-config.h' + includeEmptyDirs = false + into "$thirdPartyNdkDir/LibEvent/libevent-release-2.1.9/include/event2/" +} + +task finalizeEvent2(dependsOn: [finalizeEvent], type: Copy) { + from './third-party/LibEvent/' + include 'libs/**/*' + includeEmptyDirs = false + into "$thirdPartyNdkDir/LibEvent/" +} + +task downloadOpenSSL(dependsOn: [], type: Download) { + src 'https://github.com/priteshrnandgaonkar/openssl-android/archive/1.0.0.tar.gz' + onlyIfNewer true + overwrite false + dest new File(downloadsDir, 'openssl-android-1.0.0.tar.gz'); +} + +task prepareOpenSSL(dependsOn: [downloadOpenSSL], type: Copy) { + from tarTree(downloadOpenSSL.dest) + from './third-party/OpenSSL/' + include 'openssl-android-1.0.0/**/*', 'libs/**/*' + includeEmptyDirs = false + into "$thirdPartyNdkDir/OpenSSL/" +} + +task finalizeOpenSSL(dependsOn: [prepareOpenSSL], type: Copy) { + from './third-party/OpenSSL/' + include 'build.gradle', 'ApplicationManifest.xml' + includeEmptyDirs = false + into "$thirdPartyNdkDir/OpenSSL/openssl-android-1.0.0/" +} + +task downloadRSocket(dependsOn: [], type: Download) { + src 'https://github.com/priteshrnandgaonkar/rsocket-cpp/archive/0.10.1.tar.gz' + onlyIfNewer true + overwrite false + dest new File(downloadsDir, 'rsocket-cpp-0.10.1.tar.gz'); +} + +task prepareRSocket(dependsOn: [downloadRSocket], type: Copy) { + from tarTree(downloadRSocket.dest) + from './third-party/RSocket/' + include 'rsocket-cpp-0.10.1/**/*', 'build.gradle', 'ApplicationManifest.xml', 'CMakeLists.txt' + includeEmptyDirs = false + into "$thirdPartyNdkDir/RSocket" +} + task prepareAllLibs() { dependsOn finalizeGlog dependsOn prepareDoubleConversion dependsOn prepareBoost - dependsOn prepareFolly + dependsOn finalizeFolly + dependsOn finalizeEvent + dependsOn finalizeOpenSSL + dependsOn prepareRSocket } android { @@ -120,7 +196,7 @@ android { targetSdkVersion rootProject.targetSdkVersion buildConfigField "boolean", "IS_INTERNAL_BUILD", 'true' ndk { - abiFilters 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a' + abiFilters 'arm64-v8a', 'x86', 'armeabi-v7a' stl 'c++_shared' } @@ -141,6 +217,9 @@ android { srcDir 'android' srcDir 'core' srcDir 'plugins' + exclude 'console/ConsoleSonarPlugin.java' + exclude 'console/JavascriptEnvironment.java' + exclude 'console/JavascriptSession.java' } res { srcDir 'res' @@ -163,8 +242,8 @@ android { implementation deps.supportAppCompat implementation deps.stetho implementation deps.okhttp3 - implementation 'com.facebook.litho:litho-core:0.15.0' - implementation 'com.facebook.litho:litho-widget:0.15.0' + implementation deps.lithoCore + implementation deps.lithoWidget implementation 'org.mozilla:rhino:1.7.10' } } diff --git a/android/res/values/ids.xml b/android/res/values/ids.xml new file mode 100644 index 000000000..6d454c931 --- /dev/null +++ b/android/res/values/ids.xml @@ -0,0 +1,4 @@ + + + + diff --git a/android/sample/build.gradle b/android/sample/build.gradle index 5e793ba0f..d27d3c761 100644 --- a/android/sample/build.gradle +++ b/android/sample/build.gradle @@ -22,17 +22,21 @@ android { res { srcDir 'res' } + jniLibs.srcDirs = ['../third-party/OpenSSL/libs'] } } - packagingOptions { - pickFirst 'lib/armeabi-v7a/libfb.so' - pickFirst 'lib/x86/libfb.so' - pickFirst 'lib/x86_64/libfb.so' - pickFirst 'lib/arm64-v8a/libfb.so' + + packagingOptions { + pickFirst 'lib/armeabi-v7a/libsonarfb.so' + pickFirst 'lib/x86/libsonarfb.so' + pickFirst 'lib/x86_64/libsonarfb.so' + pickFirst 'lib/arm64-v8a/libsonarfb.so' } } + dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) implementation deps.supportAppCompat implementation deps.supportConstraintLayout @@ -50,12 +54,12 @@ dependencies { // SoLoader implementation deps.soloader - // For integration with Fresco +// For integration with Fresco implementation deps.lithoFresco - // For testing testImplementation deps.lithoTesting implementation deps.okhttp3 implementation project(':android') + implementation project(':fbjni') } diff --git a/android/sample/src/sonar/com/facebook/sonar/sample/MainActivity.java b/android/sample/src/sonar/com/facebook/sonar/sample/MainActivity.java index 656a670cd..3229b4c68 100644 --- a/android/sample/src/sonar/com/facebook/sonar/sample/MainActivity.java +++ b/android/sample/src/sonar/com/facebook/sonar/sample/MainActivity.java @@ -4,16 +4,20 @@ package com.facebook.sonar.sample; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; +import java.util.List; +import android.widget.LinearLayout.LayoutParams; +import android.widget.LinearLayout; +import android.widget.TextView; import com.facebook.litho.ComponentContext; import com.facebook.litho.LithoView; +import android.util.Log; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - - final ComponentContext c = new ComponentContext(this); + final ComponentContext c = new ComponentContext(this); setContentView( LithoView.create( c, diff --git a/android/sample/src/sonar/com/facebook/sonar/sample/SonarSampleApplication.java b/android/sample/src/sonar/com/facebook/sonar/sample/SonarSampleApplication.java index 6acb6a5b3..18c32e30f 100644 --- a/android/sample/src/sonar/com/facebook/sonar/sample/SonarSampleApplication.java +++ b/android/sample/src/sonar/com/facebook/sonar/sample/SonarSampleApplication.java @@ -7,8 +7,13 @@ import android.net.Network; import android.support.annotation.Nullable; import java.util.ArrayList; import java.util.List; +import android.widget.LinearLayout.LayoutParams; +import android.widget.LinearLayout; +import android.widget.TextView; import com.facebook.litho.sonar.LithoSonarDescriptors; import com.facebook.soloader.SoLoader; +import com.facebook.sonar.plugins.inspector.DescriptorMapping; +import com.facebook.sonar.plugins.inspector.InspectorSonarPlugin; import com.facebook.sonar.android.utils.SonarUtils; import com.facebook.sonar.android.AndroidSonarClient; import com.facebook.sonar.core.SonarClient; @@ -46,5 +51,5 @@ public class SonarSampleApplication extends Application { client.addPlugin(new InspectorSonarPlugin(this, descriptorMapping)); client.addPlugin(networkPlugin); client.start(); -} + } } diff --git a/android/third-party/DoubleConversion/CMakeLists.txt b/android/third-party/DoubleConversion/CMakeLists.txt index 3e9993f7e..af6748b35 100644 --- a/android/third-party/DoubleConversion/CMakeLists.txt +++ b/android/third-party/DoubleConversion/CMakeLists.txt @@ -6,7 +6,6 @@ set(PACKAGE_NAME doubleconversion) set(doubleconversion_DIR double-conversion-3.0.0/double-conversion) include_directories(${doubleconversion_DIR}) file(GLOB SRCFILES ${doubleconversion_DIR}/*.cc) -message(STATUS "SRC FILES :- " ${SRCFILES}) add_library(${PACKAGE_NAME} SHARED ${SRCFILES}) install(TARGETS ${PACKAGE_NAME} DESTINATION ./build/) target_link_libraries(${PACKAGE_NAME}) diff --git a/android/third-party/Folly/AsyncServerSocket.cpp b/android/third-party/Folly/AsyncServerSocket.cpp new file mode 100644 index 000000000..6beba061c --- /dev/null +++ b/android/third-party/Folly/AsyncServerSocket.cpp @@ -0,0 +1,1103 @@ +/* + * Copyright 2014-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. + */ + +#ifndef __STDC_FORMAT_MACROS + #define __STDC_FORMAT_MACROS +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace fsp = folly::portability::sockets; + +namespace folly { + +static constexpr bool msgErrQueueSupported = +#ifdef FOLLY_HAVE_MSG_ERRQUEUE + true; +#else + false; +#endif // FOLLY_HAVE_MSG_ERRQUEUE + +const uint32_t AsyncServerSocket::kDefaultMaxAcceptAtOnce; +const uint32_t AsyncServerSocket::kDefaultCallbackAcceptAtOnce; +const uint32_t AsyncServerSocket::kDefaultMaxMessagesInQueue; + +int setCloseOnExec(int fd, int value) { + // Read the current flags + int old_flags = fcntl(fd, F_GETFD, 0); + + // If reading the flags failed, return error indication now + if (old_flags < 0) { + return -1; + } + + // Set just the flag we want to set + int new_flags; + if (value != 0) { + new_flags = old_flags | FD_CLOEXEC; + } else { + new_flags = old_flags & ~FD_CLOEXEC; + } + + // Store modified flag word in the descriptor + return fcntl(fd, F_SETFD, new_flags); +} + +void AsyncServerSocket::RemoteAcceptor::start( + EventBase* eventBase, uint32_t maxAtOnce, uint32_t maxInQueue) { + setMaxReadAtOnce(maxAtOnce); + queue_.setMaxQueueSize(maxInQueue); + + if (!eventBase->runInEventBaseThread([=](){ + callback_->acceptStarted(); + this->startConsuming(eventBase, &queue_); + })) { + throw std::invalid_argument("unable to start waiting on accept " + "notification queue in the specified " + "EventBase thread"); + } +} + +void AsyncServerSocket::RemoteAcceptor::stop( + EventBase* eventBase, AcceptCallback* callback) { + if (!eventBase->runInEventBaseThread([=](){ + callback->acceptStopped(); + delete this; + })) { + throw std::invalid_argument("unable to start waiting on accept " + "notification queue in the specified " + "EventBase thread"); + } +} + +void AsyncServerSocket::RemoteAcceptor::messageAvailable( + QueueMessage&& msg) noexcept { + switch (msg.type) { + case MessageType::MSG_NEW_CONN: + { + if (connectionEventCallback_) { + connectionEventCallback_->onConnectionDequeuedByAcceptorCallback( + msg.fd, msg.address); + } + callback_->connectionAccepted(msg.fd, msg.address); + break; + } + case MessageType::MSG_ERROR: + { + std::runtime_error ex(msg.msg); + callback_->acceptError(ex); + break; + } + default: + { + LOG(ERROR) << "invalid accept notification message type " + << int(msg.type); + std::runtime_error ex( + "received invalid accept notification message type"); + callback_->acceptError(ex); + } + } +} + +/* + * AsyncServerSocket::BackoffTimeout + */ +class AsyncServerSocket::BackoffTimeout : public AsyncTimeout { + public: + // Disallow copy, move, and default constructors. + BackoffTimeout(BackoffTimeout&&) = delete; + explicit BackoffTimeout(AsyncServerSocket* socket) + : AsyncTimeout(socket->getEventBase()), socket_(socket) {} + + void timeoutExpired() noexcept override { socket_->backoffTimeoutExpired(); } + + private: + AsyncServerSocket* socket_; +}; + +/* + * AsyncServerSocket methods + */ + +AsyncServerSocket::AsyncServerSocket(EventBase* eventBase) + : eventBase_(eventBase), + accepting_(false), + maxAcceptAtOnce_(kDefaultMaxAcceptAtOnce), + maxNumMsgsInQueue_(kDefaultMaxMessagesInQueue), + acceptRateAdjustSpeed_(0), + acceptRate_(1), + lastAccepTimestamp_(std::chrono::steady_clock::now()), + numDroppedConnections_(0), + callbackIndex_(0), + backoffTimeout_(nullptr), + callbacks_(), + keepAliveEnabled_(true), + closeOnExec_(true) {} + +void AsyncServerSocket::setShutdownSocketSet( + const std::weak_ptr& wNewSS) { + const auto newSS = wNewSS.lock(); + const auto shutdownSocketSet = wShutdownSocketSet_.lock(); + + if (shutdownSocketSet == newSS) { + return; + } + + if (shutdownSocketSet) { + for (auto& h : sockets_) { + shutdownSocketSet->remove(h.socket_); + } + } + + if (newSS) { + for (auto& h : sockets_) { + newSS->add(h.socket_); + } + } + + wShutdownSocketSet_ = wNewSS; +} + +AsyncServerSocket::~AsyncServerSocket() { + assert(callbacks_.empty()); +} + +int AsyncServerSocket::stopAccepting(int shutdownFlags) { + int result = 0; + for (auto& handler : sockets_) { + VLOG(10) << "AsyncServerSocket::stopAccepting " << this << + handler.socket_; + } + if (eventBase_) { + eventBase_->dcheckIsInEventBaseThread(); + } + + // When destroy is called, unregister and close the socket immediately. + accepting_ = false; + + // Close the sockets in reverse order as they were opened to avoid + // the condition where another process concurrently tries to open + // the same port, succeed to bind the first socket but fails on the + // second because it hasn't been closed yet. + for (; !sockets_.empty(); sockets_.pop_back()) { + auto& handler = sockets_.back(); + handler.unregisterHandler(); + if (const auto shutdownSocketSet = wShutdownSocketSet_.lock()) { + shutdownSocketSet->close(handler.socket_); + } else if (shutdownFlags >= 0) { + result = shutdownNoInt(handler.socket_, shutdownFlags); + pendingCloseSockets_.push_back(handler.socket_); + } else { + closeNoInt(handler.socket_); + } + } + + // Destroy the backoff timout. This will cancel it if it is running. + delete backoffTimeout_; + backoffTimeout_ = nullptr; + + // Close all of the callback queues to notify them that they are being + // destroyed. No one should access the AsyncServerSocket any more once + // destroy() is called. However, clear out callbacks_ before invoking the + // accept callbacks just in case. This will potentially help us detect the + // bug if one of the callbacks calls addAcceptCallback() or + // removeAcceptCallback(). + std::vector callbacksCopy; + callbacks_.swap(callbacksCopy); + for (std::vector::iterator it = callbacksCopy.begin(); + it != callbacksCopy.end(); + ++it) { + // consumer may not be set if we are running in primary event base + if (it->consumer) { + DCHECK(it->eventBase); + it->consumer->stop(it->eventBase, it->callback); + } else { + DCHECK(it->callback); + it->callback->acceptStopped(); + } + } + + return result; +} + +void AsyncServerSocket::destroy() { + stopAccepting(); + for (auto s : pendingCloseSockets_) { + closeNoInt(s); + } + // Then call DelayedDestruction::destroy() to take care of + // whether or not we need immediate or delayed destruction + DelayedDestruction::destroy(); +} + +void AsyncServerSocket::attachEventBase(EventBase *eventBase) { + assert(eventBase_ == nullptr); + eventBase->dcheckIsInEventBaseThread(); + + eventBase_ = eventBase; + for (auto& handler : sockets_) { + handler.attachEventBase(eventBase); + } +} + +void AsyncServerSocket::detachEventBase() { + assert(eventBase_ != nullptr); + eventBase_->dcheckIsInEventBaseThread(); + assert(!accepting_); + + eventBase_ = nullptr; + for (auto& handler : sockets_) { + handler.detachEventBase(); + } +} + +void AsyncServerSocket::useExistingSockets(const std::vector& fds) { + if (eventBase_) { + eventBase_->dcheckIsInEventBaseThread(); + } + + if (sockets_.size() > 0) { + throw std::invalid_argument( + "cannot call useExistingSocket() on a " + "AsyncServerSocket that already has a socket"); + } + + for (auto fd: fds) { + // Set addressFamily_ from this socket. + // Note that the socket may not have been bound yet, but + // setFromLocalAddress() will still work and get the correct address family. + // We will update addressFamily_ again anyway if bind() is called later. + SocketAddress address; + address.setFromLocalAddress(fd); + +#if __linux__ + if (noTransparentTls_) { + // Ignore return value, errors are ok + setsockopt(fd, SOL_SOCKET, SO_NO_TRANSPARENT_TLS, nullptr, 0); + } +#endif + + setupSocket(fd, address.getFamily()); + sockets_.emplace_back(eventBase_, fd, this, address.getFamily()); + sockets_.back().changeHandlerFD(fd); + } +} + +void AsyncServerSocket::useExistingSocket(int fd) { + useExistingSockets({fd}); +} + +void AsyncServerSocket::bindSocket( + int fd, + const SocketAddress& address, + bool isExistingSocket) { + sockaddr_storage addrStorage; + address.getAddress(&addrStorage); + sockaddr* saddr = reinterpret_cast(&addrStorage); + + if (fsp::bind(fd, saddr, address.getActualSize()) != 0) { + if (!isExistingSocket) { + closeNoInt(fd); + } + folly::throwSystemError(errno, + "failed to bind to async server socket: " + + address.describe()); + } + +#if __linux__ + if (noTransparentTls_) { + // Ignore return value, errors are ok + setsockopt(fd, SOL_SOCKET, SO_NO_TRANSPARENT_TLS, nullptr, 0); + } +#endif + + // If we just created this socket, update the EventHandler and set socket_ + if (!isExistingSocket) { + sockets_.emplace_back(eventBase_, fd, this, address.getFamily()); + } +} + +bool AsyncServerSocket::setZeroCopy(bool enable) { + if (msgErrQueueSupported) { + int fd = getSocket(); + int val = enable ? 1 : 0; + int ret = setsockopt(fd, SOL_SOCKET, SO_ZEROCOPY, &val, sizeof(val)); + + return (0 == ret); + } + + return false; +} + +void AsyncServerSocket::bind(const SocketAddress& address) { + if (eventBase_) { + eventBase_->dcheckIsInEventBaseThread(); + } + + // useExistingSocket() may have been called to initialize socket_ already. + // However, in the normal case we need to create a new socket now. + // Don't set socket_ yet, so that socket_ will remain uninitialized if an + // error occurs. + int fd; + if (sockets_.size() == 0) { + fd = createSocket(address.getFamily()); + } else if (sockets_.size() == 1) { + if (address.getFamily() != sockets_[0].addressFamily_) { + throw std::invalid_argument( + "Attempted to bind address to socket with " + "different address family"); + } + fd = sockets_[0].socket_; + } else { + throw std::invalid_argument( + "Attempted to bind to multiple fds"); + } + + bindSocket(fd, address, !sockets_.empty()); +} + +void AsyncServerSocket::bind( + const std::vector& ipAddresses, + uint16_t port) { + if (ipAddresses.empty()) { + throw std::invalid_argument("No ip addresses were provided"); + } + if (!sockets_.empty()) { + throw std::invalid_argument("Cannot call bind on a AsyncServerSocket " + "that already has a socket."); + } + + for (const IPAddress& ipAddress : ipAddresses) { + SocketAddress address(ipAddress.toFullyQualified(), port); + int fd = createSocket(address.getFamily()); + + bindSocket(fd, address, false); + } + if (sockets_.size() == 0) { + throw std::runtime_error( + "did not bind any async server socket for port and addresses"); + } +} + +void AsyncServerSocket::bind(uint16_t port) { + struct addrinfo hints, *res0; + char sport[sizeof("65536")]; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV; + snprintf(sport, sizeof(sport), "%u", port); + + // On Windows the value we need to pass to bind to all available + // addresses is an empty string. Everywhere else, it's nullptr. + constexpr const char* kWildcardNode = kIsWindows ? "" : nullptr; + if (getaddrinfo(kWildcardNode, sport, &hints, &res0)) { + throw std::invalid_argument( + "Attempted to bind address to socket with " + "bad getaddrinfo"); + } + + SCOPE_EXIT { freeaddrinfo(res0); }; + + auto setupAddress = [&] (struct addrinfo* res) { + int s = fsp::socket(res->ai_family, res->ai_socktype, res->ai_protocol); + // IPv6/IPv4 may not be supported by the kernel + if (s < 0 && errno == EAFNOSUPPORT) { + return; + } + CHECK_GE(s, 0); + + try { + setupSocket(s, res->ai_family); + } catch (...) { + closeNoInt(s); + throw; + } + + if (res->ai_family == AF_INET6) { + int v6only = 1; + CHECK(0 == setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, + &v6only, sizeof(v6only))); + } + + // Bind to the socket + if (fsp::bind(s, res->ai_addr, socklen_t(res->ai_addrlen)) != 0) { + folly::throwSystemError( + errno, + "failed to bind to async server socket for port ", + SocketAddress::getPortFrom(res->ai_addr), + " family ", + SocketAddress::getFamilyNameFrom(res->ai_addr, "")); + } + +#if __linux__ + if (noTransparentTls_) { + // Ignore return value, errors are ok + setsockopt(s, SOL_SOCKET, SO_NO_TRANSPARENT_TLS, nullptr, 0); + } +#endif + + SocketAddress address; + address.setFromLocalAddress(s); + + sockets_.emplace_back(eventBase_, s, this, address.getFamily()); + }; + + const int kNumTries = 25; + for (int tries = 1; true; tries++) { + // Prefer AF_INET6 addresses. RFC 3484 mandates that getaddrinfo + // should return IPv6 first and then IPv4 addresses, but glibc's + // getaddrinfo(nullptr) with AI_PASSIVE returns: + // - 0.0.0.0 (IPv4-only) + // - :: (IPv6+IPv4) in this order + // See: https://sourceware.org/bugzilla/show_bug.cgi?id=9981 + for (struct addrinfo* res = res0; res; res = res->ai_next) { + if (res->ai_family == AF_INET6) { + setupAddress(res); + } + } + + // If port == 0, then we should try to bind to the same port on ipv4 and + // ipv6. So if we did bind to ipv6, figure out that port and use it. + if (sockets_.size() == 1 && port == 0) { + SocketAddress address; + address.setFromLocalAddress(sockets_.back().socket_); + snprintf(sport, sizeof(sport), "%u", address.getPort()); + freeaddrinfo(res0); + CHECK_EQ(0, getaddrinfo(nullptr, sport, &hints, &res0)); + } + + try { + for (struct addrinfo* res = res0; res; res = res->ai_next) { + if (res->ai_family != AF_INET6) { + setupAddress(res); + } + } + } catch (const std::system_error&) { + // If we can't bind to the same port on ipv4 as ipv6 when using + // port=0 then we will retry again before giving up after + // kNumTries attempts. We do this by closing the sockets that + // were opened, then restarting from scratch. + if (port == 0 && !sockets_.empty() && tries != kNumTries) { + for (const auto& socket : sockets_) { + if (socket.socket_ <= 0) { + continue; + } else if ( + const auto shutdownSocketSet = wShutdownSocketSet_.lock()) { + shutdownSocketSet->close(socket.socket_); + } else { + closeNoInt(socket.socket_); + } + } + sockets_.clear(); + snprintf(sport, sizeof(sport), "%u", port); + freeaddrinfo(res0); + CHECK_EQ(0, getaddrinfo(nullptr, sport, &hints, &res0)); + continue; + } + + throw; + } + + break; + } + + if (sockets_.size() == 0) { + throw std::runtime_error( + "did not bind any async server socket for port"); + } +} + +void AsyncServerSocket::listen(int backlog) { + if (eventBase_) { + eventBase_->dcheckIsInEventBaseThread(); + } + + // Start listening + for (auto& handler : sockets_) { + if (fsp::listen(handler.socket_, backlog) == -1) { + folly::throwSystemError(errno, + "failed to listen on async server socket"); + } + } +} + +void AsyncServerSocket::getAddress(SocketAddress* addressReturn) const { + CHECK(sockets_.size() >= 1); + VLOG_IF(2, sockets_.size() > 1) + << "Warning: getAddress() called and multiple addresses available (" + << sockets_.size() << "). Returning only the first one."; + + addressReturn->setFromLocalAddress(sockets_[0].socket_); +} + +std::vector AsyncServerSocket::getAddresses() + const { + CHECK(sockets_.size() >= 1); + auto tsaVec = std::vector(sockets_.size()); + auto tsaIter = tsaVec.begin(); + for (const auto& socket : sockets_) { + (tsaIter++)->setFromLocalAddress(socket.socket_); + }; + return tsaVec; +} + +void AsyncServerSocket::addAcceptCallback(AcceptCallback *callback, + EventBase *eventBase, + uint32_t maxAtOnce) { + if (eventBase_) { + eventBase_->dcheckIsInEventBaseThread(); + } + + // If this is the first accept callback and we are supposed to be accepting, + // start accepting once the callback is installed. + bool runStartAccepting = accepting_ && callbacks_.empty(); + + callbacks_.emplace_back(callback, eventBase); + + SCOPE_SUCCESS { + // If this is the first accept callback and we are supposed to be accepting, + // start accepting. + if (runStartAccepting) { + startAccepting(); + } + }; + + if (!eventBase) { + // Run in AsyncServerSocket's eventbase; notify that we are + // starting to accept connections + callback->acceptStarted(); + return; + } + + // Start the remote acceptor. + // + // It would be nice if we could avoid starting the remote acceptor if + // eventBase == eventBase_. However, that would cause issues if + // detachEventBase() and attachEventBase() were ever used to change the + // primary EventBase for the server socket. Therefore we require the caller + // to specify a nullptr EventBase if they want to ensure that the callback is + // always invoked in the primary EventBase, and to be able to invoke that + // callback more efficiently without having to use a notification queue. + RemoteAcceptor* acceptor = nullptr; + try { + acceptor = new RemoteAcceptor(callback, connectionEventCallback_); + acceptor->start(eventBase, maxAtOnce, maxNumMsgsInQueue_); + } catch (...) { + callbacks_.pop_back(); + delete acceptor; + throw; + } + callbacks_.back().consumer = acceptor; +} + +void AsyncServerSocket::removeAcceptCallback(AcceptCallback *callback, + EventBase *eventBase) { + if (eventBase_) { + eventBase_->dcheckIsInEventBaseThread(); + } + + // Find the matching AcceptCallback. + // We just do a simple linear search; we don't expect removeAcceptCallback() + // to be called frequently, and we expect there to only be a small number of + // callbacks anyway. + std::vector::iterator it = callbacks_.begin(); + uint32_t n = 0; + while (true) { + if (it == callbacks_.end()) { + throw std::runtime_error("AsyncServerSocket::removeAcceptCallback(): " + "accept callback not found"); + } + if (it->callback == callback && + (it->eventBase == eventBase || eventBase == nullptr)) { + break; + } + ++it; + ++n; + } + + // Remove this callback from callbacks_. + // + // Do this before invoking the acceptStopped() callback, in case + // acceptStopped() invokes one of our methods that examines callbacks_. + // + // Save a copy of the CallbackInfo first. + CallbackInfo info(*it); + callbacks_.erase(it); + if (n < callbackIndex_) { + // We removed an element before callbackIndex_. Move callbackIndex_ back + // one step, since things after n have been shifted back by 1. + --callbackIndex_; + } else { + // We removed something at or after callbackIndex_. + // If we removed the last element and callbackIndex_ was pointing at it, + // we need to reset callbackIndex_ to 0. + if (callbackIndex_ >= callbacks_.size()) { + callbackIndex_ = 0; + } + } + + if (info.consumer) { + // consumer could be nullptr is we run callbacks in primary event + // base + DCHECK(info.eventBase); + info.consumer->stop(info.eventBase, info.callback); + } else { + // callback invoked in the primary event base, just call directly + DCHECK(info.callback); + callback->acceptStopped(); + } + + // If we are supposed to be accepting but the last accept callback + // was removed, unregister for events until a callback is added. + if (accepting_ && callbacks_.empty()) { + for (auto& handler : sockets_) { + handler.unregisterHandler(); + } + } +} + +void AsyncServerSocket::startAccepting() { + if (eventBase_) { + eventBase_->dcheckIsInEventBaseThread(); + } + + accepting_ = true; + if (callbacks_.empty()) { + // We can't actually begin accepting if no callbacks are defined. + // Wait until a callback is added to start accepting. + return; + } + + for (auto& handler : sockets_) { + if (!handler.registerHandler( + EventHandler::READ | EventHandler::PERSIST)) { + throw std::runtime_error("failed to register for accept events"); + } + } +} + +void AsyncServerSocket::pauseAccepting() { + if (eventBase_) { + eventBase_->dcheckIsInEventBaseThread(); + } + accepting_ = false; + for (auto& handler : sockets_) { + handler. unregisterHandler(); + } + + // If we were in the accept backoff state, disable the backoff timeout + if (backoffTimeout_) { + backoffTimeout_->cancelTimeout(); + } +} + +int AsyncServerSocket::createSocket(int family) { + int fd = fsp::socket(family, SOCK_STREAM, 0); + if (fd == -1) { + folly::throwSystemError(errno, "error creating async server socket"); + } + + try { + setupSocket(fd, family); + } catch (...) { + closeNoInt(fd); + throw; + } + return fd; +} + +void AsyncServerSocket::setupSocket(int fd, int family) { + // Put the socket in non-blocking mode + if (fcntl(fd, F_SETFL, O_NONBLOCK) != 0) { + folly::throwSystemError(errno, + "failed to put socket in non-blocking mode"); + } + + // Set reuseaddr to avoid 2MSL delay on server restart + int one = 1; + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) != 0) { + // This isn't a fatal error; just log an error message and continue + LOG(ERROR) << "failed to set SO_REUSEADDR on async server socket " << errno; + } + + // Set reuseport to support multiple accept threads + int zero = 0; + if (reusePortEnabled_ && + setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(int)) != 0) { + LOG(ERROR) << "failed to set SO_REUSEPORT on async server socket " + << strerror(errno); +#ifdef WIN32 + folly::throwSystemError(errno, "failed to bind to the async server socket"); +#else + SocketAddress address; + address.setFromLocalAddress(fd); + folly::throwSystemError(errno, + "failed to bind to async server socket: " + + address.describe()); +#endif + } + + // Set keepalive as desired + if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, + (keepAliveEnabled_) ? &one : &zero, sizeof(int)) != 0) { + LOG(ERROR) << "failed to set SO_KEEPALIVE on async server socket: " << + strerror(errno); + } + + // Setup FD_CLOEXEC flag + if (closeOnExec_ && + (-1 == folly::setCloseOnExec(fd, closeOnExec_))) { + LOG(ERROR) << "failed to set FD_CLOEXEC on async server socket: " << + strerror(errno); + } + + // Set TCP nodelay if available, MAC OS X Hack + // See http://lists.danga.com/pipermail/memcached/2005-March/001240.html +#ifndef TCP_NOPUSH + if (family != AF_UNIX) { + if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one)) != 0) { + // This isn't a fatal error; just log an error message and continue + LOG(ERROR) << "failed to set TCP_NODELAY on async server socket: " << + strerror(errno); + } + } +#else + (void)family; // to avoid unused parameter warning +#endif + +#if FOLLY_ALLOW_TFO + if (tfo_ && detail::tfo_enable(fd, tfoMaxQueueSize_) != 0) { + // This isn't a fatal error; just log an error message and continue + LOG(WARNING) << "failed to set TCP_FASTOPEN on async server socket: " + << folly::errnoStr(errno); + } +#endif + + if (const auto shutdownSocketSet = wShutdownSocketSet_.lock()) { + shutdownSocketSet->add(fd); + } +} + +void AsyncServerSocket::handlerReady(uint16_t /* events */, + int fd, + sa_family_t addressFamily) noexcept { + assert(!callbacks_.empty()); + DestructorGuard dg(this); + + // Only accept up to maxAcceptAtOnce_ connections at a time, + // to avoid starving other I/O handlers using this EventBase. + for (uint32_t n = 0; n < maxAcceptAtOnce_; ++n) { + SocketAddress address; + + sockaddr_storage addrStorage; + socklen_t addrLen = sizeof(addrStorage); + sockaddr* saddr = reinterpret_cast(&addrStorage); + + // In some cases, accept() doesn't seem to update these correctly. + saddr->sa_family = addressFamily; + if (addressFamily == AF_UNIX) { + addrLen = sizeof(struct sockaddr_un); + } + + // Accept a new client socket +#ifdef SOCK_NONBLOCK +// int clientSocket = accept4(fd, saddr, &addrLen, SOCK_NONBLOCK); +// #else + int clientSocket = accept(fd, saddr, &addrLen); +#endif + + address.setFromSockaddr(saddr, addrLen); + + if (clientSocket >= 0 && connectionEventCallback_) { + connectionEventCallback_->onConnectionAccepted(clientSocket, address); + } + + std::chrono::time_point nowMs = + std::chrono::steady_clock::now(); + auto timeSinceLastAccept = std::max( + 0, + nowMs.time_since_epoch().count() - + lastAccepTimestamp_.time_since_epoch().count()); + lastAccepTimestamp_ = nowMs; + if (acceptRate_ < 1) { + acceptRate_ *= 1 + acceptRateAdjustSpeed_ * timeSinceLastAccept; + if (acceptRate_ >= 1) { + acceptRate_ = 1; + } else if (rand() > acceptRate_ * RAND_MAX) { + ++numDroppedConnections_; + if (clientSocket >= 0) { + closeNoInt(clientSocket); + if (connectionEventCallback_) { + connectionEventCallback_->onConnectionDropped(clientSocket, + address); + } + } + continue; + } + } + + if (clientSocket < 0) { + if (errno == EAGAIN) { + // No more sockets to accept right now. + // Check for this code first, since it's the most common. + return; + } else if (errno == EMFILE || errno == ENFILE) { + // We're out of file descriptors. Perhaps we're accepting connections + // too quickly. Pause accepting briefly to back off and give the server + // a chance to recover. + LOG(ERROR) << "accept failed: out of file descriptors; entering accept " + "back-off state"; + enterBackoff(); + + // Dispatch the error message + dispatchError("accept() failed", errno); + } else { + dispatchError("accept() failed", errno); + } + if (connectionEventCallback_) { + connectionEventCallback_->onConnectionAcceptError(errno); + } + return; + } + +#ifndef SOCK_NONBLOCK + // Explicitly set the new connection to non-blocking mode + if (fcntl(clientSocket, F_SETFL, O_NONBLOCK) != 0) { + closeNoInt(clientSocket); + dispatchError("failed to set accepted socket to non-blocking mode", + errno); + if (connectionEventCallback_) { + connectionEventCallback_->onConnectionDropped(clientSocket, address); + } + return; + } +#endif + + // Inform the callback about the new connection + dispatchSocket(clientSocket, std::move(address)); + + // If we aren't accepting any more, break out of the loop + if (!accepting_ || callbacks_.empty()) { + break; + } + } +} + +void AsyncServerSocket::dispatchSocket(int socket, + SocketAddress&& address) { + uint32_t startingIndex = callbackIndex_; + + // Short circuit if the callback is in the primary EventBase thread + + CallbackInfo *info = nextCallback(); + if (info->eventBase == nullptr) { + info->callback->connectionAccepted(socket, address); + return; + } + + const SocketAddress addr(address); + // Create a message to send over the notification queue + QueueMessage msg; + msg.type = MessageType::MSG_NEW_CONN; + msg.address = std::move(address); + msg.fd = socket; + + // Loop until we find a free queue to write to + while (true) { + if (info->consumer->getQueue()->tryPutMessageNoThrow(std::move(msg))) { + if (connectionEventCallback_) { + connectionEventCallback_->onConnectionEnqueuedForAcceptorCallback( + socket, + addr); + } + // Success! return. + return; + } + + // We couldn't add to queue. Fall through to below + + ++numDroppedConnections_; + if (acceptRateAdjustSpeed_ > 0) { + // aggressively decrease accept rate when in trouble + static const double kAcceptRateDecreaseSpeed = 0.1; + acceptRate_ *= 1 - kAcceptRateDecreaseSpeed; + } + + + if (callbackIndex_ == startingIndex) { + // The notification queue was full + // We can't really do anything at this point other than close the socket. + // + // This should only happen if a user's service is behaving extremely + // badly and none of the EventBase threads are looping fast enough to + // process the incoming connections. If the service is overloaded, it + // should use pauseAccepting() to temporarily back off accepting new + // connections, before they reach the point where their threads can't + // even accept new messages. + LOG_EVERY_N(ERROR, 100) << "failed to dispatch newly accepted socket:" + << " all accept callback queues are full"; + closeNoInt(socket); + if (connectionEventCallback_) { + connectionEventCallback_->onConnectionDropped(socket, addr); + } + return; + } + + info = nextCallback(); + } +} + +void AsyncServerSocket::dispatchError(const char *msgstr, int errnoValue) { + uint32_t startingIndex = callbackIndex_; + CallbackInfo *info = nextCallback(); + + // Create a message to send over the notification queue + QueueMessage msg; + msg.type = MessageType::MSG_ERROR; + msg.err = errnoValue; + msg.msg = std::move(msgstr); + + while (true) { + // Short circuit if the callback is in the primary EventBase thread + if (info->eventBase == nullptr) { + std::runtime_error ex( + std::string(msgstr) + folly::to(errnoValue)); + info->callback->acceptError(ex); + return; + } + + if (info->consumer->getQueue()->tryPutMessageNoThrow(std::move(msg))) { + return; + } + // Fall through and try another callback + + if (callbackIndex_ == startingIndex) { + // The notification queues for all of the callbacks were full. + // We can't really do anything at this point. + LOG_EVERY_N(ERROR, 100) + << "failed to dispatch accept error: all accept" + << " callback queues are full: error msg: " << msg.msg << ": " + << errnoValue; + return; + } + info = nextCallback(); + } +} + +void AsyncServerSocket::enterBackoff() { + // If this is the first time we have entered the backoff state, + // allocate backoffTimeout_. + if (backoffTimeout_ == nullptr) { + try { + backoffTimeout_ = new BackoffTimeout(this); + } catch (const std::bad_alloc&) { + // Man, we couldn't even allocate the timer to re-enable accepts. + // We must be in pretty bad shape. Don't pause accepting for now, + // since we won't be able to re-enable ourselves later. + LOG(ERROR) << "failed to allocate AsyncServerSocket backoff" + << " timer; unable to temporarly pause accepting"; + if (connectionEventCallback_) { + connectionEventCallback_->onBackoffError(); + } + return; + } + } + + // For now, we simply pause accepting for 1 second. + // + // We could add some smarter backoff calculation here in the future. (e.g., + // start sleeping for longer if we keep hitting the backoff frequently.) + // Typically the user needs to figure out why the server is overloaded and + // fix it in some other way, though. The backoff timer is just a simple + // mechanism to try and give the connection processing code a little bit of + // breathing room to catch up, and to avoid just spinning and failing to + // accept over and over again. + const uint32_t timeoutMS = 1000; + if (!backoffTimeout_->scheduleTimeout(timeoutMS)) { + LOG(ERROR) << "failed to schedule AsyncServerSocket backoff timer;" + << "unable to temporarly pause accepting"; + if (connectionEventCallback_) { + connectionEventCallback_->onBackoffError(); + } + return; + } + + // The backoff timer is scheduled to re-enable accepts. + // Go ahead and disable accepts for now. We leave accepting_ set to true, + // since that tracks the desired state requested by the user. + for (auto& handler : sockets_) { + handler.unregisterHandler(); + } + if (connectionEventCallback_) { + connectionEventCallback_->onBackoffStarted(); + } +} + +void AsyncServerSocket::backoffTimeoutExpired() { + // accepting_ should still be true. + // If pauseAccepting() was called while in the backoff state it will cancel + // the backoff timeout. + assert(accepting_); + // We can't be detached from the EventBase without being paused + assert(eventBase_ != nullptr); + eventBase_->dcheckIsInEventBaseThread(); + + // If all of the callbacks were removed, we shouldn't re-enable accepts + if (callbacks_.empty()) { + if (connectionEventCallback_) { + connectionEventCallback_->onBackoffEnded(); + } + return; + } + + // Register the handler. + for (auto& handler : sockets_) { + if (!handler.registerHandler( + EventHandler::READ | EventHandler::PERSIST)) { + // We're hosed. We could just re-schedule backoffTimeout_ to + // re-try again after a little bit. However, we don't want to + // loop retrying forever if we can't re-enable accepts. Just + // abort the entire program in this state; things are really bad + // and restarting the entire server is probably the best remedy. + LOG(ERROR) + << "failed to re-enable AsyncServerSocket accepts after backoff; " + << "crashing now"; + abort(); + } + } + if (connectionEventCallback_) { + connectionEventCallback_->onBackoffEnded(); + } +} + +} // namespace folly diff --git a/android/third-party/Folly/CMakeLists.txt b/android/third-party/Folly/CMakeLists.txt index ee9c054ea..67c425a2e 100644 --- a/android/third-party/Folly/CMakeLists.txt +++ b/android/third-party/Folly/CMakeLists.txt @@ -3,10 +3,11 @@ cmake_minimum_required (VERSION 3.6.0) PROJECT(folly CXX) enable_language(CXX) set(PACKAGE_NAME folly) +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_EXTENSIONS OFF) set(FOLLY_DIR ${PROJECT_SOURCE_DIR}/folly) - list(APPEND dir_list ./) list(APPEND dir_list ${FOLLY_DIR}/lang) list(APPEND dir_list ${FOLLY_DIR}/hash/) @@ -15,14 +16,21 @@ list(APPEND dir_list ${FOLLY_DIR}/memory/detail) set(BOOST_DIR ../boost/boost_1_63_0/) set(GLOG_DIR ../glog/) +set(OPENSSL_DIR ../OpenSSL/openssl-android-1.0.0/) +set(LIBEVENT_DIR ../LibEvent/libevent-release-2.1.9/) set(DOUBLECONVERSION_DIR ../double-conversion/double-conversion-3.0.0/) list(APPEND dir_list ${BOOST_DIR}) list(APPEND dir_list ${BOOST_DIR}/../) +list(APPEND dir_list ${LIBEVENT_DIR}/) +list(APPEND dir_list ${LIBEVENT_DIR}/include/) +list(APPEND dir_list ${OPENSSL_DIR}) include_directories(${dir_list}) add_compile_options( + -DFOLLY_HAVE_CLOCK_GETTIME=1 + -DFOLLY_HAVE_PTHREAD=1 -DFOLLY_NO_CONFIG=1 -DFOLLY_HAVE_MEMRCHR -DFOLLY_MOBILE=1 @@ -31,7 +39,6 @@ add_compile_options( -DFOLLY_HAVE_PREADV=0 -frtti -fexceptions - -std=c++14 -Wno-error -Wno-unused-local-typedefs -Wno-unused-variable @@ -40,8 +47,49 @@ add_compile_options( -Wno-return-type -Wno-tautological-constant-compare ) - -list(APPEND SRC_FILES ${FOLLY_DIR}/Executor.cpp + file(GLOB SRC_FILES ${FOLLY_DIR}/portability/*.cpp + ${FOLLY_DIR}/io/async/ssl/*.cpp + ${FOLLY_DIR}/io/async/*.cpp + ${FOLLY_DIR}/detail/*.cpp + ${FOLLY_DIR}/synchronization/*.cpp + ${FOLLY_DIR}/lang/*.cpp + ${FOLLY_DIR}/hash/*.cpp + ${FOLLY_DIR}/hash/detail/*.cpp + ${FOLLY_DIR}/memory/*.cpp + ${FOLLY_DIR}/futures/*.cpp + ${FOLLY_DIR}/futures/detail/*.cpp + ${FOLLY_DIR}/experimental/hazptr/*.cpp + ${FOLLY_DIR}/executors/*.cpp + ${FOLLY_DIR}/concurrency/*.cpp + ${FOLLY_DIR}/ssl/*.cpp + ${FOLLY_DIR}/ssl/detail/*.cpp + ) +list(APPEND SRC_FILES ${FOLLY_DIR}/io/async/HHWheelTimer.cpp + ${FOLLY_DIR}/io/async/AsyncPipe.cpp + ${FOLLY_DIR}/io/async/AsyncTimeout.cpp + ${FOLLY_DIR}/io/async/EventBaseManager.cpp + ${FOLLY_DIR}/io/async/TimeoutManager.cpp + ${FOLLY_DIR}/io/async/AsyncSocketException.cpp + ${FOLLY_DIR}/io/async/Request.cpp + ${FOLLY_DIR}/io/async/EventBase.cpp + ${FOLLY_DIR}/io/async/EventHandler.cpp + ${FOLLY_DIR}/io/async/VirtualEventBase.cpp + ${FOLLY_DIR}/io/ShutdownSocketSet.cpp + ${FOLLY_DIR}/SharedMutex.cpp + ${FOLLY_DIR}/ExceptionWrapper.cpp + ${FOLLY_DIR}/system/ThreadName.cpp + ${FOLLY_DIR}/io/IOBuf.cpp + ${FOLLY_DIR}/io/IOBufQueue.cpp + ${FOLLY_DIR}/File.cpp + ${FOLLY_DIR}/Random.cpp + ${FOLLY_DIR}/Singleton.cpp + ${FOLLY_DIR}/IPAddress.cpp + ${FOLLY_DIR}/IPAddressV4.cpp + ${FOLLY_DIR}/IPAddressV6.cpp + ${FOLLY_DIR}/MacAddress.cpp + ${FOLLY_DIR}/SocketAddress.cpp + ${FOLLY_DIR}/Executor.cpp + ${FOLLY_DIR}/FileUtil.cpp ${FOLLY_DIR}/lang/ColdClass.cpp ${FOLLY_DIR}/lang/Assume.cpp ${FOLLY_DIR}/json.cpp @@ -55,6 +103,7 @@ list(APPEND SRC_FILES ${FOLLY_DIR}/Executor.cpp ${FOLLY_DIR}/json_pointer.cpp ${FOLLY_DIR}/FormatArg.cpp ${FOLLY_DIR}/Format.cpp + ${FOLLY_DIR}/memory/detail/MallocImpl.cpp ) add_library(${PACKAGE_NAME} SHARED ${SRC_FILES}) @@ -63,13 +112,18 @@ set(build_DIR ${CMAKE_SOURCE_DIR}/build) set(libglog_build_DIR ${build_DIR}/libglog/${ANDROID_ABI}) set(doubleconversion_build_DIR ${build_DIR}/doubleconversion/${ANDROID_ABI}) +set(libevent_build_DIR ${build_DIR}/libevent/${ANDROID_ABI}) + file(MAKE_DIRECTORY ${build_DIR}) add_subdirectory(${GLOG_DIR} ${libglog_build_DIR}) add_subdirectory(${DOUBLECONVERSION_DIR} ${doubleconversion_build_DIR}) - +add_subdirectory(${LIBEVENT_DIR} ${libevent_build_DIR}) target_include_directories(${PACKAGE_NAME} PRIVATE + ${OPENSSL_DIR}/jni/openssl-android/ + ${OPENSSL_DIR}/jni/openssl-android/include + ${OPENSSL_DIR}/jni/openssl-android/include/openssl ${BOOST_DIR} ${BOOST_DIR}/../ ${GLOG_DIR}/../ @@ -77,5 +131,10 @@ target_include_directories(${PACKAGE_NAME} PRIVATE ${DOUBLECONVERSION_DIR}) +set(LIBEXTRA_PATH /Users/prit91/LocalDevTesting/sonar-upstream-proper/Sonar/android/build/third-party-ndk/LibEvent/.externalNativeBuild/cmake/debug/${ANDROID_ABI}/lib) +set(OPENSSL_LINK_DIRECTORIES ${PROJECT_SOURCE_DIR}/../OpenSSL/libs/${ANDROID_ABI}/) +find_path(OPENSSL_LIBRARY libssl.so HINTS ${OPENSSL_LINK_DIRECTORIES}) + install(TARGETS ${PACKAGE_NAME} DESTINATION ./build/) -target_link_libraries(${PACKAGE_NAME} glog double-conversion) + +target_link_libraries(${PACKAGE_NAME} glog double-conversion ${OPENSSL_LINK_DIRECTORIES}/libssl.so ${OPENSSL_LINK_DIRECTORIES}/libcrypto.so event event_extra event_core) diff --git a/android/third-party/Folly/build.gradle b/android/third-party/Folly/build.gradle index 5593f5fac..59d7fd6f0 100644 --- a/android/third-party/Folly/build.gradle +++ b/android/third-party/Folly/build.gradle @@ -9,7 +9,7 @@ android { targetSdkVersion rootProject.targetSdkVersion buildConfigField "boolean", "IS_INTERNAL_BUILD", 'true' ndk { - abiFilters 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a' + abiFilters 'arm64-v8a', 'x86', 'armeabi-v7a', 'armeabi' } externalNativeBuild { @@ -34,6 +34,9 @@ android { dependencies { implementation project(':glog') + //implementation project(':libevent') implementation project(':doubleconversion') + implementation project(':openssl') + } } diff --git a/android/third-party/LibEvent/ApplicationManifest.xml b/android/third-party/LibEvent/ApplicationManifest.xml new file mode 100644 index 000000000..07b394959 --- /dev/null +++ b/android/third-party/LibEvent/ApplicationManifest.xml @@ -0,0 +1,4 @@ + + + diff --git a/android/third-party/LibEvent/CMakeLists.txt b/android/third-party/LibEvent/CMakeLists.txt new file mode 100644 index 000000000..1a8626416 --- /dev/null +++ b/android/third-party/LibEvent/CMakeLists.txt @@ -0,0 +1,37 @@ +cmake_minimum_required (VERSION 3.6.0) + +PROJECT(libevent C) +enable_language(C) +set(PACKAGE_NAME libevent) +set(LIBEVENT_DIR libevent-release-2.1.8-stable) +list(APPEND DIR_LIST ${LIBEVENT_DIR}/) +list(APPEND DIR_LIST ${LIBEVENT_DIR}/include) +include_directories(${DIR_LIST}) +list(APPEND SRCFILES ${LIBEVENT_DIR}/event.c + ${LIBEVENT_DIR}/buffer.c + ${LIBEVENT_DIR}/bufferevent.c + ${LIBEVENT_DIR}/bufferevent_filter.c + ${LIBEVENT_DIR}/bufferevent_ratelim.c + ${LIBEVENT_DIR}/bufferevent_sock.c + ${LIBEVENT_DIR}/epoll.c + ${LIBEVENT_DIR}/epoll_sub.c + ${LIBEVENT_DIR}/evdns.c + ${LIBEVENT_DIR}/event_tagging.c + ${LIBEVENT_DIR}/evmap.c + ${LIBEVENT_DIR}/evrpc.c + ${LIBEVENT_DIR}/evthread.c + ${LIBEVENT_DIR}/evthread_pthread.c + ${LIBEVENT_DIR}/evutil.c + ${LIBEVENT_DIR}/evutil_rand.c + ${LIBEVENT_DIR}/http.c + ${LIBEVENT_DIR}/listener.c + ${LIBEVENT_DIR}/log.c + ${LIBEVENT_DIR}/poll.c + ${LIBEVENT_DIR}/select.c + ${LIBEVENT_DIR}/signal.c + ${LIBEVENT_DIR}/strlcpy.c +) + +add_library(${PACKAGE_NAME} SHARED ${SRCFILES}) +install(TARGETS ${PACKAGE_NAME} DESTINATION ./build/) +target_link_libraries(${PACKAGE_NAME}) diff --git a/android/third-party/LibEvent/build.gradle b/android/third-party/LibEvent/build.gradle new file mode 100644 index 000000000..252f28ca8 --- /dev/null +++ b/android/third-party/LibEvent/build.gradle @@ -0,0 +1,33 @@ +apply plugin: 'com.android.library' + +android { + compileSdkVersion rootProject.compileSdkVersion + buildToolsVersion rootProject.buildToolsVersion + + defaultConfig { + minSdkVersion rootProject.minSdkVersion + targetSdkVersion rootProject.targetSdkVersion + ndk { + abiFilters 'arm64-v8a', 'x86', 'armeabi-v7a', 'armeabi' + } + + externalNativeBuild { + cmake { + arguments '-DANDROID_TOOLCHAIN=clang' + } + } + } + lintOptions { + abortOnError false + } + sourceSets { + main { + manifest.srcFile './ApplicationManifest.xml' + } + } + externalNativeBuild { + cmake { + path 'libevent-release-2.1.9/CMakeLists.txt' + } + } +} diff --git a/android/third-party/LibEvent/event-config.h b/android/third-party/LibEvent/event-config.h new file mode 100644 index 000000000..470a41728 --- /dev/null +++ b/android/third-party/LibEvent/event-config.h @@ -0,0 +1,365 @@ +/* event2/event-config.h + * + * This file was generated by autoconf when libevent was built, and post- + * processed by Libevent so that its macros would have a uniform prefix. + * + * DO NOT EDIT THIS FILE. + * + * Do not rely on macros in this file existing in later versions. + */ +#ifndef EVENT_CONFIG_H__ +#define EVENT_CONFIG_H__ +/* config.h. Generated by configure. */ +/* config.h.in. Generated from configure.in by autoheader. */ + +/* Define if libevent should not allow replacing the mm functions */ +/* #undef EVENT__DISABLE_MM_REPLACEMENT */ + +/* Define if libevent should not be compiled with thread support */ +/* #undef EVENT__DISABLE_THREAD_SUPPORT */ + +/* Define if clock_gettime is available in libc */ +/* #undef _EVENT_DNS_USE_CPU_CLOCK_FOR_ID */ + +/* Define is no secure id variant is available */ +/* #define _EVENT_DNS_USE_GETTIMEOFDAY_FOR_ID 1 */ +#define EVENT_DNS_USE_FTIME_FOR_ID_ 1 + +/* Define to 1 if you have the header file. */ +/* #undef EVENT__HAVE_ARPA_INET_H */ + +/* Define to 1 if you have the `clock_gettime' function. */ +/* #undef EVENT__HAVE_CLOCK_GETTIME */ + +/* Define if /dev/poll is available */ +/* #undef EVENT__HAVE_DEVPOLL */ + +/* Define to 1 if you have the header file. */ +/* #undef EVENT__HAVE_DLFCN_H */ + +/* Define if your system supports the epoll system calls */ +/* #undef EVENT__HAVE_EPOLL */ + +/* Define to 1 if you have the `epoll_ctl' function. */ +/* #undef EVENT__HAVE_EPOLL_CTL */ + +/* Define to 1 if you have the `eventfd' function. */ +/* #undef EVENT__HAVE_EVENTFD */ + +/* Define if your system supports event ports */ +/* #undef EVENT__HAVE_EVENT_PORTS */ + +/* Define to 1 if you have the `fcntl' function. */ +/* #undef EVENT__HAVE_FCNTL */ + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_FCNTL_H 1 + +/* Define to 1 if you have the `getaddrinfo' function. */ +#define EVENT__HAVE_GETADDRINFO 1 + +/* Define to 1 if you have the `getnameinfo' function. */ +#define EVENT__HAVE_GETNAMEINFO 1 + +/* Define to 1 if you have the `getprotobynumber' function. */ +#define EVENT__HAVE_GETPROTOBYNUMBER 1 + +/* Define to 1 if you have the `getservbyname' function. */ +#define EVENT__HAVE_GETSERVBYNAME 1 + +/* Define to 1 if you have the `gettimeofday' function. */ +/* #define EVENT__HAVE_GETTIMEOFDAY 1 */ + +/* Define to 1 if you have the `inet_ntop' function. */ +/* #undef EVENT__HAVE_INET_NTOP */ + +/* Define to 1 if you have the `inet_pton' function. */ +/* #undef EVENT__HAVE_INET_PTON */ + +/* Define to 1 if you have the header file. */ +/* #define EVENT__HAVE_INTTYPES_H 1 */ + +/* Define to 1 if you have the `kqueue' function. */ +/* #undef EVENT__HAVE_KQUEUE */ + +/* Define if the system has zlib */ +/* #undef EVENT__HAVE_LIBZ */ + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_MEMORY_H 1 + +/* Define to 1 if you have the `mmap' function. */ +/* #undef EVENT__HAVE_MMAP */ + +/* Define to 1 if you have the header file. */ +/* #undef EVENT__HAVE_NETINET_IN6_H */ + +/* Define to 1 if you have the header file. */ +/* #undef EVENT__HAVE_NETINET_IN_H */ + +/* Define to 1 if you have the `pipe' function. */ +/* #undef EVENT__HAVE_PIPE */ + +/* Define to 1 if you have the `poll' function. */ +/* #undef EVENT__HAVE_POLL */ + +/* Define to 1 if you have the header file. */ +/* #undef EVENT__HAVE_POLL_H */ + +/* Define to 1 if you have the `port_create' function. */ +/* #undef EVENT__HAVE_PORT_CREATE */ + +/* Define to 1 if you have the header file. */ +/* #undef EVENT__HAVE_PORT_H */ + +/* Define if you have POSIX threads libraries and header files. */ +/* #undef EVENT__HAVE_PTHREAD */ + +/* Define if we have pthreads on this system */ +/* #undef EVENT__HAVE_PTHREADS */ + +/* Define to 1 if the system has the type `sa_family_t'. */ +/* #undef EVENT__HAVE_SA_FAMILY_T */ + +/* Define to 1 if you have the `select' function. */ +/* #undef EVENT__HAVE_SELECT */ + +/* Define to 1 if you have the `sendfile' function. */ +/* #undef EVENT__HAVE_SENDFILE */ + +/* Define if F_SETFD is defined in */ +/* #undef EVENT__HAVE_SETFD */ + +/* Define to 1 if you have the `sigaction' function. */ +/* #undef EVENT__HAVE_SIGACTION */ + +/* Define to 1 if you have the `signal' function. */ +#define EVENT__HAVE_SIGNAL 1 + +/* Define to 1 if you have the `splice' function. */ +/* #undef EVENT__HAVE_SPLICE */ + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_STDARG_H 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_STDDEF_H 1 + +/* Define to 1 if you have the header file. */ +/* #define EVENT__HAVE_STDINT_H 1 */ + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_STDLIB_H 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_STRING_H 1 + +/* Define to 1 if you have the `strlcpy' function. */ +/* #undef EVENT__HAVE_STRLCPY */ + +/* Define to 1 if you have the `strsep' function. */ +/* #undef EVENT__HAVE_STRSEP */ + +/* Define to 1 if you have the `strtok_r' function. */ +/* #undef EVENT__HAVE_STRTOK_R */ + +/* Define to 1 if you have the `strtoll' function. */ +/* #define EVENT__HAVE_STRTOLL 1 */ + +#define EVENT__HAVE_STRUCT_ADDRINFO 1 + +/* Define to 1 if the system has the type `struct in6_addr'. */ +#define EVENT__HAVE_STRUCT_IN6_ADDR 1 + +/* Define to 1 if `s6_addr16' is member of `struct in6_addr'. */ +#define EVENT__HAVE_STRUCT_IN6_ADDR_S6_ADDR16 1 + +/* Define to 1 if `s6_addr32' is member of `struct in6_addr'. */ +#define EVENT__HAVE_STRUCT_IN6_ADDR_S6_ADDR32 1 + +/* Define to 1 if the system has the type `struct sockaddr_in6'. */ +#define EVENT__HAVE_STRUCT_SOCKADDR_IN6 1 + +/* Define to 1 if `sin6_len' is member of `struct sockaddr_in6'. */ +/* #undef EVENT__HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN */ + +/* Define to 1 if `sin_len' is member of `struct sockaddr_in'. */ +/* #undef EVENT__HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ + +/* Define to 1 if the system has the type `struct sockaddr_storage'. */ +#define EVENT__HAVE_STRUCT_SOCKADDR_STORAGE 1 + +/* Define to 1 if you have the header file. */ +/* #undef EVENT__HAVE_SYS_DEVPOLL_H */ + +/* Define to 1 if you have the header file. */ +/* #undef EVENT__HAVE_SYS_EPOLL_H */ + +/* Define to 1 if you have the header file. */ +/* #undef EVENT__HAVE_SYS_EVENTFD_H */ + +/* Define to 1 if you have the header file. */ +/* #undef EVENT__HAVE_SYS_EVENT_H */ + +/* Define to 1 if you have the header file. */ +/* #undef EVENT__HAVE_SYS_IOCTL_H */ + +/* Define to 1 if you have the header file. */ +/* #undef EVENT__HAVE_SYS_MMAN_H */ + +/* Define to 1 if you have the header file. */ +/* #define EVENT__HAVE_SYS_PARAM_H 1 */ + +/* Define to 1 if you have the header file. */ +/* #undef EVENT__HAVE_SYS_QUEUE_H */ + +/* Define to 1 if you have the header file. */ +/* #undef EVENT__HAVE_SYS_SELECT_H */ + +/* Define to 1 if you have the header file. */ +/* #undef EVENT__HAVE_SYS_SENDFILE_H */ + +/* Define to 1 if you have the header file. */ +/* #undef EVENT__HAVE_SYS_SOCKET_H */ + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +/* #define EVENT__HAVE_SYS_TIME_H 1 */ + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_SYS_TYPES_H 0 + +/* Define to 1 if you have the header file. */ +/* #undef EVENT__HAVE_SYS_UIO_H */ + +/* Define if TAILQ_FOREACH is defined in */ +/* #undef EVENT__HAVE_TAILQFOREACH */ + +/* Define if timeradd is defined in */ +/* #undef EVENT__HAVE_TIMERADD */ + +/* Define if timerclear is defined in */ +#define EVENT__HAVE_TIMERCLEAR 1 + +/* Define if timercmp is defined in */ +#define EVENT__HAVE_TIMERCMP 1 + +/* Define if timerisset is defined in */ +#define EVENT__HAVE_TIMERISSET 1 + +/* Define to 1 if the system has the type `uint16_t'. */ +/* #define EVENT__HAVE_UINT16_T 1 */ + +/* Define to 1 if the system has the type `uint32_t'. */ +/* #define EVENT__HAVE_UINT32_T 1 */ + +/* Define to 1 if the system has the type `uint64_t'. */ +/* #define EVENT__HAVE_UINT64_T 1 */ + +/* Define to 1 if the system has the type `uint8_t'. */ +/* #define EVENT__HAVE_UINT8_T 1 */ + +/* Define to 1 if you have the header file. */ +/* #define EVENT__HAVE_UNISTD_H 1 */ + +/* Define to 1 if you have the `vasprintf' function. */ +/* #undef EVENT__HAVE_VASPRINTF */ + +/* Define if kqueue works correctly with pipes */ +/* #undef EVENT__HAVE_WORKING_KQUEUE */ + +/* Numeric representation of the version */ +#define EVENT__NUMERIC_VERSION 0x02020001 + +/* Name of package */ +#define EVENT__PACKAGE "libevent" + +/* Define to the address where bug reports for this package should be sent. */ +#define EVENT__PACKAGE_BUGREPORT "" + +/* Define to the full name of this package. */ +#define EVENT__PACKAGE_NAME "" + +/* Define to the full name and version of this package. */ +#define EVENT__PACKAGE_STRING "" + +/* Define to the one symbol short name of this package. */ +#define EVENT__PACKAGE_TARNAME "" + +/* Define to the version of this package. */ +#define EVENT__PACKAGE_VERSION "" + +/* Define to necessary symbol if this constant uses a non-standard name on + your system. */ +/* #undef EVENT__PTHREAD_CREATE_JOINABLE */ + +/* The size of a `int', as computed by sizeof. */ +#define EVENT__SIZEOF_INT 4 + +/* The size of a `long', as computed by sizeof. */ +#define EVENT__SIZEOF_LONG 4 + +/* The size of a `long long', as computed by sizeof. */ +#define EVENT__SIZEOF_LONG_LONG 8 + +/* The size of a `short', as computed by sizeof. */ +#define EVENT__SIZEOF_SHORT 2 + +/* The size of `size_t', as computed by sizeof. */ +#ifdef _WIN64 +#define EVENT__SIZEOF_SIZE_T 8 +#else +#define EVENT__SIZEOF_SIZE_T 4 +#endif + +/* The size of `void *', as computed by sizeof. */ +#ifdef _WIN64 +#define EVENT__SIZEOF_VOID_P 8 +#else +#define EVENT__SIZEOF_VOID_P 4 +#endif + +/* The size of `time_t`, as computed by sizeof. */ +#ifdef _WIN64 +#define EVENT__SIZEOF_TIME_T 8 +#else +#define EVENT__SIZEOF_TIME_T 4 +#endif + +/* Define to 1 if you have the ANSI C header files. */ +#define EVENT__STDC_HEADERS 1 + +/* Define to 1 if you can safely include both and . */ +#define EVENT__TIME_WITH_SYS_TIME 1 + +/* Version number of package */ +#define EVENT__VERSION "2.2.0-alpha-dev" + +/* Define to appropriate substitue if compiler doesnt have __func__ */ +#define EVENT____func__ __FUNCTION__ + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef _EVENT___cplusplus +#define EVENT__inline __inline +#endif + +/* Define to `int' if does not define. */ +#undef EVENT__pid_t + +/* Define to `unsigned' if does not define. */ + #undef EVENT__size_t + +/* Define to unsigned int if you dont have it */ +#define EVENT__socklen_t unsigned int + +/* Define to `int' if does not define. */ +#define EVENT__ssize_t int +//SSIZE_T + +#endif diff --git a/android/third-party/OpenSSL/ApplicationManifest.xml b/android/third-party/OpenSSL/ApplicationManifest.xml new file mode 100644 index 000000000..60bb67e6a --- /dev/null +++ b/android/third-party/OpenSSL/ApplicationManifest.xml @@ -0,0 +1,4 @@ + + + diff --git a/android/third-party/OpenSSL/build.gradle b/android/third-party/OpenSSL/build.gradle new file mode 100644 index 000000000..5525de45d --- /dev/null +++ b/android/third-party/OpenSSL/build.gradle @@ -0,0 +1,35 @@ +apply plugin: 'com.android.library' + +android { + compileSdkVersion rootProject.compileSdkVersion + buildToolsVersion rootProject.buildToolsVersion + + defaultConfig { + minSdkVersion rootProject.minSdkVersion + targetSdkVersion rootProject.targetSdkVersion + ndk { + abiFilters 'arm64-v8a', 'x86', 'armeabi-v7a', 'armeabi' + } + + externalNativeBuild { + ndkBuild { + arguments "NDK_APPLICATION_MK:=$projectDir/jni/Application.mk", + "NDK_TOOLCHAIN_VERSION:=clang" + cppFlags "-std=c++11" + } + } + } + externalNativeBuild { + ndkBuild { + path "$projectDir/jni/Android.mk" + } + } + lintOptions { + abortOnError false + } + sourceSets { + main { + manifest.srcFile './ApplicationManifest.xml' + } + } +} diff --git a/android/third-party/OpenSSL/libs/arm64-v8a/libcrypto.so b/android/third-party/OpenSSL/libs/arm64-v8a/libcrypto.so new file mode 100755 index 000000000..3bcbfa949 Binary files /dev/null and b/android/third-party/OpenSSL/libs/arm64-v8a/libcrypto.so differ diff --git a/android/third-party/OpenSSL/libs/arm64-v8a/libssl.so b/android/third-party/OpenSSL/libs/arm64-v8a/libssl.so new file mode 100755 index 000000000..00e3f440e Binary files /dev/null and b/android/third-party/OpenSSL/libs/arm64-v8a/libssl.so differ diff --git a/android/third-party/OpenSSL/libs/arm64-v8a/openssl b/android/third-party/OpenSSL/libs/arm64-v8a/openssl new file mode 100755 index 000000000..a3b579292 Binary files /dev/null and b/android/third-party/OpenSSL/libs/arm64-v8a/openssl differ diff --git a/android/third-party/OpenSSL/libs/arm64-v8a/ssltest b/android/third-party/OpenSSL/libs/arm64-v8a/ssltest new file mode 100755 index 000000000..e5801732f Binary files /dev/null and b/android/third-party/OpenSSL/libs/arm64-v8a/ssltest differ diff --git a/android/third-party/OpenSSL/libs/armeabi-v7a/libcrypto.so b/android/third-party/OpenSSL/libs/armeabi-v7a/libcrypto.so new file mode 100755 index 000000000..615086aa6 Binary files /dev/null and b/android/third-party/OpenSSL/libs/armeabi-v7a/libcrypto.so differ diff --git a/android/third-party/OpenSSL/libs/armeabi-v7a/libssl.so b/android/third-party/OpenSSL/libs/armeabi-v7a/libssl.so new file mode 100755 index 000000000..532a8f63b Binary files /dev/null and b/android/third-party/OpenSSL/libs/armeabi-v7a/libssl.so differ diff --git a/android/third-party/OpenSSL/libs/armeabi-v7a/openssl b/android/third-party/OpenSSL/libs/armeabi-v7a/openssl new file mode 100755 index 000000000..ed0997f8d Binary files /dev/null and b/android/third-party/OpenSSL/libs/armeabi-v7a/openssl differ diff --git a/android/third-party/OpenSSL/libs/armeabi-v7a/ssltest b/android/third-party/OpenSSL/libs/armeabi-v7a/ssltest new file mode 100755 index 000000000..d80b72172 Binary files /dev/null and b/android/third-party/OpenSSL/libs/armeabi-v7a/ssltest differ diff --git a/android/third-party/OpenSSL/libs/armeabi/libcrypto.so b/android/third-party/OpenSSL/libs/armeabi/libcrypto.so new file mode 100755 index 000000000..0161f199c Binary files /dev/null and b/android/third-party/OpenSSL/libs/armeabi/libcrypto.so differ diff --git a/android/third-party/OpenSSL/libs/armeabi/libssl.so b/android/third-party/OpenSSL/libs/armeabi/libssl.so new file mode 100755 index 000000000..4589c7219 Binary files /dev/null and b/android/third-party/OpenSSL/libs/armeabi/libssl.so differ diff --git a/android/third-party/OpenSSL/libs/armeabi/openssl b/android/third-party/OpenSSL/libs/armeabi/openssl new file mode 100755 index 000000000..11dd86fdb Binary files /dev/null and b/android/third-party/OpenSSL/libs/armeabi/openssl differ diff --git a/android/third-party/OpenSSL/libs/armeabi/ssltest b/android/third-party/OpenSSL/libs/armeabi/ssltest new file mode 100755 index 000000000..be7838aba Binary files /dev/null and b/android/third-party/OpenSSL/libs/armeabi/ssltest differ diff --git a/android/third-party/OpenSSL/libs/x86/libcrypto.so b/android/third-party/OpenSSL/libs/x86/libcrypto.so new file mode 100755 index 000000000..4c42b213d Binary files /dev/null and b/android/third-party/OpenSSL/libs/x86/libcrypto.so differ diff --git a/android/third-party/OpenSSL/libs/x86/libssl.so b/android/third-party/OpenSSL/libs/x86/libssl.so new file mode 100755 index 000000000..2fd0bd9bb Binary files /dev/null and b/android/third-party/OpenSSL/libs/x86/libssl.so differ diff --git a/android/third-party/OpenSSL/libs/x86/openssl b/android/third-party/OpenSSL/libs/x86/openssl new file mode 100755 index 000000000..955536f66 Binary files /dev/null and b/android/third-party/OpenSSL/libs/x86/openssl differ diff --git a/android/third-party/OpenSSL/libs/x86/ssltest b/android/third-party/OpenSSL/libs/x86/ssltest new file mode 100755 index 000000000..c0f22b1c2 Binary files /dev/null and b/android/third-party/OpenSSL/libs/x86/ssltest differ diff --git a/android/third-party/RSocket/ApplicationManifest.xml b/android/third-party/RSocket/ApplicationManifest.xml new file mode 100644 index 000000000..4e5fbde5b --- /dev/null +++ b/android/third-party/RSocket/ApplicationManifest.xml @@ -0,0 +1,4 @@ + + + diff --git a/android/third-party/RSocket/CMakeLists.txt b/android/third-party/RSocket/CMakeLists.txt new file mode 100644 index 000000000..ba6ffeebc --- /dev/null +++ b/android/third-party/RSocket/CMakeLists.txt @@ -0,0 +1,92 @@ +cmake_minimum_required (VERSION 3.6.0) + +PROJECT(rsocket CXX) +set(PACKAGE_NAME rsocket) +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_EXTENSIONS OFF) + +set(third_party_ndk ${PROJECT_SOURCE_DIR}/../) +set(libfolly_DIR ${third_party_ndk}/folly/) +set(glog_DIR ${third_party_ndk}/glog) +set(BOOST_DIR ${third_party_ndk}/boost/boost_1_63_0/) +set(LIBEVENT_DIR ${third_party_ndk}/LibEvent/libevent-release-2.1.9/) +set(DOUBLECONVERSION_DIR ${third_party_ndk}/double-conversion/double-conversion-3.0.0/) +set(OPENSSL_DIR ${third_party_ndk}/OpenSSL/openssl-android-1.0.0/) + +set(RSOCKET_ROOT_DIR ${PROJECT_SOURCE_DIR}/rsocket-cpp-0.10.1) +set(RSOCKET_DIR ${PROJECT_SOURCE_DIR}/rsocket-cpp-0.10.1/rsocket) + +list(APPEND dir_list ${RSOCKET_ROOT_DIR}/) +list(APPEND dir_list ${RSOCKET_DIR}/) +list(APPEND dir_list ${RSOCKET_DIR}/framing) +list(APPEND dir_list ${RSOCKET_DIR}/internal) +list(APPEND dir_list ${RSOCKET_DIR}/statemachine) +list(APPEND dir_list ${RSOCKET_DIR}/transports) +list(APPEND dir_list ${RSOCKET_DIR}/transports/tcp) +list(APPEND dir_list ${RSOCKET_ROOT_DIR}/yarpl/flowable) +list(APPEND dir_list ${RSOCKET_ROOT_DIR}/yarpl/observable) +list(APPEND dir_list ${RSOCKET_ROOT_DIR}/yarpl/utils) + +file(GLOB SRC_FILES ${RSOCKET_DIR}/*.cpp + ${RSOCKET_DIR}/internal/*.cpp + ${RSOCKET_DIR}/framing/*.cpp + ${RSOCKET_DIR}/statemachine/*.cpp + ${RSOCKET_DIR}/transports/*.cpp + ${RSOCKET_DIR}/transports/tcp/*.cpp + ${RSOCKET_ROOT_DIR}/yarpl/observable/*.cpp + ${RSOCKET_ROOT_DIR}/yarpl/flowable/*.cpp + ${RSOCKET_ROOT_DIR}/yarpl/utils/*.cpp + ) + +include_directories(${dir_list}) + +add_compile_options( + -DFOLLY_HAVE_CLOCK_GETTIME=1 + -DFOLLY_HAVE_PTHREAD=1 + -DFOLLY_NO_CONFIG=1 + -DFOLLY_HAVE_MEMRCHR + -DFOLLY_MOBILE=1 + -DFOLLY_USE_LIBCPP=1 + -DFOLLY_HAVE_LIBJEMALLOC=0 + -DFOLLY_HAVE_PREADV=0 + -frtti + -fexceptions + -Wno-error + -Wno-unused-local-typedefs + -Wno-unused-variable + -Wno-sign-compare + -Wno-comment + -Wno-return-type + -Wno-tautological-constant-compare + ) + +add_library(${PACKAGE_NAME} SHARED ${SRC_FILES}) + +set(build_DIR ${CMAKE_SOURCE_DIR}/build) +set(libfolly_build_DIR ${build_DIR}/libfolly/${ANDROID_ABI}) + +file(MAKE_DIRECTORY ${build_DIR}) + +add_subdirectory(${libfolly_DIR} ${libfolly_build_DIR}) + +target_include_directories(${PACKAGE_NAME} PRIVATE + ${libfolly_DIR} + ${BOOST_DIR} + ${BOOST_DIR}/../ + ${LIBEVENT_DIR}/ + ${LIBEVENT_DIR}/include/ + ${LIBEVENT_DIR}/include/event2 + ${OPENSSL_DIR}/jni/openssl-android/ + ${OPENSSL_DIR}/jni/openssl-android/include + ${OPENSSL_DIR}/jni/openssl-android/include/openssl + ${glog_DIR} + ${glog_DIR}/../ + ${glog_DIR}/glog-0.3.5/src/ + ) + + +set(OPENSSL_LINK_DIRECTORIES ${third_party_ndk}/OpenSSL/libs/${ANDROID_ABI}/) + +find_path(OPENSSL_LIBRARY libssl.so HINTS ${OPENSSL_LINK_DIRECTORIES}) + +target_link_libraries(${PACKAGE_NAME} folly glog double-conversion log event ${OPENSSL_LINK_DIRECTORIES}/libssl.so ${OPENSSL_LINK_DIRECTORIES}/libcrypto.so) diff --git a/android/third-party/RSocket/build.gradle b/android/third-party/RSocket/build.gradle new file mode 100644 index 000000000..21f83b739 --- /dev/null +++ b/android/third-party/RSocket/build.gradle @@ -0,0 +1,35 @@ +apply plugin: 'com.android.library' + +android { + compileSdkVersion rootProject.compileSdkVersion + buildToolsVersion rootProject.buildToolsVersion + + defaultConfig { + minSdkVersion rootProject.minSdkVersion + targetSdkVersion rootProject.targetSdkVersion + ndk { + abiFilters 'arm64-v8a', 'x86', 'armeabi-v7a' + } + + externalNativeBuild { + cmake { + arguments '-DANDROID_TOOLCHAIN=clang' + } + } + } + lintOptions { + abortOnError false + } + sourceSets { + main { + manifest.srcFile './ApplicationManifest.xml' + } + } + externalNativeBuild { + cmake { + path './CMakeLists.txt' + //'rsocket-cpp-0.10.0/CMakeLists.txt' + } + } +} +//'x86', 'x86_64', 'armeabi-v7a', diff --git a/android/third-party/glog/CMakeLists.txt b/android/third-party/glog/CMakeLists.txt index 346f24852..63cbf972d 100644 --- a/android/third-party/glog/CMakeLists.txt +++ b/android/third-party/glog/CMakeLists.txt @@ -13,7 +13,6 @@ list(APPEND dir_list ${glog_DIR}/src) list(APPEND dir_list ${glog_DIR}/glog) list(APPEND dir_list ${glog_DIR}/base) -message(STATUS "dir_list = " ${dir_list}) include_directories(${dir_list}) add_compile_options( diff --git a/build.gradle b/build.gradle index 864b5d410..9f4da8f50 100644 --- a/build.gradle +++ b/build.gradle @@ -16,7 +16,10 @@ buildscript { subprojects { repositories { google() + mavenLocal() + mavenCentral() jcenter() + maven { url "https://oss.sonatype.org/content/repositories/snapshots/" } } } @@ -49,12 +52,12 @@ ext.deps = [ jsr305 : 'com.google.code.findbugs:jsr305:3.0.1', inferAnnotations : 'com.facebook.infer.annotation:infer-annotation:0.11.2', // Litho - lithoAnnotations : 'com.facebook.litho:litho-annotations:0.15.0', - lithoCore : 'com.facebook.litho:litho-core:0.15.0', - lithoWidget : 'com.facebook.litho:litho-widget:0.15.0', - lithoProcessor : 'com.facebook.litho:litho-processor:0.15.0', - lithoFresco : 'com.facebook.litho:litho-fresco:0.15.0', - lithoTesting : 'com.facebook.litho:litho-testing:0.15.0', + lithoAnnotations : 'com.facebook.litho:litho-annotations:0.15.1-SNAPSHOT', + lithoCore : 'com.facebook.litho:litho-core:0.15.1-SNAPSHOT', + lithoWidget : 'com.facebook.litho:litho-widget:0.15.1-SNAPSHOT', + lithoProcessor : 'com.facebook.litho:litho-processor:0.15.1-SNAPSHOT', + lithoFresco : 'com.facebook.litho:litho-fresco:0.15.1-SNAPSHOT', + lithoTesting : 'com.facebook.litho:litho-testing:0.15.1-SNAPSHOT', // Debugging and testing guava : 'com.google.guava:guava:20.0', robolectric : 'org.robolectric:robolectric:3.0', 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 new file mode 100644 index 000000000..43eee3b67 --- /dev/null +++ b/libs/fbjni/BUCK @@ -0,0 +1,81 @@ +load("//build_defs:fb_xplat_cxx_library.bzl", "fb_xplat_cxx_library") +load("@xplat//build_defs:fb_java_library.bzl", "fb_java_library") + +ANNOTATIONS_SRCS = [ + "java/com/facebook/jni/annotations/*.java", +] + +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..1f71b8a70 --- /dev/null +++ b/libs/fbjni/CMakeLists.txt @@ -0,0 +1,46 @@ +# +# 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(PACKAGE_NAME "sonarfb") +project(${PACKAGE_NAME} CXX) + +set(CMAKE_VERBOSE_MAKEFILE on) + +add_compile_options( + -fno-omit-frame-pointer + -fexceptions + -O3 + -Wall + -std=c++11 + -frtti + -ffunction-sections + -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) +list(APPEND FBJNI_HDRS ${FBJNI_CXX}/../../jni-hack) + +include_directories(${FBJNI_HDRS}) + +file(GLOB FBJNI_SRC + ${FBJNI_CXX}/fbjni/*.cpp + ${FBJNI_CXX}/fbjni/detail/*.cpp + ${FBJNI_CXX}/lyra/*.cpp + ) + +add_library(${PACKAGE_NAME} SHARED + ${FBJNI_SRC}) + +target_include_directories(${PACKAGE_NAME} PRIVATE ${FBJNI_HDRS}) + +target_link_libraries(${PACKAGE_NAME} android log) diff --git a/libs/fbjni/build.gradle b/libs/fbjni/build.gradle index f4aa91810..662b90ef9 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 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a' + abiFilters 'arm64-v8a', 'x86', 'armeabi-v7a' } 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' + implementation deps.soloader } 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 b41a9d309..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 57182214b..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 9ab0071e8..3628639bd 100644 --- a/libs/fbjni/src/main/cpp/include/fb/fbjni/Boxed.h +++ b/libs/fbjni/cxx/fbjni/detail/Boxed.h @@ -1,10 +1,19 @@ -/* - * 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" @@ -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,6 +65,10 @@ 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 71% rename from libs/fbjni/src/main/cpp/include/fb/fbjni/Common.h rename to libs/fbjni/cxx/fbjni/detail/Common.h index ee00299ce..b207cee84 100644 --- a/libs/fbjni/src/main/cpp/include/fb/fbjni/Common.h +++ b/libs/fbjni/cxx/fbjni/detail/Common.h @@ -1,10 +1,19 @@ -/* - * 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 * * Defining the stuff that don't deserve headers of their own... @@ -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 aeda7d7ac..6d6db77e6 100644 --- a/libs/fbjni/src/main/cpp/include/fb/fbjni/CoreClasses-inl.h +++ b/libs/fbjni/cxx/fbjni/detail/CoreClasses-inl.h @@ -1,10 +1,19 @@ -/* - * 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 @@ -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 e73f9f5e7..47c551a51 100644 --- a/libs/fbjni/src/main/cpp/include/fb/fbjni/CoreClasses.h +++ b/libs/fbjni/cxx/fbjni/detail/CoreClasses.h @@ -1,10 +1,19 @@ -/* - * 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 /** @file CoreClasses.h @@ -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 4d56c2960..e48fcbe89 100644 --- a/libs/fbjni/src/main/cpp/include/fb/Environment.h +++ b/libs/fbjni/cxx/fbjni/detail/Environment.h @@ -1,33 +1,87 @@ -/* - * 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 #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 { + public: + static auto constexpr kJavaDescriptor = "Ljava/lang/RuntimeException;"; + + static local_ref create(const char* str) { + return newInstance(make_jstring(str)); + } + + static local_ref create() { + return newInstance(); + } +}; + +class JIOException : public JavaClass { + public: + static auto constexpr kJavaDescriptor = "Ljava/io/IOException;"; + + static local_ref create(const char* str) { + return newInstance(make_jstring(str)); + } +}; + +class JOutOfMemoryError : public JavaClass { + public: + static auto constexpr kJavaDescriptor = "Ljava/lang/OutOfMemoryError;"; + + static local_ref create(const char* str) { + return newInstance(make_jstring(str)); + } +}; + +class JArrayIndexOutOfBoundsException : public JavaClass { + public: + static auto constexpr kJavaDescriptor = "Ljava/lang/ArrayIndexOutOfBoundsException;"; + + static local_ref create(const char* str) { + return newInstance(make_jstring(str)); + } +}; + +class JUnknownCppException : public JavaClass { + public: + static auto constexpr kJavaDescriptor = "Lcom/facebook/jni/UnknownCppException;"; + + static local_ref create() { + return newInstance(); + } + + static local_ref create(const char* str) { + return newInstance(make_jstring(str)); + } +}; + +class JCppSystemErrorException : public JavaClass { + public: + static auto constexpr kJavaDescriptor = "Lcom/facebook/jni/CppSystemErrorException;"; + + static local_ref 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 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(), + make_jstring(msg).release()); + throwNewJavaException(throwable.get()); +} + +// jthrowable ////////////////////////////////////////////////////////////////////////////////////// + +local_ref JThrowable::initCause(alias_ref cause) { + static auto meth = javaClassStatic()->getMethod)>("initCause"); + return meth(self(), cause); +} + +auto JThrowable::getStackTrace() -> local_ref { + static auto meth = javaClassStatic()->getMethod("getStackTrace"); + return meth(self()); +} + +void JThrowable::setStackTrace(alias_ref stack) { + static auto meth = javaClassStatic()->getMethod)>("setStackTrace"); + return meth(self(), stack); +} + +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); +} + +std::string JStackTraceElement::getClassName() const { + static auto meth = javaClassStatic()->getMethod()>("getClassName"); + return meth(self())->toStdString(); +} + +std::string JStackTraceElement::getMethodName() const { + static auto meth = javaClassStatic()->getMethod()>("getMethodName"); + return meth(self())->toStdString(); +} + +std::string JStackTraceElement::getFileName() const { + static auto meth = javaClassStatic()->getMethod()>("getFileName"); + return meth(self())->toStdString(); +} + +int JStackTraceElement::getLineNumber() const { + static auto meth = javaClassStatic()->getMethod("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& 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 createJStackTraceElement(const lyra::StackTraceElement& cpp) { + return JStackTraceElement::create( + "|lyra|{" + cpp.libraryName() + "}", cpp.functionName(), cpp.buildId(), cpp.libraryOffset()); +} + +void addCppStacktraceToJavaException(alias_ref 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 convertCppExceptionToJavaException(std::exception_ptr ptr) { + FBJNI_ASSERT(ptr); + local_ref 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 getJavaExceptionForCppBackTrace() { + return getJavaExceptionForCppBackTrace(nullptr); +} + +local_ref getJavaExceptionForCppBackTrace(const char* msg) { + local_ref 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 getJavaExceptionForCppException(std::exception_ptr ptr) { + FBJNI_ASSERT(ptr); + local_ref 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 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 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_); +} + +}} diff --git a/libs/fbjni/src/main/cpp/include/fb/fbjni/Exceptions.h b/libs/fbjni/cxx/fbjni/detail/Exceptions.h similarity index 70% rename from libs/fbjni/src/main/cpp/include/fb/fbjni/Exceptions.h rename to libs/fbjni/cxx/fbjni/detail/Exceptions.h index 663667aea..2073beba1 100644 --- a/libs/fbjni/src/main/cpp/include/fb/fbjni/Exceptions.h +++ b/libs/fbjni/cxx/fbjni/detail/Exceptions.h @@ -1,10 +1,19 @@ -/* - * 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 Exceptions.h * @@ -24,12 +33,15 @@ #include -#include - #include "Common.h" #include "References.h" #include "CoreClasses.h" +#if defined(__ANDROID__) && defined(__ARM_ARCH_5TE__) && !defined(FBJNI_NO_EXCEPTION_PTR) +// ARMv5 NDK does not support exception_ptr so we cannot use that when building for it. +#define FBJNI_NO_EXCEPTION_PTR +#endif + namespace facebook { namespace jni { @@ -58,7 +70,7 @@ class JCppException : public JavaClass { * * Note: the what() method of this class is not thread-safe (t6900503). */ -class FBEXPORT JniException : public std::exception { +class JniException : public std::exception { public: JniException(); ~JniException(); @@ -106,11 +118,23 @@ template } // 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. -FBEXPORT void translatePendingCppExceptionToJavaException() noexcept; +// be thrown, it aborts the program. +void translatePendingCppExceptionToJavaException(); + +#ifndef FBJNI_NO_EXCEPTION_PTR +local_ref 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 getJavaExceptionForCppBackTrace(); + +local_ref getJavaExceptionForCppBackTrace(const char* msg); // For convenience, some exception names in java.lang are available here. - const char* const gJavaLangIllegalArgumentException = "java/lang/IllegalArgumentException"; }} diff --git a/libs/fbjni/cxx/fbjni/detail/Hybrid.cpp b/libs/fbjni/cxx/fbjni/detail/Hybrid.cpp new file mode 100644 index 000000000..3ba4f4d0e --- /dev/null +++ b/libs/fbjni/cxx/fbjni/detail/Hybrid.cpp @@ -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 + +namespace facebook { +namespace jni { + +namespace detail { + +local_ref HybridData::create() { + return newInstance(); +} + +} + +namespace { +void deleteNative(alias_ref, jlong ptr) { + delete reinterpret_cast(ptr); +} +} + +void HybridDataOnLoad() { + registerNatives("com/facebook/jni/HybridData$Destructor", { + makeNativeMethod("deleteNative", deleteNative), + }); +} + +}} diff --git a/libs/fbjni/src/main/cpp/include/fb/fbjni/Hybrid.h b/libs/fbjni/cxx/fbjni/detail/Hybrid.h similarity index 67% rename from libs/fbjni/src/main/cpp/include/fb/fbjni/Hybrid.h rename to libs/fbjni/cxx/fbjni/detail/Hybrid.h index a95a44f51..d84dd31e1 100644 --- a/libs/fbjni/src/main/cpp/include/fb/fbjni/Hybrid.h +++ b/libs/fbjni/cxx/fbjni/detail/Hybrid.h @@ -1,18 +1,24 @@ -/* - * 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 #include -#include -#include - #include "CoreClasses.h" namespace facebook { @@ -25,13 +31,45 @@ public: virtual ~BaseHybridClass() {} }; -struct FBEXPORT HybridData : public JavaClass { +struct HybridData : public JavaClass { constexpr static auto kJavaDescriptor = "Lcom/facebook/jni/HybridData;"; - void setNativePointer(std::unique_ptr new_value); - BaseHybridClass* getNativePointer(); static local_ref create(); }; +class HybridDestructor : public JavaClass { + public: + static auto constexpr kJavaDescriptor = "Lcom/facebook/jni/HybridData$Destructor;"; + + detail::BaseHybridClass* getNativePointer(); + + void setNativePointer(std::unique_ptr new_value); +}; + +template +detail::BaseHybridClass* getNativePointer(T t) { + return getHolder(t)->getNativePointer(); +} + +template +void setNativePointer(T t, std::unique_ptr new_value) { + getHolder(t)->setNativePointer(std::move(new_value)); +} + +template +local_ref getHolder(T t) { + static auto holderField = t->getClass()->template getField("mDestructor"); + return t->getFieldValue(holderField); +} + +// JavaClass for HybridClassBase +struct HybridClassBase : public JavaClass { + constexpr static auto kJavaDescriptor = "Lcom/facebook/jni/HybridClassBase;"; + + static bool isHybridClassBase(alias_ref jclass) { + return HybridClassBase::javaClassStatic()->isAssignableFrom(jclass); + } +}; + template struct HybridTraits { // 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 template -struct FBEXPORT Convert< +struct Convert< T, typename std::enable_if< std::is_base_of::type>::value>::type> { typedef typename std::remove_pointer::type::jhybridobject jniType; @@ -91,7 +129,7 @@ struct RefReprType -class FBEXPORT HybridClass : public detail::HybridTraits::CxxBase { +class HybridClass : public detail::HybridTraits::CxxBase { public: struct JavaPart : JavaClass::JavaBase> { // At this point, T is incomplete, and so we cannot access @@ -108,6 +146,7 @@ public: T* cthis(); friend class HybridClass; + friend T; }; using jhybridobject = typename JavaPart::javaobject; @@ -137,7 +176,7 @@ protected: static local_ref makeHybridData(std::unique_ptr cxxPart) { auto hybridData = detail::HybridData::create(); - hybridData->setNativePointer(std::move(cxxPart)); + setNativePointer(hybridData, std::move(cxxPart)); return hybridData; } @@ -146,6 +185,11 @@ protected: return makeHybridData(std::unique_ptr(new T(std::forward(args)...))); } + template + static void setCxxInstance(alias_ref o, Args&&... args) { + setNativePointer(o, std::unique_ptr(new T(std::forward(args)...))); + } + public: // Factory method for creating a hybrid object where the arguments // are used to initialize the C++ part directly without passing them @@ -159,11 +203,23 @@ public: // C++ object fails, or any JNI methods throw. template static local_ref newObjectCxxArgs(Args&&... args) { - auto hybridData = makeCxxInstance(std::forward(args)...); - return JavaPart::newInstance(hybridData); + static bool isHybrid = detail::HybridClassBase::isHybridClassBase(javaClassStatic()); + auto cxxPart = std::unique_ptr(new T(std::forward(args)...)); + + local_ref 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 // implementation of allocate().) template @@ -193,17 +249,23 @@ public: template inline T* HybridClass::JavaPart::cthis() { - static auto field = - HybridClass::JavaPart::javaClassStatic()->template getField("mHybridData"); - auto hybridData = this->getFieldValue(field); - if (!hybridData) { - throwNewJavaException("java/lang/NullPointerException", "java.lang.NullPointerException"); + detail::BaseHybridClass* result = 0; + static bool isHybrid = detail::HybridClassBase::isHybridClassBase(this->getClass()); + if (isHybrid) { + result = getNativePointer(this); + } else { + static auto field = + HybridClass::JavaPart::javaClassStatic()->template getField("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. - T* value = static_cast(hybridData->getNativePointer()); - // This would require some serious programmer error. - FBASSERTMSGF(value != 0, "Incorrect C++ type in hybrid field"); - return value; + return static_cast(result); }; template diff --git a/libs/fbjni/src/main/cpp/include/fb/fbjni/Iterator-inl.h b/libs/fbjni/cxx/fbjni/detail/Iterator-inl.h similarity index 89% rename from libs/fbjni/src/main/cpp/include/fb/fbjni/Iterator-inl.h rename to libs/fbjni/cxx/fbjni/detail/Iterator-inl.h index 507306de4..d915c69fb 100644 --- a/libs/fbjni/src/main/cpp/include/fb/fbjni/Iterator-inl.h +++ b/libs/fbjni/cxx/fbjni/detail/Iterator-inl.h @@ -1,10 +1,19 @@ -/* - * 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 namespace facebook { @@ -33,7 +42,7 @@ struct IteratorHelper : public JavaClass> { value_type next() { static auto elementField = JavaBase_::javaClassStatic()->template getField("mElement"); - return dynamic_ref_cast(JavaBase_::getFieldValue(elementField)); + return dynamic_ref_cast>(JavaBase_::getFieldValue(elementField)); } static void reset(value_type& v) { diff --git a/libs/fbjni/src/main/cpp/include/fb/fbjni/Iterator.h b/libs/fbjni/cxx/fbjni/detail/Iterator.h similarity index 87% rename from libs/fbjni/src/main/cpp/include/fb/fbjni/Iterator.h rename to libs/fbjni/cxx/fbjni/detail/Iterator.h index efe8436f6..02c5c4607 100644 --- a/libs/fbjni/src/main/cpp/include/fb/fbjni/Iterator.h +++ b/libs/fbjni/cxx/fbjni/detail/Iterator.h @@ -1,10 +1,19 @@ -/* - * 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" diff --git a/libs/fbjni/cxx/fbjni/detail/JWeakReference.h b/libs/fbjni/cxx/fbjni/detail/JWeakReference.h new file mode 100644 index 000000000..433c6f890 --- /dev/null +++ b/libs/fbjni/cxx/fbjni/detail/JWeakReference.h @@ -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 +class JWeakReference : public JavaClass> { + + typedef JavaClass> JavaBase_; + + public: + static constexpr const char* kJavaDescriptor = "Ljava/lang/ref/WeakReference;"; + + static local_ref> newInstance(alias_ref object) { + return JavaBase_::newInstance(static_ref_cast(object)); + } + + local_ref get() const { + static const auto method = JavaBase_::javaClassStatic()->template getMethod("get"); + return static_ref_cast(method(JavaBase_::self())); + } +}; + +} +} diff --git a/libs/fbjni/cxx/fbjni/detail/Log.h b/libs/fbjni/cxx/fbjni/detail/Log.h new file mode 100644 index 000000000..dbc57abdf --- /dev/null +++ b/libs/fbjni/cxx/fbjni/detail/Log.h @@ -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 + +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 +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 +inline void logf(const char* tag, const char* msg, ARGS... args) noexcept { + __android_log_print(ANDROID_LOG_FATAL, tag, msg, args...); +} + +template +[[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 + +# define FBJNI_LOGE(...) ((void)0) +# define FBJNI_LOGF(...) (abort()) +# define FBJNI_ASSERT(cond) ((void)0) +#endif diff --git a/libs/fbjni/cxx/fbjni/detail/Meta-forward.h b/libs/fbjni/cxx/fbjni/detail/Meta-forward.h new file mode 100644 index 000000000..84b015249 --- /dev/null +++ b/libs/fbjni/cxx/fbjni/detail/Meta-forward.h @@ -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 +class JMethod; +template +class JStaticMethod; +template +class JNonvirtualMethod; +template +struct JConstructor; +template +class JField; +template +class JStaticField; + +/// Type traits for Java types (currently providing Java type descriptors) +template +struct jtype_traits; + +/// Type traits for Java methods (currently providing Java type descriptors) +template +struct jmethod_traits; + +}} diff --git a/libs/fbjni/src/main/cpp/include/fb/fbjni/Meta-inl.h b/libs/fbjni/cxx/fbjni/detail/Meta-inl.h similarity index 84% rename from libs/fbjni/src/main/cpp/include/fb/fbjni/Meta-inl.h rename to libs/fbjni/cxx/fbjni/detail/Meta-inl.h index e8f447698..bd7f5bfdb 100644 --- a/libs/fbjni/src/main/cpp/include/fb/fbjni/Meta-inl.h +++ b/libs/fbjni/cxx/fbjni/detail/Meta-inl.h @@ -1,10 +1,19 @@ -/* - * 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 @@ -15,10 +24,6 @@ #include "References.h" #include "Boxed.h" -#if defined(__ANDROID__) -#include -#endif - namespace facebook { namespace jni { @@ -63,31 +68,10 @@ local_ref::javaobject> makeArgsArray(Args... args) { return arr; } - -inline bool needsSlowPath(alias_ref 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 -inline void JMethod::operator()(alias_ref self, Args... args) { +inline void JMethod::operator()(alias_ref self, Args... args) const { const auto env = Environment::current(); env->CallVoidMethod( self.get(), @@ -98,10 +82,10 @@ inline void JMethod::operator()(alias_ref self, Args... #pragma push_macro("DEFINE_PRIMITIVE_CALL") #undef DEFINE_PRIMITIVE_CALL -#define DEFINE_PRIMITIVE_CALL(TYPE, METHOD, CLASS) \ +#define DEFINE_PRIMITIVE_CALL(TYPE, METHOD) \ template \ -inline TYPE JMethod::operator()(alias_ref self, Args... args) { \ - const auto env = internal::getEnv(); \ +inline TYPE JMethod::operator()(alias_ref self, Args... args) const { \ + const auto env = Environment::current(); \ auto result = env->Call ## METHOD ## Method( \ self.get(), \ getId(), \ @@ -110,14 +94,14 @@ inline TYPE JMethod::operator()(alias_ref self, Args... return result; \ } -DEFINE_PRIMITIVE_CALL(jboolean, Boolean, JBoolean) -DEFINE_PRIMITIVE_CALL(jbyte, Byte, JByte) -DEFINE_PRIMITIVE_CALL(jchar, Char, JCharacter) -DEFINE_PRIMITIVE_CALL(jshort, Short, JShort) -DEFINE_PRIMITIVE_CALL(jint, Int, JInteger) -DEFINE_PRIMITIVE_CALL(jlong, Long, JLong) -DEFINE_PRIMITIVE_CALL(jfloat, Float, JFloat) -DEFINE_PRIMITIVE_CALL(jdouble, Double, JDouble) +DEFINE_PRIMITIVE_CALL(jboolean, Boolean) +DEFINE_PRIMITIVE_CALL(jbyte, Byte) +DEFINE_PRIMITIVE_CALL(jchar, Char) +DEFINE_PRIMITIVE_CALL(jshort, Short) +DEFINE_PRIMITIVE_CALL(jint, Int) +DEFINE_PRIMITIVE_CALL(jlong, Long) +DEFINE_PRIMITIVE_CALL(jfloat, Float) +DEFINE_PRIMITIVE_CALL(jdouble, Double) #pragma pop_macro("DEFINE_PRIMITIVE_CALL") /// JMethod specialization for references that wraps the return value in a @ref local_ref @@ -132,13 +116,13 @@ class JMethod : public JMethodBase { JMethod(const JMethod& other) noexcept = default; /// Invoke a method and return a local reference wrapping the result - local_ref operator()(alias_ref self, Args... args); + local_ref operator()(alias_ref self, Args... args) const; friend class JClass; }; template -inline auto JMethod::operator()(alias_ref self, Args... args) -> local_ref { +inline auto JMethod::operator()(alias_ref self, Args... args) const -> local_ref { const auto env = Environment::current(); auto result = env->CallObjectMethod( self.get(), @@ -149,8 +133,8 @@ inline auto JMethod::operator()(alias_ref self, Args... arg } template -inline void JStaticMethod::operator()(alias_ref cls, Args... args) { - const auto env = internal::getEnv(); +inline void JStaticMethod::operator()(alias_ref cls, Args... args) const { + const auto env = Environment::current(); env->CallStaticVoidMethod( cls.get(), getId(), @@ -162,8 +146,8 @@ inline void JStaticMethod::operator()(alias_ref cls, Args #undef DEFINE_PRIMITIVE_STATIC_CALL #define DEFINE_PRIMITIVE_STATIC_CALL(TYPE, METHOD) \ template \ -inline TYPE JStaticMethod::operator()(alias_ref cls, Args... args) { \ - const auto env = internal::getEnv(); \ +inline TYPE JStaticMethod::operator()(alias_ref cls, Args... args) const { \ + const auto env = Environment::current(); \ auto result = env->CallStatic ## METHOD ## Method( \ cls.get(), \ getId(), \ @@ -194,8 +178,8 @@ class JStaticMethod : public JMethodBase { JStaticMethod(const JStaticMethod& other) noexcept = default; /// Invoke a method and return a local reference wrapping the result - local_ref operator()(alias_ref cls, Args... args) { - const auto env = internal::getEnv(); + local_ref operator()(alias_ref cls, Args... args) const { + const auto env = Environment::current(); auto result = env->CallStaticObjectMethod( cls.get(), getId(), @@ -209,8 +193,8 @@ class JStaticMethod : public JMethodBase { template inline void -JNonvirtualMethod::operator()(alias_ref self, alias_ref cls, Args... args) { - const auto env = internal::getEnv(); +JNonvirtualMethod::operator()(alias_ref self, alias_ref cls, Args... args) const { + const auto env = Environment::current(); env->CallNonvirtualVoidMethod( self.get(), cls.get(), @@ -224,8 +208,8 @@ JNonvirtualMethod::operator()(alias_ref self, alias_ref< #define DEFINE_PRIMITIVE_NON_VIRTUAL_CALL(TYPE, METHOD) \ template \ inline TYPE \ -JNonvirtualMethod::operator()(alias_ref self, alias_ref cls, Args... args) { \ - const auto env = internal::getEnv(); \ +JNonvirtualMethod::operator()(alias_ref self, alias_ref cls, Args... args) const { \ + const auto env = Environment::current(); \ auto result = env->CallNonvirtual ## METHOD ## Method( \ self.get(), \ cls.get(), \ @@ -256,8 +240,8 @@ class JNonvirtualMethod : public JMethodBase { JNonvirtualMethod(const JNonvirtualMethod& other) noexcept = default; /// Invoke a method and return a local reference wrapping the result - local_ref operator()(alias_ref self, alias_ref cls, Args... args){ - const auto env = internal::getEnv(); + local_ref operator()(alias_ref self, alias_ref cls, Args... args) const { + const auto env = Environment::current(); auto result = env->CallNonvirtualObjectMethod( self.get(), cls.get(), @@ -306,13 +290,13 @@ inline jfieldID JField::getId() const noexcept { #define DEFINE_FIELD_PRIMITIVE_GET_SET(TYPE, METHOD) \ template<> \ inline TYPE JField::get(jobject object) const noexcept { \ - const auto env = internal::getEnv(); \ + const auto env = Environment::current(); \ return env->Get ## METHOD ## Field(object, field_id_); \ } \ \ template<> \ inline void JField::set(jobject object, TYPE value) noexcept { \ - const auto env = internal::getEnv(); \ + const auto env = Environment::current(); \ env->Set ## METHOD ## Field(object, field_id_, value); \ } @@ -328,12 +312,12 @@ DEFINE_FIELD_PRIMITIVE_GET_SET(jdouble, Double) template inline T JField::get(jobject object) const noexcept { - return static_cast(internal::getEnv()->GetObjectField(object, field_id_)); + return static_cast(Environment::current()->GetObjectField(object, field_id_)); } template inline void JField::set(jobject object, T value) noexcept { - internal::getEnv()->SetObjectField(object, field_id_, static_cast(value)); + Environment::current()->SetObjectField(object, field_id_, static_cast(value)); } // JStaticField ///////////////////////////////////////////////////////////////////////////////// @@ -358,13 +342,13 @@ inline jfieldID JStaticField::getId() const noexcept { #define DEFINE_STATIC_FIELD_PRIMITIVE_GET_SET(TYPE, METHOD) \ template<> \ inline TYPE JStaticField::get(jclass jcls) const noexcept { \ - const auto env = internal::getEnv(); \ + const auto env = Environment::current(); \ return env->GetStatic ## METHOD ## Field(jcls, field_id_); \ } \ \ template<> \ inline void JStaticField::set(jclass jcls, TYPE value) noexcept { \ - const auto env = internal::getEnv(); \ + const auto env = Environment::current(); \ env->SetStatic ## METHOD ## Field(jcls, field_id_, value); \ } @@ -380,13 +364,13 @@ DEFINE_STATIC_FIELD_PRIMITIVE_GET_SET(jdouble, Double) template inline T JStaticField::get(jclass jcls) const noexcept { - const auto env = internal::getEnv(); + const auto env = Environment::current(); return static_cast(env->GetStaticObjectField(jcls, field_id_)); } template inline void JStaticField::set(jclass jcls, T value) noexcept { - internal::getEnv()->SetStaticObjectField(jcls, field_id_, value); + Environment::current()->SetStaticObjectField(jcls, field_id_, value); } diff --git a/libs/fbjni/src/main/cpp/include/fb/fbjni/Meta.h b/libs/fbjni/cxx/fbjni/detail/Meta.h similarity index 93% rename from libs/fbjni/src/main/cpp/include/fb/fbjni/Meta.h rename to libs/fbjni/cxx/fbjni/detail/Meta.h index a859367fa..739de84f8 100644 --- a/libs/fbjni/src/main/cpp/include/fb/fbjni/Meta.h +++ b/libs/fbjni/cxx/fbjni/detail/Meta.h @@ -1,10 +1,19 @@ -/* - * 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 meta.h * * Provides wrappers for meta data such as methods and fields. @@ -81,7 +90,7 @@ class JMethod : public JMethodBase { JMethod() noexcept {}; \ JMethod(const JMethod& other) noexcept = default; \ \ - TYPE operator()(alias_ref self, Args... args); \ + TYPE operator()(alias_ref self, Args... args) const; \ \ friend class JClass; \ } @@ -131,7 +140,7 @@ class JStaticMethod : public JMethodBase { \ JStaticMethod() noexcept {}; \ JStaticMethod(const JStaticMethod& other) noexcept = default; \ \ - TYPE operator()(alias_ref cls, Args... args); \ + TYPE operator()(alias_ref cls, Args... args) const; \ \ friend class JClass; \ } @@ -171,7 +180,7 @@ class JNonvirtualMethod : public JMethodBase { \ JNonvirtualMethod() noexcept {}; \ JNonvirtualMethod(const JNonvirtualMethod& other) noexcept = default; \ \ - TYPE operator()(alias_ref self, alias_ref cls, Args... args); \ + TYPE operator()(alias_ref self, alias_ref cls, Args... args) const; \ \ friend class JClass; \ } diff --git a/libs/fbjni/src/main/cpp/include/fb/fbjni/MetaConvert.h b/libs/fbjni/cxx/fbjni/detail/MetaConvert.h similarity index 65% rename from libs/fbjni/src/main/cpp/include/fb/fbjni/MetaConvert.h rename to libs/fbjni/cxx/fbjni/detail/MetaConvert.h index 479316197..71ea8b157 100644 --- a/libs/fbjni/src/main/cpp/include/fb/fbjni/MetaConvert.h +++ b/libs/fbjni/cxx/fbjni/detail/MetaConvert.h @@ -1,10 +1,19 @@ -/* - * Copyright (c) 2004-present, Facebook, Inc. +/** + * Copyright 2004-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 @@ -100,7 +109,21 @@ template struct Convert> { typedef JniType jniType; // No automatic synthesis of global_ref - static jniType toJniRet(global_ref t) { + static jniType toJniRet(global_ref&& 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& t) { + // If this gets called, the function was declared to return const&. We + // have a ref to a global_ref whose lifetime will exceed this call, so we + // can just get the underlying jobject and return it to java without + // needing to make a local_ref. return t.get(); } static jniType toCall(global_ref t) { diff --git a/libs/fbjni/src/main/cpp/include/fb/fbjni/ReferenceAllocators-inl.h b/libs/fbjni/cxx/fbjni/detail/ReferenceAllocators-inl.h similarity index 61% rename from libs/fbjni/src/main/cpp/include/fb/fbjni/ReferenceAllocators-inl.h rename to libs/fbjni/cxx/fbjni/detail/ReferenceAllocators-inl.h index 2b3cf3dee..3a42d364c 100644 --- a/libs/fbjni/src/main/cpp/include/fb/fbjni/ReferenceAllocators-inl.h +++ b/libs/fbjni/cxx/fbjni/detail/ReferenceAllocators-inl.h @@ -1,16 +1,27 @@ -/* - * 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 #include #include +#include "Environment.h" + namespace facebook { namespace jni { @@ -19,7 +30,8 @@ namespace internal { // Statistics mostly provided for test (only updated if FBJNI_DEBUG_REFS is defined) struct ReferenceStats { - std::atomic_uint locals_deleted, globals_deleted, weaks_deleted; + std::atomic_uint locals_created, globals_created, weaks_created, + locals_deleted, globals_deleted, weaks_deleted; void reset() noexcept; }; @@ -33,7 +45,10 @@ extern ReferenceStats g_reference_stats; inline jobject LocalReferenceAllocator::newReference(jobject original) const { 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(); return ref; } @@ -46,15 +61,12 @@ inline void LocalReferenceAllocator::deleteReference(jobject reference) const no ++internal::g_reference_stats.locals_deleted; #endif assert(verifyReference(reference)); - internal::getEnv()->DeleteLocalRef(reference); + Environment::current()->DeleteLocalRef(reference); } } inline bool LocalReferenceAllocator::verifyReference(jobject reference) const noexcept { - if (!reference || !internal::doesGetObjectRefTypeWork()) { - return true; - } - return internal::getEnv()->GetObjectRefType(reference) == JNILocalRefType; + return isObjectRefType(reference, JNILocalRefType); } @@ -62,7 +74,10 @@ inline bool LocalReferenceAllocator::verifyReference(jobject reference) const no inline jobject GlobalReferenceAllocator::newReference(jobject original) const { 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(); return ref; } @@ -75,15 +90,12 @@ inline void GlobalReferenceAllocator::deleteReference(jobject reference) const n ++internal::g_reference_stats.globals_deleted; #endif assert(verifyReference(reference)); - internal::getEnv()->DeleteGlobalRef(reference); + Environment::current()->DeleteGlobalRef(reference); } } inline bool GlobalReferenceAllocator::verifyReference(jobject reference) const noexcept { - if (!reference || !internal::doesGetObjectRefTypeWork()) { - return true; - } - return internal::getEnv()->GetObjectRefType(reference) == JNIGlobalRefType; + return isObjectRefType(reference, JNIGlobalRefType); } @@ -91,7 +103,10 @@ inline bool GlobalReferenceAllocator::verifyReference(jobject reference) const n inline jobject WeakGlobalReferenceAllocator::newReference(jobject original) const { 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(); return ref; } @@ -104,15 +119,12 @@ inline void WeakGlobalReferenceAllocator::deleteReference(jobject reference) con ++internal::g_reference_stats.weaks_deleted; #endif assert(verifyReference(reference)); - internal::getEnv()->DeleteWeakGlobalRef(reference); + Environment::current()->DeleteWeakGlobalRef(reference); } } inline bool WeakGlobalReferenceAllocator::verifyReference(jobject reference) const noexcept { - if (!reference || !internal::doesGetObjectRefTypeWork()) { - return true; - } - return internal::getEnv()->GetObjectRefType(reference) == JNIWeakGlobalRefType; + return isObjectRefType(reference, JNIWeakGlobalRefType); } }} diff --git a/libs/fbjni/src/main/cpp/include/fb/fbjni/ReferenceAllocators.h b/libs/fbjni/cxx/fbjni/detail/ReferenceAllocators.h similarity index 50% rename from libs/fbjni/src/main/cpp/include/fb/fbjni/ReferenceAllocators.h rename to libs/fbjni/cxx/fbjni/detail/ReferenceAllocators.h index 214f6b2c2..7278ed094 100644 --- a/libs/fbjni/src/main/cpp/include/fb/fbjni/ReferenceAllocators.h +++ b/libs/fbjni/cxx/fbjni/detail/ReferenceAllocators.h @@ -1,10 +1,19 @@ -/* - * 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 ReferenceAllocators.h * @@ -14,14 +23,12 @@ #pragma once -#include - #include "Common.h" namespace facebook { namespace jni { /// Allocator that handles local references -class FBEXPORT LocalReferenceAllocator { +class LocalReferenceAllocator { public: jobject newReference(jobject original) const; void deleteReference(jobject reference) const noexcept; @@ -29,7 +36,7 @@ class FBEXPORT LocalReferenceAllocator { }; /// Allocator that handles global references -class FBEXPORT GlobalReferenceAllocator { +class GlobalReferenceAllocator { public: jobject newReference(jobject original) const; void deleteReference(jobject reference) const noexcept; @@ -37,23 +44,20 @@ class FBEXPORT GlobalReferenceAllocator { }; /// Allocator that handles weak global references -class FBEXPORT WeakGlobalReferenceAllocator { +class WeakGlobalReferenceAllocator { public: jobject newReference(jobject original) const; void deleteReference(jobject reference) const noexcept; bool verifyReference(jobject reference) const noexcept; }; -/// @cond INTERNAL -namespace internal { - /** - * @return true iff env->GetObjectRefType is expected to work properly. + * @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. */ -FBEXPORT bool doesGetObjectRefTypeWork(); - -} -/// @endcond +bool isObjectRefType(jobject reference, jobjectRefType refType); }} diff --git a/libs/fbjni/src/main/cpp/include/fb/fbjni/References-forward.h b/libs/fbjni/cxx/fbjni/detail/References-forward.h similarity index 69% rename from libs/fbjni/src/main/cpp/include/fb/fbjni/References-forward.h rename to libs/fbjni/cxx/fbjni/detail/References-forward.h index 13b92877a..9b68d614f 100644 --- a/libs/fbjni/src/main/cpp/include/fb/fbjni/References-forward.h +++ b/libs/fbjni/cxx/fbjni/detail/References-forward.h @@ -1,10 +1,19 @@ -/* - * 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 "ReferenceAllocators.h" diff --git a/libs/fbjni/src/main/cpp/include/fb/fbjni/References-inl.h b/libs/fbjni/cxx/fbjni/detail/References-inl.h similarity index 93% rename from libs/fbjni/src/main/cpp/include/fb/fbjni/References-inl.h rename to libs/fbjni/cxx/fbjni/detail/References-inl.h index 58c158515..7fc17671e 100644 --- a/libs/fbjni/src/main/cpp/include/fb/fbjni/References-inl.h +++ b/libs/fbjni/cxx/fbjni/detail/References-inl.h @@ -1,10 +1,19 @@ -/* - * 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 @@ -484,22 +493,25 @@ template auto dynamic_ref_cast(const RefType& ref) -> enable_if_t(), decltype(static_ref_cast(ref))> { - if (! ref) { + if (!ref) { return decltype(static_ref_cast(ref))(); } - std::string target_class_name{jtype_traits::base_name()}; + static alias_ref target_class = findClassStatic(jtype_traits::base_name().c_str()); + if (!target_class) { + throwNewJavaException("java/lang/ClassCastException", + "Could not find class %s.", + jtype_traits::base_name().c_str()); - // If not found, will throw an exception. - alias_ref target_class = findClassStatic(target_class_name.c_str()); + } local_ref source_class = ref->getClass(); - if ( ! source_class->isAssignableFrom(target_class)) { + if (!target_class->isAssignableFrom(source_class)) { throwNewJavaException("java/lang/ClassCastException", "Tried to cast from %s to %s.", source_class->toString().c_str(), - target_class_name.c_str()); + jtype_traits::base_name().c_str()); } return static_ref_cast(ref); diff --git a/libs/fbjni/cxx/fbjni/detail/References.cpp b/libs/fbjni/cxx/fbjni/detail/References.cpp new file mode 100644 index 000000000..6062c49ca --- /dev/null +++ b/libs/fbjni/cxx/fbjni/detail/References.cpp @@ -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("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; +} + +} +} diff --git a/libs/fbjni/src/main/cpp/include/fb/fbjni/References.h b/libs/fbjni/cxx/fbjni/detail/References.h similarity index 96% rename from libs/fbjni/src/main/cpp/include/fb/fbjni/References.h rename to libs/fbjni/cxx/fbjni/detail/References.h index 981acb6c6..2cd0d8482 100644 --- a/libs/fbjni/src/main/cpp/include/fb/fbjni/References.h +++ b/libs/fbjni/cxx/fbjni/detail/References.h @@ -1,10 +1,20 @@ -/* - * 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 References.h * * Functionality similar to smart pointers, but for references into the VM. Four main reference @@ -74,8 +84,6 @@ #include -#include - #include "ReferenceAllocators.h" #include "TypeTraits.h" #include "References-forward.h" @@ -337,7 +345,7 @@ class weak_ref : public base_owned_ref { : base_owned_ref{} {} /// Create a null reference - explicit weak_ref(std::nullptr_t) noexcept + /* implicit */ weak_ref(std::nullptr_t) noexcept : base_owned_ref{nullptr} {} /// Copy constructor (note creates a new reference) @@ -406,7 +414,7 @@ class basic_strong_ref : public base_owned_ref { : base_owned_ref{} {} /// Create a null reference - explicit basic_strong_ref(std::nullptr_t) noexcept + /* implicit */ basic_strong_ref(std::nullptr_t) noexcept : base_owned_ref{nullptr} {} /// Copy constructor (note creates a new reference) @@ -493,7 +501,7 @@ class alias_ref { alias_ref() noexcept; /// Create a null reference - alias_ref(std::nullptr_t) noexcept; + /* implicit */ alias_ref(std::nullptr_t) noexcept; /// Copy constructor alias_ref(const alias_ref& other) noexcept; @@ -554,7 +562,7 @@ class alias_ref { * This is useful when you have a call which is initiated from C++-land, and therefore * doesn't automatically get a local JNI frame managed for you by the JNI framework. */ -class FBEXPORT JniLocalScope { +class JniLocalScope { public: JniLocalScope(JNIEnv* p_env, jint capacity); ~JniLocalScope(); diff --git a/libs/fbjni/cxx/fbjni/detail/Registration-inl.h b/libs/fbjni/cxx/fbjni/detail/Registration-inl.h new file mode 100644 index 000000000..f7171a6a3 --- /dev/null +++ b/libs/fbjni/cxx/fbjni/detail/Registration-inl.h @@ -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 +struct CreateDefault { + static R create() { + return R{}; + } +}; + +template <> +struct CreateDefault { + static void create() {} +}; + +template +using Converter = Convert::type>; + +template +struct WrapForVoidReturn { + static typename Converter::jniType call(Args&&... args) { + return Converter::toJniRet(func(std::forward(args)...)); + } +}; + +template +struct WrapForVoidReturn { + static void call(Args&&... args) { + func(std::forward(args)...); + } +}; + +// registration wrapper for legacy JNI-style functions +template +struct BareJniWrapper { + JNI_ENTRY_POINT static R call(JNIEnv* env, jobject obj, Args... args) { + detail::JniEnvCacher jec(env); + try { + return (*func)(env, static_cast>(obj), args...); + } catch (...) { + translatePendingCppExceptionToJavaException(); + return CreateDefault::create(); + } + } +}; + +// registration wrappers for functions, with autoconversion of arguments. +template +struct FunctionWrapper { + using jniRet = typename Converter::jniType; + JNI_ENTRY_POINT static jniRet call(JNIEnv* env, jobject obj, typename Converter::jniType... args) { + detail::JniEnvCacher jec(env); + try { + return WrapForVoidReturn, Args...>::call( + static_cast>(obj), Converter::fromJni(args)...); + } catch (...) { + translatePendingCppExceptionToJavaException(); + return CreateDefault::create(); + } + } +}; + +// registration wrappers for non-static methods, with autoconvertion of arguments. +template +struct MethodWrapper { + using jhybrid = typename C::jhybridobject; + static R dispatch(alias_ref 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(ref->cthis()); + return (cobj->*method)(std::forward(args)...); + } catch (const std::exception& ex) { + C::mapException(ex); + throw; + } + } + + JNI_ENTRY_POINT static typename Converter::jniType call( + JNIEnv* env, jobject obj, typename Converter::jniType... args) { + return FunctionWrapper, Args&&...), dispatch, jhybrid, R, Args...>::call(env, obj, args...); + } +}; + +template +inline NativeMethodWrapper* exceptionWrapJNIMethod(R (*)(JNIEnv*, C, Args... args)) { + // This intentionally erases the real type; JNI will do it anyway + return reinterpret_cast(&(BareJniWrapper::call)); +} + +template +inline NativeMethodWrapper* exceptionWrapJNIMethod(R (*)(alias_ref, Args... args)) { + // This intentionally erases the real type; JNI will do it anyway + return reinterpret_cast(&(FunctionWrapper::call)); +} + +template +inline NativeMethodWrapper* exceptionWrapJNIMethod(R (C::*method0)(Args... args)) { + // This intentionally erases the real type; JNI will do it anyway + return reinterpret_cast(&(MethodWrapper::call)); +} + +template +inline std::string makeDescriptor(R (*)(JNIEnv*, C, Args... args)) { + return jmethod_traits::descriptor(); +} + +template +inline std::string makeDescriptor(R (*)(alias_ref, Args... args)) { + return jmethod_traits_from_cxx::descriptor(); +} + +template +inline std::string makeDescriptor(R (C::*)(Args... args)) { + return jmethod_traits_from_cxx::descriptor(); +} + +} + +}} diff --git a/libs/fbjni/src/main/cpp/include/fb/fbjni/Registration.h b/libs/fbjni/cxx/fbjni/detail/Registration.h similarity index 76% rename from libs/fbjni/src/main/cpp/include/fb/fbjni/Registration.h rename to libs/fbjni/cxx/fbjni/detail/Registration.h index 4cf65fac6..924538757 100644 --- a/libs/fbjni/src/main/cpp/include/fb/fbjni/Registration.h +++ b/libs/fbjni/cxx/fbjni/detail/Registration.h @@ -1,10 +1,19 @@ -/* - * 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 @@ -18,28 +27,14 @@ namespace detail { // This uses the real JNI function as a non-type template parameter to // cause a (static member) function to exist with the same signature, // but with try/catch exception translation. -template -NativeMethodWrapper* exceptionWrapJNIMethod(void (*func0)(JNIEnv*, jobject, Args... args)); - -// Same as above, but for non-void return types. template NativeMethodWrapper* exceptionWrapJNIMethod(R (*func0)(JNIEnv*, jobject, Args... args)); // Automatically wrap object argument, and don't take env explicitly. -template -NativeMethodWrapper* exceptionWrapJNIMethod(void (*func0)(alias_ref, Args... args)); - -// Automatically wrap object argument, and don't take env explicitly, -// non-void return type. template NativeMethodWrapper* exceptionWrapJNIMethod(R (*func0)(alias_ref, Args... args)); -// Extract C++ instance from object, and invoke given method on it. -template -NativeMethodWrapper* exceptionWrapJNIMethod(void (C::*method0)(Args... args)); - // Extract C++ instance from object, and invoke given method on it, -// non-void return type template NativeMethodWrapper* exceptionWrapJNIMethod(R (C::*method0)(Args... args)); diff --git a/libs/fbjni/src/main/cpp/include/fb/fbjni/TypeTraits.h b/libs/fbjni/cxx/fbjni/detail/TypeTraits.h similarity index 88% rename from libs/fbjni/src/main/cpp/include/fb/fbjni/TypeTraits.h rename to libs/fbjni/cxx/fbjni/detail/TypeTraits.h index 80cb382b0..dc9fbb019 100644 --- a/libs/fbjni/src/main/cpp/include/fb/fbjni/TypeTraits.h +++ b/libs/fbjni/cxx/fbjni/detail/TypeTraits.h @@ -1,10 +1,19 @@ -/* - * 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 diff --git a/libs/fbjni/src/main/cpp/jni/LocalString.cpp b/libs/fbjni/cxx/fbjni/detail/utf8.cpp similarity index 76% rename from libs/fbjni/src/main/cpp/jni/LocalString.cpp rename to libs/fbjni/cxx/fbjni/detail/utf8.cpp index aca121438..2cda26d6d 100644 --- a/libs/fbjni/src/main/cpp/jni/LocalString.cpp +++ b/libs/fbjni/cxx/fbjni/detail/utf8.cpp @@ -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. */ -#include -#include -#include -#include +#include "utf8.h" + +#include "Log.h" namespace facebook { namespace jni { @@ -23,7 +30,9 @@ const uint16_t kUtf16HighSubHighBoundary = 0xDC00; const uint16_t kUtf16LowSubHighBoundary = 0xE000; inline void encode3ByteUTF8(char32_t code, uint8_t* out) { - FBASSERTMSGF((code & 0xffff0000) == 0, "3 byte utf-8 encodings only valid for up to 16 bits"); + if ((code & 0xffff0000) != 0) { + FBJNI_LOGF("3 byte utf-8 encodings only valid for up to 16 bits"); + } out[0] = 0xE0 | (code >> 12); 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) { - 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 + 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; 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) { - 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 + 1] = 0x80; 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 - 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(second, modified + j + 3); i += 4; j += 6; } - FBASSERTMSGF(j < modifiedBufLen, "output buffer is too short"); + if (j >= modifiedBufLen) { + FBJNI_LOGF("output buffer is too short"); + } 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(modlen + 1); // allocate extra byte for \0 - detail::utf8ToModifiedUTF8( - reinterpret_cast(str.data()), str.size(), - reinterpret_cast(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(str), &len); - if (modlen == len) { - // no supplementary characters, build jstring from input buffer - m_string = Environment::current()->NewStringUTF(str); - return; - } - auto modified = std::vector(modlen + 1); // allocate extra byte for \0 - detail::utf8ToModifiedUTF8( - reinterpret_cast(str), len, - reinterpret_cast(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); -} - -} } diff --git a/libs/fbjni/src/main/cpp/include/jni/LocalString.h b/libs/fbjni/cxx/fbjni/detail/utf8.h similarity index 68% rename from libs/fbjni/src/main/cpp/include/jni/LocalString.h rename to libs/fbjni/cxx/fbjni/detail/utf8.h index 77f0696b5..b1b8558db 100644 --- a/libs/fbjni/src/main/cpp/include/jni/LocalString.h +++ b/libs/fbjni/cxx/fbjni/detail/utf8.h @@ -1,18 +1,25 @@ -/* - * 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 #include -#include - namespace facebook { 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 // - https://docs.oracle.com/javase/6/docs/api/java/io/DataInput.html#modified-utf-8 -class FBEXPORT LocalString { -public: - // Assumes UTF8 encoding and make a required convertion to modified UTF-8 when the string - // contains unicode supplementary characters. - explicit LocalString(const std::string& str); - explicit LocalString(const char* str); - jstring string() const { - return m_string; - } - ~LocalString(); -private: - jstring m_string; -}; - -// JString to UTF16 extractor using RAII idiom +// JString to UTF16 extractor using RAII idiom. Note that the +// ctor/dtor use GetStringCritical/ReleaseStringCritical, so this +// class is subject to the restrictions imposed by those functions. class JStringUtf16Extractor { public: JStringUtf16Extractor(JNIEnv* env, jstring javaString) : env_(env) , javaString_(javaString) + , length_(0) , utf16String_(nullptr) { if (env_ && javaString_) { + length_ = env_->GetStringLength(javaString_); utf16String_ = env_->GetStringCritical(javaString_, nullptr); } } @@ -75,18 +72,20 @@ public: } } - operator const jchar* () const { + const jsize length() const { + return length_; + } + + const jchar* chars() const { return utf16String_; } private: JNIEnv* env_; jstring javaString_; + jsize length_; const jchar* utf16String_; }; -// The string from JNI is converted to standard UTF-8 if the string contains supplementary -// characters. -FBEXPORT std::string fromJString(JNIEnv* env, jstring str); - -} } +} +} diff --git a/libs/fbjni/src/main/cpp/jni/fbjni.cpp b/libs/fbjni/cxx/fbjni/fbjni.cpp similarity index 64% rename from libs/fbjni/src/main/cpp/jni/fbjni.cpp rename to libs/fbjni/cxx/fbjni/fbjni.cpp index d07d62ffa..a8e78a1e9 100644 --- a/libs/fbjni/src/main/cpp/jni/fbjni.cpp +++ b/libs/fbjni/cxx/fbjni/fbjni.cpp @@ -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 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 -#include -#include + +#include namespace facebook { namespace jni { jint initialize(JavaVM* vm, std::function&& init_fn) noexcept { - static std::once_flag flag{}; // TODO (t7832883): DTRT when we have exception pointers static auto error_msg = std::string{"Failed to initialize fbjni"}; - static auto error_occured = false; - - std::call_once(flag, [vm] { - try { - Environment::initialize(vm); - } catch (std::exception& ex) { - error_occured = true; + static bool error_occured = [vm] { + bool retVal = false; 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 (...) { - // Ignore, we already have a fall back message + retVal = true; } - } catch (...) { - error_occured = true; - } - }); + return retVal; + }(); try { if (error_occured) { @@ -43,7 +51,7 @@ jint initialize(JavaVM* vm, std::function&& init_fn) noexcept { init_fn(); } catch (const std::exception& e) { - FBLOGE("error %s", e.what()); + FBJNI_LOGE("error %s", e.what()); translatePendingCppExceptionToJavaException(); } catch (...) { translatePendingCppExceptionToJavaException(); @@ -54,19 +62,19 @@ jint initialize(JavaVM* vm, std::function&& init_fn) noexcept { } alias_ref findClassStatic(const char* name) { - const auto env = internal::getEnv(); + const auto env = detail::currentOrNull(); if (!env) { throw std::runtime_error("Unable to retrieve JNIEnv*."); } - auto cls = env->FindClass(name); + local_ref cls = adopt_local(env->FindClass(name)); 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); return wrap_alias(leaking_ref); } local_ref findClassLocal(const char* name) { - const auto env = internal::getEnv(); + const auto env = detail::currentOrNull(); if (!env) { throw std::runtime_error("Unable to retrieve JNIEnv*."); } @@ -79,17 +87,25 @@ local_ref findClassLocal(const char* name) { // jstring ///////////////////////////////////////////////////////////////////////////////////////// std::string JString::toStdString() const { - const auto env = internal::getEnv(); + const auto env = Environment::current(); auto utf16String = JStringUtf16Extractor(env, self()); - auto length = env->GetStringLength(self()); - return detail::utf16toUTF8(utf16String, length); + return detail::utf16toUTF8(utf16String.chars(), 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(utf16String.chars()), utf16String.length()); } local_ref make_jstring(const char* utf8) { if (!utf8) { return {}; } - const auto env = internal::getEnv(); + const auto env = Environment::current(); size_t len; size_t modlen = detail::modifiedLength(reinterpret_cast(utf8), &len); jstring result; @@ -112,6 +128,18 @@ local_ref make_jstring(const char* utf8) { return adopt_local(result); } +local_ref 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(utf16.c_str()), utf16.size()); + FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); + return adopt_local(result); +} // JniPrimitiveArrayFunctions ////////////////////////////////////////////////////////////////////// @@ -120,50 +148,44 @@ local_ref make_jstring(const char* utf8) { #define DEFINE_PRIMITIVE_METHODS(TYPE, NAME, SMALLNAME) \ \ template<> \ -FBEXPORT \ TYPE* JPrimitiveArray::getElements(jboolean* isCopy) { \ - auto env = internal::getEnv(); \ + auto env = Environment::current(); \ TYPE* res = env->Get ## NAME ## ArrayElements(self(), isCopy); \ FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); \ return res; \ } \ \ template<> \ -FBEXPORT \ void JPrimitiveArray::releaseElements( \ TYPE* elements, jint mode) { \ - auto env = internal::getEnv(); \ + auto env = Environment::current(); \ env->Release ## NAME ## ArrayElements(self(), elements, mode); \ FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); \ } \ \ template<> \ -FBEXPORT \ void JPrimitiveArray::getRegion( \ jsize start, jsize length, TYPE* buf) { \ - auto env = internal::getEnv(); \ + auto env = Environment::current(); \ env->Get ## NAME ## ArrayRegion(self(), start, length, buf); \ FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); \ } \ \ template<> \ -FBEXPORT \ void JPrimitiveArray::setRegion( \ jsize start, jsize length, const TYPE* elements) { \ - auto env = internal::getEnv(); \ + auto env = Environment::current(); \ env->Set ## NAME ## ArrayRegion(self(), start, length, elements); \ FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); \ } \ \ -FBEXPORT \ local_ref 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); \ return adopt_local(array); \ } \ \ template<> \ -FBEXPORT \ local_ref JArray ## NAME::newArray(size_t count) { \ return make_ ## SMALLNAME ## _array(count); \ } \ @@ -179,13 +201,37 @@ DEFINE_PRIMITIVE_METHODS(jfloat, Float, float) DEFINE_PRIMITIVE_METHODS(jdouble, Double, double) #pragma pop_macro("DEFINE_PRIMITIVE_METHODS") +namespace detail { + +detail::BaseHybridClass* HybridDestructor::getNativePointer() { + static auto pointerField = javaClassStatic()->getField("mNativePointer"); + auto* value = reinterpret_cast(getFieldValue(pointerField)); + if (!value) { + throwNewJavaException("java/lang/NullPointerException", "java.lang.NullPointerException"); + } + return value; +} + +void HybridDestructor::setNativePointer( + std::unique_ptr new_value) { + static auto pointerField = javaClassStatic()->getField("mNativePointer"); + auto old_value = std::unique_ptr( + reinterpret_cast(getFieldValue(pointerField))); + if (new_value && old_value) { + FBJNI_LOGF("Attempt to set C++ native pointer twice"); + } + setFieldValue(pointerField, reinterpret_cast(new_value.release())); +} + +} + // Internal debug ///////////////////////////////////////////////////////////////////////////////// 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; } diff --git a/libs/fbjni/cxx/fbjni/fbjni.h b/libs/fbjni/cxx/fbjni/fbjni.h new file mode 100644 index 000000000..9af170ce0 --- /dev/null +++ b/libs/fbjni/cxx/fbjni/fbjni.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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/libs/fbjni/cxx/lyra/cxa_throw.cpp b/libs/fbjni/cxx/lyra/cxa_throw.cpp new file mode 100644 index 000000000..e0449d9a5 --- /dev/null +++ b/libs/fbjni/cxx/lyra/cxa_throw.cpp @@ -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 +#include +#include +#include +#include + +#include + +namespace facebook { +namespace lyra { + +namespace { +std::atomic 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(&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(&obj); + auto info = reinterpret_cast(exc_ptr->__cxa_exception_type()); + auto mutable_info = static_cast(const_cast(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 diff --git a/libs/fbjni/src/main/cpp/lyra/lyra.cpp b/libs/fbjni/cxx/lyra/lyra.cpp similarity index 58% rename from libs/fbjni/src/main/cpp/lyra/lyra.cpp rename to libs/fbjni/cxx/lyra/lyra.cpp index 23da86cc8..2a59c2a2e 100644 --- a/libs/fbjni/src/main/cpp/lyra/lyra.cpp +++ b/libs/fbjni/cxx/lyra/lyra.cpp @@ -1,19 +1,33 @@ -/* - * Copyright (c) 2004-present, Facebook, Inc. +/** + * Copyright 2004-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 +#include +#include #include #include #include #include +#include + using namespace std; namespace facebook { @@ -68,6 +82,27 @@ void captureBacktrace(size_t skip, vector& stackTrace) { BacktraceState state = {skip, stackTrace}; _Unwind_Backtrace(unwindCallback, &state); } + +// this is a pointer to a function +std::atomic 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_ = ""; + } + hasBuildId_ = true; + } + return buildId_; } void getStackTrace(vector& stackTrace, size_t skip) { @@ -94,7 +129,6 @@ void getStackTraceSymbols(vector& symbols, ostream& operator<<(ostream& out, const StackTraceElement& elm) { IosFlagsSaver flags{out}; - // TODO(t10748683): Add build id to the output out << "{dso=" << elm.libraryName() << " offset=" << hex << showbase << elm.libraryOffset(); @@ -102,7 +136,7 @@ ostream& operator<<(ostream& out, const StackTraceElement& elm) { out << " func=" << elm.functionName() << "()+" << elm.functionOffset(); } - out << " build-id=" << hex << setw(8) << 0 + out << " build-id=" << hex << setw(8) << elm.buildId() << "}"; return out; @@ -121,5 +155,28 @@ ostream& operator<<(ostream& out, const vector& trace) { return out; } + +void logStackTrace(const vector& 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()); + } + } +} + } } diff --git a/libs/fbjni/src/main/cpp/include/fb/lyra.h b/libs/fbjni/cxx/lyra/lyra.h similarity index 67% rename from libs/fbjni/src/main/cpp/include/fb/lyra.h rename to libs/fbjni/cxx/lyra/lyra.h index 6b8bdf346..60698d1bb 100644 --- a/libs/fbjni/src/main/cpp/include/fb/lyra.h +++ b/libs/fbjni/cxx/lyra/lyra.h @@ -1,19 +1,25 @@ -/* - * Copyright (c) 2004-present, Facebook, Inc. +/** + * Copyright 2004-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 -#include #include #include -#include - namespace facebook { namespace lyra { @@ -21,17 +27,21 @@ constexpr size_t kDefaultLimit = 64; using InstructionPointer = const void*; -class FBEXPORT StackTraceElement { +class StackTraceElement { public: StackTraceElement(InstructionPointer absoluteProgramCounter, InstructionPointer libraryBase, - InstructionPointer functionAddress, std::string libraryName, + InstructionPointer functionAddress, + std::string libraryName, std::string functionName) : absoluteProgramCounter_{absoluteProgramCounter}, libraryBase_{libraryBase}, functionAddress_{functionAddress}, libraryName_{std::move(libraryName)}, - functionName_{std::move(functionName)} {} + functionName_{std::move(functionName)}, + hasBuildId_{false}, + buildId_{} + {} InstructionPointer libraryBase() const noexcept { return libraryBase_; } @@ -68,14 +78,28 @@ class FBEXPORT StackTraceElement { return absoluteabsoluteProgramCounter - absoluteSymbol; } + std::string buildId() const; private: const InstructionPointer absoluteProgramCounter_; const InstructionPointer libraryBase_; const InstructionPointer functionAddress_; const std::string libraryName_; 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 * @@ -92,8 +116,7 @@ class FBEXPORT StackTraceElement { * * @param skip The number of frames to skip before capturing the trace */ -FBEXPORT void getStackTrace(std::vector& stackTrace, - size_t skip = 0); +void getStackTrace(std::vector& stackTrace, size_t skip = 0); /** * Creates a vector and populates it with the current stack trace @@ -109,7 +132,7 @@ FBEXPORT void getStackTrace(std::vector& stackTrace, * * @limit The maximum number of frames captured */ -FBEXPORT inline std::vector getStackTrace( +inline std::vector getStackTrace( size_t skip = 0, size_t limit = kDefaultLimit) { auto stackTrace = std::vector{}; @@ -126,15 +149,15 @@ FBEXPORT inline std::vector getStackTrace( * * @param stackTrace The input stack trace */ -FBEXPORT void getStackTraceSymbols(std::vector& symbols, - const std::vector& trace); +void getStackTraceSymbols(std::vector& symbols, + const std::vector& trace); /** * Symbolicates a stack trace into a new vector * * @param stackTrace The input stack trace */ -FBEXPORT inline std::vector getStackTraceSymbols( +inline std::vector getStackTraceSymbols( const std::vector& trace) { auto symbols = std::vector{}; getStackTraceSymbols(symbols, trace); @@ -153,7 +176,7 @@ FBEXPORT inline std::vector getStackTraceSymbols( * * @param limit The maximum number of frames captured */ -FBEXPORT inline std::vector getStackTraceSymbols( +inline std::vector getStackTraceSymbols( size_t skip = 0, size_t limit = kDefaultLimit) { return getStackTraceSymbols(getStackTrace(skip + 1, limit)); @@ -162,12 +185,21 @@ FBEXPORT inline std::vector getStackTraceSymbols( /** * 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 */ -FBEXPORT std::ostream& operator<<(std::ostream& out, - const std::vector& trace); +std::ostream& operator<<(std::ostream& out, + const std::vector& 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& trace); + } } diff --git a/libs/fbjni/cxx/lyra/lyra_breakpad.cpp b/libs/fbjni/cxx/lyra/lyra_breakpad.cpp new file mode 100644 index 000000000..0c810f411 --- /dev/null +++ b/libs/fbjni/cxx/lyra/lyra_breakpad.cpp @@ -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 + +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 ""; +} + +} +} diff --git a/libs/fbjni/cxx/lyra/lyra_exceptions.cpp b/libs/fbjni/cxx/lyra/lyra_exceptions.cpp new file mode 100644 index 000000000..0664f8047 --- /dev/null +++ b/libs/fbjni/cxx/lyra/lyra_exceptions.cpp @@ -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 + +#include +#include +#include +#include + +#include + +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 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& 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"; + } +} + +} +} diff --git a/libs/fbjni/cxx/lyra/lyra_exceptions.h b/libs/fbjni/cxx/lyra/lyra_exceptions.h new file mode 100644 index 000000000..e04c276af --- /dev/null +++ b/libs/fbjni/cxx/lyra/lyra_exceptions.h @@ -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 +#include +#include + +#include + +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 stackTrace_; + }; + + template + struct Holder : E, ExceptionTraceHolder { + Holder(E&& e) : E{std::forward(e)}, ExceptionTraceHolder{} {} + }; + template + struct Holder : E { + Holder(E&& e) : E{std::forward(e)} {} + }; +} + +/** + * Retrieves the stack trace of an exception + */ +const std::vector& 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 +[[noreturn]] void fbthrow(E&& exception) { + throw detail::Holder::value>{std::forward(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); + +} +} diff --git a/libs/fbjni/java/com/facebook/jni/CppException.java b/libs/fbjni/java/com/facebook/jni/CppException.java new file mode 100644 index 000000000..b84425d2a --- /dev/null +++ b/libs/fbjni/java/com/facebook/jni/CppException.java @@ -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); + } +} diff --git a/libs/fbjni/java/com/facebook/jni/CppSystemErrorException.java b/libs/fbjni/java/com/facebook/jni/CppSystemErrorException.java new file mode 100644 index 000000000..a7067a0d4 --- /dev/null +++ b/libs/fbjni/java/com/facebook/jni/CppSystemErrorException.java @@ -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; + } +} diff --git a/libs/fbjni/java/com/facebook/jni/DestructorThread.java b/libs/fbjni/java/com/facebook/jni/DestructorThread.java new file mode 100644 index 000000000..233db8d4d --- /dev/null +++ b/libs/fbjni/java/com/facebook/jni/DestructorThread.java @@ -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. + * + *

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. + * + *

The underlying thread in DestructorThread starts when the first Destructor is constructed and + * then runs indefinitely. + */ +public class DestructorThread { + + /** + * N.B The Destructor SHOULD NOT 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 { + + 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 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; + } + } +} diff --git a/libs/fbjni/java/com/facebook/jni/HybridClassBase.java b/libs/fbjni/java/com/facebook/jni/HybridClassBase.java new file mode 100644 index 000000000..c850dabaf --- /dev/null +++ b/libs/fbjni/java/com/facebook/jni/HybridClassBase.java @@ -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 {} diff --git a/libs/fbjni/java/com/facebook/jni/HybridData.java b/libs/fbjni/java/com/facebook/jni/HybridData.java new file mode 100644 index 000000000..8a750bece --- /dev/null +++ b/libs/fbjni/java/com/facebook/jni/HybridData.java @@ -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. + * + *

NB: THREAD SAFETY + * + *

{@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("sonarfb"); + } + + @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: + * + *


+   * synchronized(hybrid) {
+   *   if (hybrid.isValid) {
+   *     // Do stuff.
+   *   }
+   * }
+   * 
+ */ + 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); + } +} diff --git a/libs/fbjni/src/main/java/com/facebook/jni/IteratorHelper.java b/libs/fbjni/java/com/facebook/jni/IteratorHelper.java similarity index 62% rename from libs/fbjni/src/main/java/com/facebook/jni/IteratorHelper.java rename to libs/fbjni/java/com/facebook/jni/IteratorHelper.java index 3a12ec090..852f7ec98 100644 --- a/libs/fbjni/src/main/java/com/facebook/jni/IteratorHelper.java +++ b/libs/fbjni/java/com/facebook/jni/IteratorHelper.java @@ -1,13 +1,21 @@ -/* - * 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. */ package com.facebook.jni; -import com.facebook.proguard.annotations.DoNotStrip; +import com.facebook.jni.annotations.DoNotStrip; import java.util.Iterator; import javax.annotation.Nullable; diff --git a/libs/fbjni/src/main/java/com/facebook/jni/MapIteratorHelper.java b/libs/fbjni/java/com/facebook/jni/MapIteratorHelper.java similarity index 63% rename from libs/fbjni/src/main/java/com/facebook/jni/MapIteratorHelper.java rename to libs/fbjni/java/com/facebook/jni/MapIteratorHelper.java index f9f44da69..342d549fe 100644 --- a/libs/fbjni/src/main/java/com/facebook/jni/MapIteratorHelper.java +++ b/libs/fbjni/java/com/facebook/jni/MapIteratorHelper.java @@ -1,13 +1,21 @@ -/* - * 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. */ package com.facebook.jni; -import com.facebook.proguard.annotations.DoNotStrip; +import com.facebook.jni.annotations.DoNotStrip; import java.util.Iterator; import java.util.Map; import javax.annotation.Nullable; diff --git a/libs/fbjni/java/com/facebook/jni/NativeRunnable.java b/libs/fbjni/java/com/facebook/jni/NativeRunnable.java new file mode 100644 index 000000000..a7b2cbf6a --- /dev/null +++ b/libs/fbjni/java/com/facebook/jni/NativeRunnable.java @@ -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(); +} diff --git a/libs/fbjni/java/com/facebook/jni/ThreadScopeSupport.java b/libs/fbjni/java/com/facebook/jni/ThreadScopeSupport.java new file mode 100644 index 000000000..82ed31ce4 --- /dev/null +++ b/libs/fbjni/java/com/facebook/jni/ThreadScopeSupport.java @@ -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("sonarfb"); + } + + // 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); +} diff --git a/libs/fbjni/java/com/facebook/jni/UnknownCppException.java b/libs/fbjni/java/com/facebook/jni/UnknownCppException.java new file mode 100644 index 000000000..3a0e70d79 --- /dev/null +++ b/libs/fbjni/java/com/facebook/jni/UnknownCppException.java @@ -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); + } +} diff --git a/libs/fbjni/java/com/facebook/jni/annotations/DoNotStrip.java b/libs/fbjni/java/com/facebook/jni/annotations/DoNotStrip.java new file mode 100644 index 000000000..7e7facd39 --- /dev/null +++ b/libs/fbjni/java/com/facebook/jni/annotations/DoNotStrip.java @@ -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 { +} diff --git a/libs/fbjni/src/main/AndroidManifest.xml b/libs/fbjni/src/main/AndroidManifest.xml deleted file mode 100644 index 8e81b59db..000000000 --- a/libs/fbjni/src/main/AndroidManifest.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - diff --git a/libs/fbjni/src/main/cpp/CMakeLists.txt b/libs/fbjni/src/main/cpp/CMakeLists.txt deleted file mode 100644 index 161471866..000000000 --- a/libs/fbjni/src/main/cpp/CMakeLists.txt +++ /dev/null @@ -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) diff --git a/libs/fbjni/src/main/cpp/assert.cpp b/libs/fbjni/src/main/cpp/assert.cpp deleted file mode 100644 index aaa999e13..000000000 --- a/libs/fbjni/src/main/cpp/assert.cpp +++ /dev/null @@ -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 -#include - -#include -#include - -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 diff --git a/libs/fbjni/src/main/cpp/include/fb/ALog.h b/libs/fbjni/src/main/cpp/include/fb/ALog.h deleted file mode 100644 index e9cfb8351..000000000 --- a/libs/fbjni/src/main/cpp/include/fb/ALog.h +++ /dev/null @@ -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 - -namespace facebook { -namespace alog { - -template -inline void log(int level, const char* tag, const char* msg, ARGS... args) noexcept { - __android_log_print(level, tag, msg, args...); -} - -template -inline void log(int level, const char* tag, const char* msg) noexcept { - __android_log_write(level, tag, msg); -} - -template -inline void logv(const char* tag, const char* msg, ARGS... args) noexcept { - log(ANDROID_LOG_VERBOSE, tag, msg, args...); -} - -template -inline void logd(const char* tag, const char* msg, ARGS... args) noexcept { - log(ANDROID_LOG_DEBUG, tag, msg, args...); -} - -template -inline void logi(const char* tag, const char* msg, ARGS... args) noexcept { - log(ANDROID_LOG_INFO, tag, msg, args...); -} - -template -inline void logw(const char* tag, const char* msg, ARGS... args) noexcept { - log(ANDROID_LOG_WARN, tag, msg, args...); -} - -template -inline void loge(const char* tag, const char* msg, ARGS... args) noexcept { - log(ANDROID_LOG_ERROR, tag, msg, args...); -} - -template -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 diff --git a/libs/fbjni/src/main/cpp/include/fb/Countable.h b/libs/fbjni/src/main/cpp/include/fb/Countable.h deleted file mode 100644 index e50298ba1..000000000 --- a/libs/fbjni/src/main/cpp/include/fb/Countable.h +++ /dev/null @@ -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 -#include -#include -#include -#include - -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 friend class RefPtr; - std::atomic m_refcount; -}; - -} diff --git a/libs/fbjni/src/main/cpp/include/fb/Doxyfile b/libs/fbjni/src/main/cpp/include/fb/Doxyfile deleted file mode 100644 index 8b4df6a7c..000000000 --- a/libs/fbjni/src/main/cpp/include/fb/Doxyfile +++ /dev/null @@ -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 diff --git a/libs/fbjni/src/main/cpp/include/fb/ProgramLocation.h b/libs/fbjni/src/main/cpp/include/fb/ProgramLocation.h deleted file mode 100644 index d8785c118..000000000 --- a/libs/fbjni/src/main/cpp/include/fb/ProgramLocation.h +++ /dev/null @@ -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 -#include -#include - -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; -}; - -} diff --git a/libs/fbjni/src/main/cpp/include/fb/RefPtr.h b/libs/fbjni/src/main/cpp/include/fb/RefPtr.h deleted file mode 100644 index 4930fd4f6..000000000 --- a/libs/fbjni/src/main/cpp/include/fb/RefPtr.h +++ /dev/null @@ -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 -#include - -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 ref; -// -// Object creation requires explicit construction: -// RefPtr ref = createNew(...); -// -// Or if the constructor is not public: -// RefPtr ref = adoptRef(new Foo(...)); -// -// But you can implicitly create from nullptr: -// RefPtr maybeRef = cond ? ref : nullptr; -// -// Move/Copy Construction/Assignment are straightforward: -// RefPtr 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 barRef = static_cast>(ref); -// ref = barRef; -// -template -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& 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 - RefPtr(const RefPtr& ref, typename std::enable_if::value, U>::type* = nullptr) : - m_ptr(ref.get()) - { - refIfNecessary(m_ptr); - } - - RefPtr(RefPtr&& 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 - RefPtr(RefPtr&& ref, typename std::enable_if::value, U>::type* = nullptr) : - m_ptr(nullptr) - { - *this = std::move(ref); - } - - ~RefPtr() { - unrefIfNecessary(m_ptr); - m_ptr = nullptr; - } - - RefPtr& operator=(const RefPtr& 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& operator=(RefPtr&& ref) { - unrefIfNecessary(m_ptr); - m_ptr = ref.m_ptr; - ref.m_ptr = nullptr; - return *this; - } - - template - RefPtr& operator=(RefPtr&& 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 - explicit operator RefPtr () 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 assumeAlreadyReffed(T* ptr) { - return RefPtr(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 adoptRef(T* ptr) { - return RefPtr(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 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 -static inline RefPtr assumeAlreadyReffed(T* ptr) { - return RefPtr::assumeAlreadyReffed(ptr); -} - -// As above, but tolerant of nullptr. -template -static inline RefPtr assumeAlreadyReffedOrNull(T* ptr) { - return ptr ? RefPtr::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 -static inline RefPtr adoptRef(T* ptr) { - return RefPtr::adoptRef(ptr); -} - -template -static inline RefPtr createNew(Args&&... arguments) { - return RefPtr::adoptRef(new T(std::forward(arguments)...)); -} - -template template -RefPtr::operator RefPtr() const { - static_assert(std::is_base_of::value, "Invalid static cast"); - return assumeAlreadyReffedOrNull(static_cast(m_ptr)); -} - -template -inline bool operator==(const RefPtr& a, const RefPtr& b) { - return a.get() == b.get(); -} - -template -inline bool operator!=(const RefPtr& a, const RefPtr& b) { - return a.get() != b.get(); -} - -template -inline bool operator==(const RefPtr& ref, U* ptr) { - return ref.get() == ptr; -} - -template -inline bool operator!=(const RefPtr& ref, U* ptr) { - return ref.get() != ptr; -} - -template -inline bool operator==(U* ptr, const RefPtr& ref) { - return ref.get() == ptr; -} - -template -inline bool operator!=(U* ptr, const RefPtr& ref) { - return ref.get() != ptr; -} - -template -inline bool operator==(const RefPtr& ref, std::nullptr_t ptr) { - return ref.get() == ptr; -} - -template -inline bool operator!=(const RefPtr& ref, std::nullptr_t ptr) { - return ref.get() != ptr; -} - -template -inline bool operator==(std::nullptr_t ptr, const RefPtr& ref) { - return ref.get() == ptr; -} - -template -inline bool operator!=(std::nullptr_t ptr, const RefPtr& ref) { - return ref.get() != ptr; -} - -} diff --git a/libs/fbjni/src/main/cpp/include/fb/StaticInitialized.h b/libs/fbjni/src/main/cpp/include/fb/StaticInitialized.h deleted file mode 100644 index c9e848b17..000000000 --- a/libs/fbjni/src/main/cpp/include/fb/StaticInitialized.h +++ /dev/null @@ -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 -#include - -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 -class StaticInitialized { -public: - constexpr StaticInitialized() : - m_instance(nullptr) - {} - - template - void initialize(Args&&... arguments) { - FBASSERT(!m_instance); - m_instance = new T(std::forward(arguments)...); - } - - T* operator->() const { - return m_instance; - } -private: - T* m_instance; -}; - -} diff --git a/libs/fbjni/src/main/cpp/include/fb/ThreadLocal.h b/libs/fbjni/src/main/cpp/include/fb/ThreadLocal.h deleted file mode 100644 index 53672e196..000000000 --- a/libs/fbjni/src/main/cpp/include/fb/ThreadLocal.h +++ /dev/null @@ -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 -#include - -#include - -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 static_object; - * static_object->data_ = ...; - * static_object->doSomething(); - * - * ThreadLocal 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 -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; -}; - -} diff --git a/libs/fbjni/src/main/cpp/include/fb/assert.h b/libs/fbjni/src/main/cpp/include/fb/assert.h deleted file mode 100644 index ed91efa4d..000000000 --- a/libs/fbjni/src/main/cpp/include/fb/assert.h +++ /dev/null @@ -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 - -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 diff --git a/libs/fbjni/src/main/cpp/include/fb/fbjni.h b/libs/fbjni/src/main/cpp/include/fb/fbjni.h deleted file mode 100644 index 230713346..000000000 --- a/libs/fbjni/src/main/cpp/include/fb/fbjni.h +++ /dev/null @@ -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 - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include diff --git a/libs/fbjni/src/main/cpp/include/fb/fbjni/ByteBuffer.h b/libs/fbjni/src/main/cpp/include/fb/fbjni/ByteBuffer.h deleted file mode 100644 index 7c10de344..000000000 --- a/libs/fbjni/src/main/cpp/include/fb/fbjni/ByteBuffer.h +++ /dev/null @@ -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 - -#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 { - 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/src/main/cpp/include/fb/fbjni/Context.h b/libs/fbjni/src/main/cpp/include/fb/fbjni/Context.h deleted file mode 100644 index 90e4b4a6c..000000000 --- a/libs/fbjni/src/main/cpp/include/fb/fbjni/Context.h +++ /dev/null @@ -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 { - public: - static constexpr const char* kJavaDescriptor = "Landroid/content/Context;"; - - // Define a method that calls into the represented Java class - local_ref getCacheDir() { - static auto method = getClass()->getMethod("getCacheDir"); - return method(self()); - } - - local_ref getFilesDir() { - static auto method = getClass()->getMethod("getFilesDir"); - return method(self()); - } -}; - -} -} diff --git a/libs/fbjni/src/main/cpp/include/fb/fbjni/File.h b/libs/fbjni/src/main/cpp/include/fb/fbjni/File.h deleted file mode 100644 index 658ccb780..000000000 --- a/libs/fbjni/src/main/cpp/include/fb/fbjni/File.h +++ /dev/null @@ -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 { - 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("getAbsolutePath"); - return method(self())->toStdString(); - } - -}; - -} -} diff --git a/libs/fbjni/src/main/cpp/include/fb/fbjni/JThread.h b/libs/fbjni/src/main/cpp/include/fb/fbjni/JThread.h deleted file mode 100644 index 7dae9214b..000000000 --- a/libs/fbjni/src/main/cpp/include/fb/fbjni/JThread.h +++ /dev/null @@ -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 { - public: - static constexpr const char* kJavaDescriptor = "Ljava/lang/Thread;"; - - void start() { - static auto method = javaClassStatic()->getMethod("start"); - method(self()); - } - - void join() { - static 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)); - } -}; - -} -} diff --git a/libs/fbjni/src/main/cpp/include/fb/fbjni/JThrowable.h b/libs/fbjni/src/main/cpp/include/fb/fbjni/JThrowable.h deleted file mode 100644 index 475e54d14..000000000 --- a/libs/fbjni/src/main/cpp/include/fb/fbjni/JThrowable.h +++ /dev/null @@ -1,15 +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 { - constexpr static auto kJavaDescriptor = "Ljava/lang/Throwable;"; - - std::string getStackTrace() const; -}; diff --git a/libs/fbjni/src/main/cpp/include/fb/fbjni/Meta-forward.h b/libs/fbjni/src/main/cpp/include/fb/fbjni/Meta-forward.h deleted file mode 100644 index 2736b3145..000000000 --- a/libs/fbjni/src/main/cpp/include/fb/fbjni/Meta-forward.h +++ /dev/null @@ -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 -class JMethod; -template -class JStaticMethod; -template -class JNonvirtualMethod; -template -struct JConstructor; -template -class JField; -template -class JStaticField; - -/// Type traits for Java types (currently providing Java type descriptors) -template -struct jtype_traits; - -/// Type traits for Java methods (currently providing Java type descriptors) -template -struct jmethod_traits; - -}} diff --git a/libs/fbjni/src/main/cpp/include/fb/fbjni/Registration-inl.h b/libs/fbjni/src/main/cpp/include/fb/fbjni/Registration-inl.h deleted file mode 100644 index a25a88906..000000000 --- a/libs/fbjni/src/main/cpp/include/fb/fbjni/Registration-inl.h +++ /dev/null @@ -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 -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(obj), args...); - } catch (...) { - translatePendingCppExceptionToJavaException(); - } - } - }; - - // This intentionally erases the real type; JNI will do it anyway - return reinterpret_cast(&(funcWrapper::call)); -} - -template -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>(obj), args...); - } catch (...) { - translatePendingCppExceptionToJavaException(); - return R{}; - } - } - }; - - // This intentionally erases the real type; JNI will do it anyway - return reinterpret_cast(&(funcWrapper::call)); -} - -// registration wrappers for functions, with autoconversion of arguments. - -template -inline NativeMethodWrapper* exceptionWrapJNIMethod(void (*)(alias_ref, Args... args)) { - struct funcWrapper { - JNI_ENTRY_POINT static void call(JNIEnv*, jobject obj, - typename Convert::type>::jniType... args) { - try { - (*func)(static_cast>(obj), Convert::type>::fromJni(args)...); - } catch (...) { - translatePendingCppExceptionToJavaException(); - } - } - }; - - // This intentionally erases the real type; JNI will do it anyway - return reinterpret_cast(&(funcWrapper::call)); -} - -template -inline NativeMethodWrapper* exceptionWrapJNIMethod(R (*)(alias_ref, Args... args)) { - struct funcWrapper { - - JNI_ENTRY_POINT static typename Convert::type>::jniType call(JNIEnv*, jobject obj, - typename Convert::type>::jniType... args) { - try { - return Convert::type>::toJniRet( - (*func)(static_cast>(obj), Convert::type>::fromJni(args)...)); - } catch (...) { - using jniRet = typename Convert::type>::jniType; - translatePendingCppExceptionToJavaException(); - return jniRet{}; - } - } - }; - - // This intentionally erases the real type; JNI will do it anyway - return reinterpret_cast(&(funcWrapper::call)); -} - -// registration wrappers for non-static methods, with autoconvertion of arguments. - -template -inline NativeMethodWrapper* exceptionWrapJNIMethod(void (C::*method0)(Args... args)) { - struct funcWrapper { - JNI_ENTRY_POINT static void call(JNIEnv* env, jobject obj, - typename Convert::type>::jniType... args) { - try { - try { - auto aref = wrap_alias(static_cast(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(facebook::jni::cthis(aref)); - (cobj->*method)(Convert::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(&(funcWrapper::call)); -} - -template -inline NativeMethodWrapper* exceptionWrapJNIMethod(R (C::*method0)(Args... args)) { - struct funcWrapper { - - JNI_ENTRY_POINT static typename Convert::type>::jniType call(JNIEnv* env, jobject obj, - typename Convert::type>::jniType... args) { - try { - try { - auto aref = wrap_alias(static_cast(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(facebook::jni::cthis(aref)); - return Convert::type>::toJniRet( - (cobj->*method)(Convert::type>::fromJni(args)...)); - } catch (const std::exception& ex) { - C::mapException(ex); - throw; - } - } catch (...) { - using jniRet = typename Convert::type>::jniType; - translatePendingCppExceptionToJavaException(); - return jniRet{}; - } - } - }; - - // This intentionally erases the real type; JNI will do it anyway - return reinterpret_cast(&(funcWrapper::call)); -} - -template -inline std::string makeDescriptor(R (*)(JNIEnv*, C, Args... args)) { - return jmethod_traits::descriptor(); -} - -template -inline std::string makeDescriptor(R (*)(alias_ref, Args... args)) { - return jmethod_traits_from_cxx::descriptor(); -} - -template -inline std::string makeDescriptor(R (C::*)(Args... args)) { - return jmethod_traits_from_cxx::descriptor(); -} - -} - -}} diff --git a/libs/fbjni/src/main/cpp/include/fb/log.h b/libs/fbjni/src/main/cpp/include/fb/log.h deleted file mode 100644 index 5bc6fc296..000000000 --- a/libs/fbjni/src/main/cpp/include/fb/log.h +++ /dev/null @@ -1,337 +0,0 @@ -/* - * Copyright (c) 2005-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. - * - */ -/* - * 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 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 - -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef ANDROID -#include -#else -// These declarations are needed for our internal use even on non-Android -// builds. -// (they are borrowed from ) - -/* - * 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 diff --git a/libs/fbjni/src/main/cpp/include/fb/noncopyable.h b/libs/fbjni/src/main/cpp/include/fb/noncopyable.h deleted file mode 100644 index 100051a5b..000000000 --- a/libs/fbjni/src/main/cpp/include/fb/noncopyable.h +++ /dev/null @@ -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; -}; - -} diff --git a/libs/fbjni/src/main/cpp/include/fb/nonmovable.h b/libs/fbjni/src/main/cpp/include/fb/nonmovable.h deleted file mode 100644 index a9f8d0864..000000000 --- a/libs/fbjni/src/main/cpp/include/fb/nonmovable.h +++ /dev/null @@ -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; -}; - -} diff --git a/libs/fbjni/src/main/cpp/include/fb/visibility.h b/libs/fbjni/src/main/cpp/include/fb/visibility.h deleted file mode 100644 index 65f80b624..000000000 --- a/libs/fbjni/src/main/cpp/include/fb/visibility.h +++ /dev/null @@ -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"))) diff --git a/libs/fbjni/src/main/cpp/include/jni/Countable.h b/libs/fbjni/src/main/cpp/include/jni/Countable.h deleted file mode 100644 index 0b07e3228..000000000 --- a/libs/fbjni/src/main/cpp/include/jni/Countable.h +++ /dev/null @@ -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 - -#include -#include -#include - -namespace facebook { -namespace jni { - -FBEXPORT const RefPtr& countableFromJava(JNIEnv* env, jobject obj); - -template RefPtr extractRefPtr(JNIEnv* env, jobject obj) { - return static_cast>(countableFromJava(env, obj)); -} - -template RefPtr extractPossiblyNullRefPtr(JNIEnv* env, jobject obj) { - return obj ? extractRefPtr(env, obj) : nullptr; -} - -FBEXPORT void setCountableForJava(JNIEnv* env, jobject obj, RefPtr&& countable); - -void CountableOnLoad(JNIEnv* env); - -} } - diff --git a/libs/fbjni/src/main/cpp/include/jni/GlobalReference.h b/libs/fbjni/src/main/cpp/include/jni/GlobalReference.h deleted file mode 100644 index 1cac8a889..000000000 --- a/libs/fbjni/src/main/cpp/include/jni/GlobalReference.h +++ /dev/null @@ -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 -#include - -#include - -#include - -namespace facebook { namespace jni { - -template -class GlobalReference { - static_assert(std::is_convertible::value, - "GlobalReference 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& rhs) : - reference_{} { - reset(rhs.get()); - } - - GlobalReference& operator=(const GlobalReference& rhs) { - if (this == &rhs) { - return *this; - } - reset(rhs.get()); - return *this; - } - - explicit operator bool() const { - return (reference_ != nullptr); - } - - T get() const { - return reinterpret_cast(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_; -}; - -}} diff --git a/libs/fbjni/src/main/cpp/include/jni/LocalReference.h b/libs/fbjni/src/main/cpp/include/jni/LocalReference.h deleted file mode 100644 index 20955eac7..000000000 --- a/libs/fbjni/src/main/cpp/include/jni/LocalReference.h +++ /dev/null @@ -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 -#include - -#include - -#include - -namespace facebook { -namespace jni { - -template -struct LocalReferenceDeleter { - static_assert(std::is_convertible::value, - "LocalReferenceDeleter instantiated with type that is not convertible to jobject"); - void operator()(T localReference) { - if (localReference != nullptr) { - Environment::current()->DeleteLocalRef(localReference); - } - } - }; - -template -using LocalReference = - std::unique_ptr::type, LocalReferenceDeleter>; - -} } diff --git a/libs/fbjni/src/main/cpp/include/jni/Registration.h b/libs/fbjni/src/main/cpp/include/jni/Registration.h deleted file mode 100644 index 600c32c0c..000000000 --- a/libs/fbjni/src/main/cpp/include/jni/Registration.h +++ /dev/null @@ -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 -#include -#include - -namespace facebook { -namespace jni { - -static inline void registerNatives(JNIEnv* env, jclass cls, std::initializer_list 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 list) { - registerNatives(env, env->FindClass(cls), list); -} - -} } diff --git a/libs/fbjni/src/main/cpp/include/jni/WeakReference.h b/libs/fbjni/src/main/cpp/include/jni/WeakReference.h deleted file mode 100644 index e8bad1ace..000000000 --- a/libs/fbjni/src/main/cpp/include/jni/WeakReference.h +++ /dev/null @@ -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 -#include -#include -#include -#include - - -namespace facebook { -namespace jni { - -class FBEXPORT WeakReference : public Countable { -public: - typedef RefPtr 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& weakRef); - ~ResolvedWeakReference(); - - operator jobject () { - return m_strongReference; - } - - explicit operator bool () { - return m_strongReference != nullptr; - } - -private: - jobject m_strongReference; -}; - -} } - diff --git a/libs/fbjni/src/main/cpp/include/jni/jni_helpers.h b/libs/fbjni/src/main/cpp/include/jni/jni_helpers.h deleted file mode 100644 index b740d830b..000000000 --- a/libs/fbjni/src/main/cpp/include/jni/jni_helpers.h +++ /dev/null @@ -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 - -#include - -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 - diff --git a/libs/fbjni/src/main/cpp/jni/Countable.cpp b/libs/fbjni/src/main/cpp/jni/Countable.cpp deleted file mode 100644 index 4e84a9f0e..000000000 --- a/libs/fbjni/src/main/cpp/jni/Countable.cpp +++ /dev/null @@ -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 -#include -#include -#include - -namespace facebook { -namespace jni { - -static jfieldID gCountableNativePtr; - -static RefPtr* rawCountableFromJava(JNIEnv* env, jobject obj) { - FBASSERT(obj); - return reinterpret_cast*>(env->GetLongField(obj, gCountableNativePtr)); -} - -const RefPtr& countableFromJava(JNIEnv* env, jobject obj) { - FBASSERT(obj); - return *rawCountableFromJava(env, obj); -} - -void setCountableForJava(JNIEnv* env, jobject obj, RefPtr&& 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(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 = 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 }, - }); -} - -} } diff --git a/libs/fbjni/src/main/cpp/jni/Environment.cpp b/libs/fbjni/src/main/cpp/jni/Environment.cpp deleted file mode 100644 index 5be18b20f..000000000 --- a/libs/fbjni/src/main/cpp/jni/Environment.cpp +++ /dev/null @@ -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 -#include -#include -#include -#include -#include -#include - -#include - -namespace facebook { -namespace jni { - -namespace { -StaticInitialized> g_env; -JavaVM* g_vm = nullptr; - -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 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 */ -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&& 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)); -} - -} } - diff --git a/libs/fbjni/src/main/cpp/jni/Exceptions.cpp b/libs/fbjni/src/main/cpp/jni/Exceptions.cpp deleted file mode 100644 index a425dc476..000000000 --- a/libs/fbjni/src/main/cpp/jni/Exceptions.cpp +++ /dev/null @@ -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 - -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include - - -namespace facebook { -namespace jni { - -namespace { -class JRuntimeException : public JavaClass { - public: - static auto constexpr kJavaDescriptor = "Ljava/lang/RuntimeException;"; - - static local_ref create(const char* str) { - return newInstance(make_jstring(str)); - } - - static local_ref create() { - return newInstance(); - } -}; - -class JIOException : public JavaClass { - public: - static auto constexpr kJavaDescriptor = "Ljava/io/IOException;"; - - static local_ref create(const char* str) { - return newInstance(make_jstring(str)); - } -}; - -class JOutOfMemoryError : public JavaClass { - public: - static auto constexpr kJavaDescriptor = "Ljava/lang/OutOfMemoryError;"; - - static local_ref create(const char* str) { - return newInstance(make_jstring(str)); - } -}; - -class JArrayIndexOutOfBoundsException : public JavaClass { - public: - static auto constexpr kJavaDescriptor = "Ljava/lang/ArrayIndexOutOfBoundsException;"; - - static local_ref create(const char* str) { - return newInstance(make_jstring(str)); - } -}; - -class JUnknownCppException : public JavaClass { - public: - static auto constexpr kJavaDescriptor = "Lcom/facebook/jni/UnknownCppException;"; - - static local_ref create() { - return newInstance(); - } - - static local_ref create(const char* str) { - return newInstance(make_jstring(str)); - } -}; - -class JCppSystemErrorException : public JavaClass { - public: - static auto constexpr kJavaDescriptor = "Lcom/facebook/jni/CppSystemErrorException;"; - - static local_ref 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 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(), - 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 func) { - try { - throw; - } catch (const std::exception& e) { - try { - rethrow_if_nested(); - } catch (...) { - denest(func); - } - func(); - } catch (...) { - func(); - } -} -} - -void translatePendingCppExceptionToJavaException() noexcept { - local_ref previous; - auto func = [&previous] () { - local_ref 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 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 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_); -} - -}} diff --git a/libs/fbjni/src/main/cpp/jni/Hybrid.cpp b/libs/fbjni/src/main/cpp/jni/Hybrid.cpp deleted file mode 100644 index 45d5bff0b..000000000 --- a/libs/fbjni/src/main/cpp/jni/Hybrid.cpp +++ /dev/null @@ -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 new_value) { - static auto pointerField = getClass()->getField("mNativePointer"); - auto* old_value = reinterpret_cast(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(new_value.release())); -} - -BaseHybridClass* HybridData::getNativePointer() { - static auto pointerField = getClass()->getField("mNativePointer"); - auto* value = reinterpret_cast(getFieldValue(pointerField)); - if (!value) { - throwNewJavaException("java/lang/NullPointerException", "java.lang.NullPointerException"); - } - return value; -} - -local_ref HybridData::create() { - return newInstance(); -} - -} - -namespace { -void resetNative(alias_ref jthis) { - jthis->setNativePointer(nullptr); -} -} - -void HybridDataOnLoad() { - registerNatives("com/facebook/jni/HybridData", { - makeNativeMethod("resetNative", resetNative), - }); -} - -}} diff --git a/libs/fbjni/src/main/cpp/jni/JThrowable.cpp b/libs/fbjni/src/main/cpp/jni/JThrowable.cpp deleted file mode 100644 index cb2c8ae55..000000000 --- a/libs/fbjni/src/main/cpp/jni/JThrowable.cpp +++ /dev/null @@ -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 - -std::string JThrowable::getStackTrace() const { - static auto getStackTraceMethod = javaClassStatic() - ->getMethod()>("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(); -} diff --git a/libs/fbjni/src/main/cpp/jni/OnLoad.cpp b/libs/fbjni/src/main/cpp/jni/OnLoad.cpp deleted file mode 100644 index 9ba476055..000000000 --- a/libs/fbjni/src/main/cpp/jni/OnLoad.cpp +++ /dev/null @@ -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 -#include -#include -#include - -using namespace facebook::jni; - -void initialize_fbjni() { - CountableOnLoad(Environment::current()); - HybridDataOnLoad(); - JNativeRunnable::OnLoad(); - ThreadScope::OnLoad(); -} diff --git a/libs/fbjni/src/main/cpp/jni/References.cpp b/libs/fbjni/src/main/cpp/jni/References.cpp deleted file mode 100644 index e838de1f0..000000000 --- a/libs/fbjni/src/main/cpp/jni/References.cpp +++ /dev/null @@ -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 - -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; -} - -} - -} -} diff --git a/libs/fbjni/src/main/cpp/jni/WeakReference.cpp b/libs/fbjni/src/main/cpp/jni/WeakReference.cpp deleted file mode 100644 index ad0e0f1c7..000000000 --- a/libs/fbjni/src/main/cpp/jni/WeakReference.cpp +++ /dev/null @@ -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 -#include - -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& weakRef) : - m_strongReference(Environment::current()->NewLocalRef(weakRef->weakRef())) -{ -} - -ResolvedWeakReference::~ResolvedWeakReference() { - if (m_strongReference) - Environment::current()->DeleteLocalRef(m_strongReference); -} - -} } - diff --git a/libs/fbjni/src/main/cpp/jni/jni_helpers.cpp b/libs/fbjni/src/main/cpp/jni/jni_helpers.cpp deleted file mode 100644 index c1bfb5cd8..000000000 --- a/libs/fbjni/src/main/cpp/jni/jni_helpers.cpp +++ /dev/null @@ -1,195 +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 -#include -#include - -#include - -#define MSG_SIZE 1024 - -namespace facebook { - -/** - * Instructs the JNI environment to throw an exception. - * - * @param pEnv JNI environment - * @param szClassName class name to throw - * @param szFmt sprintf-style format string - * @param ... sprintf-style args - * @return 0 on success; a negative value on failure - */ -jint throwException(JNIEnv* pEnv, const char* szClassName, const char* szFmt, va_list va_args) { - char szMsg[MSG_SIZE]; - vsnprintf(szMsg, MSG_SIZE, szFmt, va_args); - jclass exClass = pEnv->FindClass(szClassName); - return pEnv->ThrowNew(exClass, szMsg); -} - -/** - * Instructs the JNI environment to throw a NoClassDefFoundError. - * - * @param pEnv JNI environment - * @param szFmt sprintf-style format string - * @param ... sprintf-style args - * @return 0 on success; a negative value on failure - */ -jint throwNoClassDefError(JNIEnv* pEnv, const char* szFmt, ...) { - va_list va_args; - va_start(va_args, szFmt); - jint ret = throwException(pEnv, "java/lang/NoClassDefFoundError", szFmt, va_args); - va_end(va_args); - return ret; -} - -/** - * Instructs the JNI environment to throw a RuntimeException. - * - * @param pEnv JNI environment - * @param szFmt sprintf-style format string - * @param ... sprintf-style args - * @return 0 on success; a negative value on failure - */ -jint throwRuntimeException(JNIEnv* pEnv, const char* szFmt, ...) { - va_list va_args; - va_start(va_args, szFmt); - jint ret = throwException(pEnv, "java/lang/RuntimeException", szFmt, va_args); - va_end(va_args); - return ret; -} - -/** - * Instructs the JNI environment to throw an IllegalArgumentException. - * - * @param pEnv JNI environment - * @param szFmt sprintf-style format string - * @param ... sprintf-style args - * @return 0 on success; a negative value on failure - */ -jint throwIllegalArgumentException(JNIEnv* pEnv, const char* szFmt, ...) { - va_list va_args; - va_start(va_args, szFmt); - jint ret = throwException(pEnv, "java/lang/IllegalArgumentException", szFmt, va_args); - va_end(va_args); - return ret; -} - -/** - * Instructs the JNI environment to throw an IllegalStateException. - * - * @param pEnv JNI environment - * @param szFmt sprintf-style format string - * @param ... sprintf-style args - * @return 0 on success; a negative value on failure - */ -jint throwIllegalStateException(JNIEnv* pEnv, const char* szFmt, ...) { - va_list va_args; - va_start(va_args, szFmt); - jint ret = throwException(pEnv, "java/lang/IllegalStateException", szFmt, va_args); - va_end(va_args); - return ret; -} - -/** - * Instructs the JNI environment to throw an OutOfMemoryError. - * - * @param pEnv JNI environment - * @param szFmt sprintf-style format string - * @param ... sprintf-style args - * @return 0 on success; a negative value on failure - */ -jint throwOutOfMemoryError(JNIEnv* pEnv, const char* szFmt, ...) { - va_list va_args; - va_start(va_args, szFmt); - jint ret = throwException(pEnv, "java/lang/OutOfMemoryError", szFmt, va_args); - va_end(va_args); - return ret; -} - -/** - * Instructs the JNI environment to throw an AssertionError. - * - * @param pEnv JNI environment - * @param szFmt sprintf-style format string - * @param ... sprintf-style args - * @return 0 on success; a negative value on failure - */ -jint throwAssertionError(JNIEnv* pEnv, const char* szFmt, ...) { - va_list va_args; - va_start(va_args, szFmt); - jint ret = throwException(pEnv, "java/lang/AssertionError", szFmt, va_args); - va_end(va_args); - return ret; -} - -/** - * Instructs the JNI environment to throw an IOException. - * - * @param pEnv JNI environment - * @param szFmt sprintf-style format string - * @param ... sprintf-style args - * @return 0 on success; a negative value on failure - */ -jint throwIOException(JNIEnv* pEnv, const char* szFmt, ...) { - va_list va_args; - va_start(va_args, szFmt); - jint ret = throwException(pEnv, "java/io/IOException", szFmt, va_args); - va_end(va_args); - return ret; -} - -/** - * Finds the specified class. If it's not found, instructs the JNI environment to throw an - * exception. - * - * @param pEnv JNI environment - * @param szClassName the classname to find in JNI format (e.g. "java/lang/String") - * @return the class or NULL if not found (in which case a pending exception will be queued). This - * returns a global reference (JNIEnv::NewGlobalRef). - */ -jclass findClassOrThrow(JNIEnv* pEnv, const char* szClassName) { - jclass clazz = pEnv->FindClass(szClassName); - if (!clazz) { - return NULL; - } - return (jclass) pEnv->NewGlobalRef(clazz); -} - -/** - * Finds the specified field of the specified class. If it's not found, instructs the JNI - * environment to throw an exception. - * - * @param pEnv JNI environment - * @param clazz the class to lookup the field in - * @param szFieldName the name of the field to find - * @param szSig the signature of the field - * @return the field or NULL if not found (in which case a pending exception will be queued) - */ -jfieldID getFieldIdOrThrow(JNIEnv* pEnv, jclass clazz, const char* szFieldName, const char* szSig) { - return pEnv->GetFieldID(clazz, szFieldName, szSig); -} - -/** - * Finds the specified method of the specified class. If it's not found, instructs the JNI - * environment to throw an exception. - * - * @param pEnv JNI environment - * @param clazz the class to lookup the method in - * @param szMethodName the name of the method to find - * @param szSig the signature of the method - * @return the method or NULL if not found (in which case a pending exception will be queued) - */ -jmethodID getMethodIdOrThrow( - JNIEnv* pEnv, - jclass clazz, - const char* szMethodName, - const char* szSig) { - return pEnv->GetMethodID(clazz, szMethodName, szSig); -} - -} // namespace facebook diff --git a/libs/fbjni/src/main/cpp/log.cpp b/libs/fbjni/src/main/cpp/log.cpp deleted file mode 100644 index 5504ebf60..000000000 --- a/libs/fbjni/src/main/cpp/log.cpp +++ /dev/null @@ -1,98 +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 -#include -#include -#include - -#define LOG_BUFFER_SIZE 4096 -static LogHandler gLogHandler; - -void setLogHandler(LogHandler logHandler) { - gLogHandler = logHandler; -} - -int fb_printLog(int prio, const char *tag, const char *fmt, ...) { - char logBuffer[LOG_BUFFER_SIZE]; - - va_list va_args; - va_start(va_args, fmt); - int result = vsnprintf(logBuffer, sizeof(logBuffer), fmt, va_args); - va_end(va_args); - if (gLogHandler != NULL) { - gLogHandler(prio, tag, logBuffer); - } - __android_log_write(prio, tag, logBuffer); - return result; -} - -void logPrintByDelims(int priority, const char* tag, const char* delims, - const char* msg, ...) -{ - va_list ap; - char buf[32768]; - char* context; - char* tok; - - va_start(ap, msg); - vsnprintf(buf, sizeof(buf), msg, ap); - va_end(ap); - - tok = strtok_r(buf, delims, &context); - - if (!tok) { - return; - } - - do { - __android_log_write(priority, tag, tok); - } while ((tok = strtok_r(NULL, delims, &context))); -} - -#ifndef ANDROID - -// Implementations of the basic android logging functions for non-android platforms. - -static char logTagChar(int prio) { - switch (prio) { - default: - case ANDROID_LOG_UNKNOWN: - case ANDROID_LOG_DEFAULT: - case ANDROID_LOG_SILENT: - return ' '; - case ANDROID_LOG_VERBOSE: - return 'V'; - case ANDROID_LOG_DEBUG: - return 'D'; - case ANDROID_LOG_INFO: - return 'I'; - case ANDROID_LOG_WARN: - return 'W'; - case ANDROID_LOG_ERROR: - return 'E'; - case ANDROID_LOG_FATAL: - return 'F'; - } -} - -int __android_log_write(int prio, const char *tag, const char *text) { - return fprintf(stderr, "[%c/%.16s] %s\n", logTagChar(prio), tag, text); -} - -int __android_log_print(int prio, const char *tag, const char *fmt, ...) { - va_list ap; - va_start(ap, fmt); - - int res = fprintf(stderr, "[%c/%.16s] ", logTagChar(prio), tag); - res += vfprintf(stderr, "%s\n", ap); - - va_end(ap); - return res; -} - -#endif diff --git a/libs/fbjni/src/main/cpp/onload.cpp b/libs/fbjni/src/main/cpp/onload.cpp deleted file mode 100644 index f7b1e5d89..000000000 --- a/libs/fbjni/src/main/cpp/onload.cpp +++ /dev/null @@ -1,29 +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 -#ifndef DISABLE_CPUCAP -#include -#endif -#include - -using namespace facebook::jni; - -void initialize_xplatinit(); -void initialize_fbjni(); - -JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) { - return facebook::jni::initialize(vm, [] { - initialize_fbjni(); -#ifndef DISABLE_XPLAT - initialize_xplatinit(); -#endif -#ifndef DISABLE_CPUCAP - initialize_cpucapabilities(); -#endif - }); -} diff --git a/libs/fbjni/src/main/java/com/facebook/jni/Countable.java b/libs/fbjni/src/main/java/com/facebook/jni/Countable.java deleted file mode 100644 index 4e4280b52..000000000 --- a/libs/fbjni/src/main/java/com/facebook/jni/Countable.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2004-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. - * - */ -package com.facebook.jni; - -import com.facebook.proguard.annotations.DoNotStrip; - -/** - * A Java Object that has native memory allocated corresponding to this instance. - * - *

NB: THREAD SAFETY (this comment also exists at Countable.cpp) - * - *

{@link #dispose} deletes the corresponding native object on whatever thread the method is - * called on. In the common case when this is called by Countable#finalize(), this will be called on - * the system finalizer thread. If you manually call dispose on the Java object, the native object - * will be deleted synchronously on that thread. - */ -@DoNotStrip -public class Countable { - - // Private C++ instance - @DoNotStrip private long mInstance = 0; - - public native void dispose(); - - protected void finalize() throws Throwable { - dispose(); - super.finalize(); - } -} diff --git a/libs/fbjni/src/main/java/com/facebook/jni/CppException.java b/libs/fbjni/src/main/java/com/facebook/jni/CppException.java deleted file mode 100644 index 1d3f7f7e6..000000000 --- a/libs/fbjni/src/main/java/com/facebook/jni/CppException.java +++ /dev/null @@ -1,18 +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. - * - */ -package com.facebook.jni; - -import com.facebook.proguard.annotations.DoNotStrip; - -@DoNotStrip -public class CppException extends RuntimeException { - @DoNotStrip - public CppException(String message) { - super(message); - } -} diff --git a/libs/fbjni/src/main/java/com/facebook/jni/CppSystemErrorException.java b/libs/fbjni/src/main/java/com/facebook/jni/CppSystemErrorException.java deleted file mode 100644 index c4c08cf43..000000000 --- a/libs/fbjni/src/main/java/com/facebook/jni/CppSystemErrorException.java +++ /dev/null @@ -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. - * - */ -package com.facebook.jni; - -import com.facebook.proguard.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; - } -} diff --git a/libs/fbjni/src/main/java/com/facebook/jni/HybridClassBase.java b/libs/fbjni/src/main/java/com/facebook/jni/HybridClassBase.java deleted file mode 100644 index 76a5e652f..000000000 --- a/libs/fbjni/src/main/java/com/facebook/jni/HybridClassBase.java +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright (c) 2004-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. - * - */ - -package com.facebook.jni; - -import com.facebook.proguard.annotations.DoNotStrip; - -@DoNotStrip -public abstract class HybridClassBase extends HybridData {} diff --git a/libs/fbjni/src/main/java/com/facebook/jni/HybridData.java b/libs/fbjni/src/main/java/com/facebook/jni/HybridData.java deleted file mode 100644 index ed9e43cca..000000000 --- a/libs/fbjni/src/main/java/com/facebook/jni/HybridData.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2004-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. - * - */ -package com.facebook.jni; - -import com.facebook.proguard.annotations.DoNotStrip; - -/** - * This object holds a native C++ member for hybrid Java/C++ objects. - * - *

NB: THREAD SAFETY - * - *

{@link #dispose} deletes the corresponding native object on whatever thread the method is - * called on. In the common case when this is called by HybridData#finalize(), this will be called - * on the system finalizer thread. If you manually call resetNative() on the Java object, the C++ - * object will be deleted synchronously on that thread. - */ -@DoNotStrip -public class HybridData { - - // Private C++ instance - @DoNotStrip private long mNativePointer = 0; - - /** - * 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 {@link #finalize} 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 native void resetNative(); - - protected void finalize() throws Throwable { - resetNative(); - super.finalize(); - } - - public boolean isValid() { - return mNativePointer != 0; - } -} diff --git a/libs/fbjni/src/main/java/com/facebook/jni/NativeRunnable.java b/libs/fbjni/src/main/java/com/facebook/jni/NativeRunnable.java deleted file mode 100644 index ecc3fc14e..000000000 --- a/libs/fbjni/src/main/java/com/facebook/jni/NativeRunnable.java +++ /dev/null @@ -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. - * - */ -package com.facebook.jni; - -import com.facebook.proguard.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(); -} diff --git a/libs/fbjni/src/main/java/com/facebook/jni/ThreadScopeSupport.java b/libs/fbjni/src/main/java/com/facebook/jni/ThreadScopeSupport.java deleted file mode 100644 index a23b46aff..000000000 --- a/libs/fbjni/src/main/java/com/facebook/jni/ThreadScopeSupport.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (c) 2004-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. - * - */ -package com.facebook.jni; - -import com.facebook.proguard.annotations.DoNotStrip; - -@DoNotStrip -public class ThreadScopeSupport { - // 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); -} diff --git a/libs/fbjni/src/main/java/com/facebook/jni/UnknownCppException.java b/libs/fbjni/src/main/java/com/facebook/jni/UnknownCppException.java deleted file mode 100644 index 4ffc63acb..000000000 --- a/libs/fbjni/src/main/java/com/facebook/jni/UnknownCppException.java +++ /dev/null @@ -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. - * - */ -package com.facebook.jni; - -import com.facebook.proguard.annotations.DoNotStrip; - -@DoNotStrip -public class UnknownCppException extends CppException { - @DoNotStrip - public UnknownCppException() { - super("Unknown"); - } - - @DoNotStrip - public UnknownCppException(String message) { - super(message); - } -} diff --git a/libs/fbjni/src/main/java/com/facebook/jni/fbjni.pro b/libs/fbjni/src/main/java/com/facebook/jni/fbjni.pro deleted file mode 100644 index 5b5b6454d..000000000 --- a/libs/fbjni/src/main/java/com/facebook/jni/fbjni.pro +++ /dev/null @@ -1,11 +0,0 @@ -# For common use cases for the hybrid pattern, keep symbols which may -# be referenced only from C++. - --keepclassmembers class * { - com.facebook.jni.HybridData *; - (com.facebook.jni.HybridData); -} - --keepclasseswithmembers class * { - com.facebook.jni.HybridData *; -} diff --git a/settings.gradle b/settings.gradle index f004675f2..409b707b2 100644 --- a/settings.gradle +++ b/settings.gradle @@ -17,6 +17,10 @@ include ':sonarcpp' include ':sample' include ':doubleconversion' include ':glog' +include ':libevent' +include ':openssl' +include ':rsocket' + project(':fbjni').projectDir = file('libs/fbjni') @@ -27,3 +31,6 @@ project(':android').projectDir = file('android') project(':doubleconversion').projectDir = file('android/build/third-party-ndk/double-conversion/') project(':glog').projectDir = file('android/build/third-party-ndk/glog/') project(':folly').projectDir = file('android/build/third-party-ndk/folly/') +project(':libevent').projectDir = file('android/build/third-party-ndk/LibEvent/') +project(':openssl').projectDir = file('android/build/third-party-ndk/OpenSSL/openssl-android-1.0.0') +project(':rsocket').projectDir = file('android/build/third-party-ndk/RSocket') diff --git a/xplat/CMakeLists.txt b/xplat/CMakeLists.txt index c5d82dcd0..2e4e68f54 100644 --- a/xplat/CMakeLists.txt +++ b/xplat/CMakeLists.txt @@ -2,17 +2,22 @@ cmake_minimum_required (VERSION 3.6.0) project(sonarcpp CXX) set(CMAKE_VERBOSE_MAKEFILE on) set(PACKAGE_NAME sonarcpp) +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_EXTENSIONS OFF) -set(third_party_ndk ../android/build/third-party-ndk) +set(third_party_ndk ${PROJECT_SOURCE_DIR}/../android/build/third-party-ndk) set(libfolly_DIR ${third_party_ndk}/folly/) +set(rsocket_DIR ${third_party_ndk}/RSocket/) set(easywsclient_DIR ../libs/) set(glog_DIR ${third_party_ndk}/glog) set(BOOST_DIR ${third_party_ndk}/boost/boost_1_63_0/) - +set(LIBEVENT_DIR ${third_party_ndk}/LibEvent/libevent-release-2.1.9/) set(DOUBLECONVERSION_DIR ${third_party_ndk}/double-conversion/double-conversion-3.0.0/) +set(OPENSSL_DIR ${third_party_ndk}/OpenSSL/openssl-android-1.0.0/) list(APPEND dir_list ./) list(APPEND dir_list ./Sonar) + include_directories(${dir_list}) add_compile_options(-DFOLLY_NO_CONFIG @@ -24,7 +29,6 @@ add_compile_options(-DFOLLY_NO_CONFIG -DFOLLY_HAVE_PREADV=0 -frtti -fexceptions - -std=c++14 -Wno-error -Wno-unused-local-typedefs -Wno-unused-variable @@ -40,21 +44,32 @@ add_library(${PACKAGE_NAME} SHARED ${SOURCES}) set(build_DIR ${CMAKE_SOURCE_DIR}/build) set(libfolly_build_DIR ${build_DIR}/libfolly/${ANDROID_ABI}) -set(easywsclient_build_DIR ${build_DIR}/easywsclient/${ANDROID_ABI}) +set(rsocket_build_DIR ${build_DIR}/rsocket/${ANDROID_ABI}) file(MAKE_DIRECTORY ${build_DIR}) -add_subdirectory(${libfolly_DIR} ${libfolly_build_DIR}) -add_subdirectory(${easywsclient_DIR}/easywsclient ${easywsclient_build_DIR}) +#add_subdirectory(${libfolly_DIR} ${libfolly_build_DIR}) +add_subdirectory(${rsocket_DIR} ${rsocket_build_DIR}) + +message(STATUS "RSocket DIR:- " ${rsocket_DIR}) target_include_directories(${PACKAGE_NAME} PRIVATE ${libfolly_DIR} ${BOOST_DIR} ${BOOST_DIR}/../ + ${LIBEVENT_DIR}/ + ${rsocket_DIR}/rsocket-cpp-0.10.1 + ${LIBEVENT_DIR}/include/ + ${LIBEVENT_DIR}/include/event2 + ${OPENSSL_DIR}/jni/openssl-android/ + ${OPENSSL_DIR}/jni/openssl-android/include + ${OPENSSL_DIR}/jni/openssl-android/include/openssl ${glog_DIR} ${glog_DIR}/../ ${glog_DIR}/glog-0.3.5/src/ - ${easywsclient_DIR} ) -target_link_libraries(${PACKAGE_NAME} folly easywsclient glog double-conversion log) +set(OPENSSL_LINK_DIRECTORIES ${third_party_ndk}/OpenSSL/libs/${ANDROID_ABI}/) +find_path(OPENSSL_LIBRARY libssl.so HINTS ${OPENSSL_LINK_DIRECTORIES}) + +target_link_libraries(${PACKAGE_NAME} folly rsocket glog double-conversion log event ${OPENSSL_LINK_DIRECTORIES}/libssl.so ${OPENSSL_LINK_DIRECTORIES}/libcrypto.so) diff --git a/xplat/build.gradle b/xplat/build.gradle index 7ce5ad5e6..6655250fd 100644 --- a/xplat/build.gradle +++ b/xplat/build.gradle @@ -9,7 +9,7 @@ android { targetSdkVersion rootProject.targetSdkVersion ndk { - abiFilters 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a' + abiFilters 'arm64-v8a', 'x86' } externalNativeBuild { @@ -31,7 +31,8 @@ android { } dependencies { - implementation project(':easywsclient') + implementation project(':rsocket') implementation project(':folly') } } +//'armeabi-v7a'