mirror of
https://github.com/status-im/react-native.git
synced 2025-02-22 14:18:23 +00:00
Remove RN files which use JSC
Summary: JSI+JSCRuntime replaces direct use of JSC @public Reviewed By: danzimm Differential Revision: D9328235 fbshipit-source-id: a4d0cad8250ef2da058ffffbdedbffa19f96bb12
This commit is contained in:
parent
00381920c6
commit
7ffb406517
@ -1,125 +0,0 @@
|
||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
||||
|
||||
// This source code is licensed under the MIT license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
|
||||
#include <cxxreact/JSCExecutor.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <cxxreact/Platform.h>
|
||||
#include <fb/fbjni.h>
|
||||
#include <folly/Conv.h>
|
||||
#include <folly/dynamic.h>
|
||||
#include <folly/Memory.h>
|
||||
#include <jschelpers/JSCHelpers.h>
|
||||
|
||||
#include "JSCPerfLogging.h"
|
||||
#include "JSLogging.h"
|
||||
#include "JReactMarker.h"
|
||||
|
||||
using namespace facebook::jni;
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
namespace {
|
||||
|
||||
ExceptionHandling::ExtractedEror extractJniError(const std::exception& ex, const char *context) {
|
||||
auto jniEx = dynamic_cast<const jni::JniException *>(&ex);
|
||||
if (!jniEx) {
|
||||
return {};
|
||||
}
|
||||
|
||||
auto stackTrace = jniEx->getThrowable()->getStackTrace();
|
||||
std::ostringstream stackStr;
|
||||
for (int i = 0, count = stackTrace->size(); i < count; ++i) {
|
||||
auto frame = stackTrace->getElement(i);
|
||||
|
||||
auto methodName = folly::to<std::string>(frame->getClassName(), ".",
|
||||
frame->getMethodName());
|
||||
|
||||
// Cut off stack traces at the Android looper, to keep them simple
|
||||
if (methodName == "android.os.Looper.loop") {
|
||||
break;
|
||||
}
|
||||
|
||||
stackStr << std::move(methodName) << '@' << frame->getFileName();
|
||||
if (frame->getLineNumber() > 0) {
|
||||
stackStr << ':' << frame->getLineNumber();
|
||||
}
|
||||
stackStr << std::endl;
|
||||
}
|
||||
|
||||
auto msg = folly::to<std::string>("Java exception in '", context, "'\n\n", jniEx->what());
|
||||
return {.message = msg, .stack = stackStr.str()};
|
||||
}
|
||||
|
||||
JSValueRef nativePerformanceNow(
|
||||
JSContextRef ctx,
|
||||
JSObjectRef function,
|
||||
JSObjectRef thisObject,
|
||||
size_t argumentCount,
|
||||
const JSValueRef arguments[], JSValueRef *exception) {
|
||||
static const int64_t NANOSECONDS_IN_SECOND = 1000000000LL;
|
||||
static const int64_t NANOSECONDS_IN_MILLISECOND = 1000000LL;
|
||||
|
||||
// Since SystemClock.uptimeMillis() is commonly used for performance measurement in Java
|
||||
// and uptimeMillis() internally uses clock_gettime(CLOCK_MONOTONIC),
|
||||
// we use the same API here.
|
||||
// We need that to make sure we use the same time system on both JS and Java sides.
|
||||
// Links to the source code:
|
||||
// https://android.googlesource.com/platform/frameworks/native/+/jb-mr1-release/libs/utils/SystemClock.cpp
|
||||
// https://android.googlesource.com/platform/system/core/+/master/libutils/Timers.cpp
|
||||
struct timespec now;
|
||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
int64_t nano = now.tv_sec * NANOSECONDS_IN_SECOND + now.tv_nsec;
|
||||
return Value::makeNumber(ctx, (nano / (double)NANOSECONDS_IN_MILLISECOND));
|
||||
}
|
||||
|
||||
JSValueRef nativeLoggingHook(
|
||||
JSContextRef ctx,
|
||||
JSObjectRef function,
|
||||
JSObjectRef thisObject,
|
||||
size_t argumentCount,
|
||||
const JSValueRef arguments[],
|
||||
JSValueRef* exception) {
|
||||
android_LogPriority logLevel = ANDROID_LOG_DEBUG;
|
||||
if (argumentCount > 1) {
|
||||
int level = (int)Value(ctx, arguments[1]).asNumber();
|
||||
// The lowest log level we get from JS is 0. We shift and cap it to be
|
||||
// in the range the Android logging method expects.
|
||||
logLevel = std::min(
|
||||
static_cast<android_LogPriority>(level + ANDROID_LOG_DEBUG),
|
||||
ANDROID_LOG_FATAL);
|
||||
}
|
||||
if (argumentCount > 0) {
|
||||
String message = Value(ctx, arguments[0]).toString();
|
||||
reactAndroidLoggingHook(message.str(), logLevel);
|
||||
}
|
||||
return Value::makeUndefined(ctx);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
|
||||
void injectJSCExecutorAndroidPlatform() {
|
||||
// Inject some behavior into react/
|
||||
JReactMarker::setLogPerfMarkerIfNeeded();
|
||||
ExceptionHandling::platformErrorExtractor = extractJniError;
|
||||
JSCNativeHooks::loggingHook = nativeLoggingHook;
|
||||
JSCNativeHooks::nowHook = nativePerformanceNow;
|
||||
JSCNativeHooks::installPerfHooks = addNativePerfLoggingHooks;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
std::unique_ptr<JSExecutorFactory> makeAndroidJSCExecutorFactory(
|
||||
const folly::dynamic& jscConfig) {
|
||||
detail::injectJSCExecutorAndroidPlatform();
|
||||
return folly::make_unique<JSCExecutorFactory>(std::move(jscConfig));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
||||
|
||||
// This source code is licensed under the MIT license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <cxxreact/JSExecutor.h>
|
||||
|
||||
namespace folly {
|
||||
|
||||
class dynamic;
|
||||
|
||||
}
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
class JSExecutorFactory;
|
||||
|
||||
namespace detail {
|
||||
|
||||
// This is only exposed so instrumentation tests can call it.
|
||||
void injectJSCExecutorAndroidPlatform();
|
||||
|
||||
}
|
||||
|
||||
std::unique_ptr<JSExecutorFactory> makeAndroidJSCExecutorFactory(
|
||||
const folly::dynamic& jscConfig);
|
||||
|
||||
}
|
||||
}
|
@ -1,308 +0,0 @@
|
||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
||||
|
||||
// This source code is licensed under the MIT license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
|
||||
#include "JSCPerfLogging.h"
|
||||
|
||||
#include <jschelpers/JSCHelpers.h>
|
||||
|
||||
#include <fb/log.h>
|
||||
#include <fb/fbjni.h>
|
||||
|
||||
using namespace facebook::jni;
|
||||
|
||||
namespace facebook { namespace react {
|
||||
|
||||
struct JQuickPerformanceLogger : JavaClass<JQuickPerformanceLogger> {
|
||||
static auto constexpr kJavaDescriptor = "Lcom/facebook/quicklog/QuickPerformanceLogger;";
|
||||
|
||||
void markerStart(int markerId, int instanceKey, long timestamp) {
|
||||
static auto markerStartMethod =
|
||||
javaClassStatic()->getMethod<void(jint, jint, jlong)>("markerStart");
|
||||
markerStartMethod(self(), markerId, instanceKey, timestamp);
|
||||
}
|
||||
|
||||
void markerEnd(int markerId, int instanceKey, short actionId, long timestamp) {
|
||||
static auto markerEndMethod =
|
||||
javaClassStatic()->getMethod<void(jint, jint, jshort, jlong)>("markerEnd");
|
||||
markerEndMethod(self(), markerId, instanceKey, actionId, timestamp);
|
||||
}
|
||||
|
||||
void markerTag(int markerId, int instanceKey, alias_ref<jstring> tag) {
|
||||
static auto markerTagMethod =
|
||||
javaClassStatic()->getMethod<void(jint, jint, alias_ref<jstring>)>("markerTag");
|
||||
markerTagMethod(self(), markerId, instanceKey, tag);
|
||||
}
|
||||
|
||||
void markerAnnotate(
|
||||
int markerId,
|
||||
int instanceKey,
|
||||
alias_ref<jstring> key,
|
||||
alias_ref<jstring> value) {
|
||||
static auto markerAnnotateMethod = javaClassStatic()->
|
||||
getMethod<void(jint, jint, alias_ref<jstring>, alias_ref<jstring>)>("markerAnnotate");
|
||||
markerAnnotateMethod(self(), markerId, instanceKey, key, value);
|
||||
}
|
||||
|
||||
void markerNote(int markerId, int instanceKey, short actionId, long timestamp) {
|
||||
static auto markerNoteMethod =
|
||||
javaClassStatic()->getMethod<void(jint, jint, jshort, jlong)>("markerNote");
|
||||
markerNoteMethod(self(), markerId, instanceKey, actionId, timestamp);
|
||||
}
|
||||
|
||||
void markerCancel(int markerId, int instanceKey) {
|
||||
static auto markerCancelMethod =
|
||||
javaClassStatic()->getMethod<void(jint, jint)>("markerCancel");
|
||||
markerCancelMethod(self(), markerId, instanceKey);
|
||||
}
|
||||
|
||||
int64_t currentMonotonicTimestamp() {
|
||||
static auto currentTimestampMethod =
|
||||
javaClassStatic()->getMethod<jlong()>("currentMonotonicTimestamp");
|
||||
return currentTimestampMethod(self());
|
||||
}
|
||||
|
||||
void markerPoint(int markerId, alias_ref<jstring> name, int instanceKey) {
|
||||
static auto markerPointMethod =
|
||||
javaClassStatic()->getMethod<void(jint, jint, alias_ref<jstring>)>("markerPoint");
|
||||
markerPointMethod(self(), markerId, instanceKey, name);
|
||||
}
|
||||
};
|
||||
|
||||
struct JQuickPerformanceLoggerProvider : JavaClass<JQuickPerformanceLoggerProvider> {
|
||||
static auto constexpr kJavaDescriptor = "Lcom/facebook/quicklog/QuickPerformanceLoggerProvider;";
|
||||
|
||||
static alias_ref<JQuickPerformanceLogger::javaobject> get() {
|
||||
static auto getQPLInstMethod =
|
||||
javaClassStatic()->getStaticMethod<JQuickPerformanceLogger::javaobject()>("getQPLInstance");
|
||||
static auto logger = make_global(getQPLInstMethod(javaClassStatic()));
|
||||
return logger;
|
||||
}
|
||||
};
|
||||
|
||||
static bool isReady() {
|
||||
static bool ready = false;
|
||||
if (!ready) {
|
||||
try {
|
||||
// TODO: findClassStatic only does the lookup once. If we can't find
|
||||
// QuickPerformanceLoggerProvider the first time we call this, we will always fail here.
|
||||
findClassStatic("com/facebook/quicklog/QuickPerformanceLoggerProvider");
|
||||
} catch(...) {
|
||||
// Swallow this exception - we don't want to crash the app, an error is enough.
|
||||
FBLOGE("Calling QPL from JS before class has been loaded in Java. Ignored.");
|
||||
return false;
|
||||
}
|
||||
if (JQuickPerformanceLoggerProvider::get()) {
|
||||
ready = true;
|
||||
} else {
|
||||
FBLOGE("Calling QPL from JS before it has been initialized in Java. Ignored.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return ready;
|
||||
}
|
||||
|
||||
// After having read the implementation of PNaN that is returned from JSValueToNumber, and some
|
||||
// more material on how NaNs are constructed, I think this is the most consistent way to verify
|
||||
// NaN with how we generate it.
|
||||
// Once the integration completes, I'll play around with it some more and potentially change this
|
||||
// implementation to use std::isnan() if it is exactly commensurate with our usage.
|
||||
static bool isNan(double value) {
|
||||
return (value != value);
|
||||
}
|
||||
|
||||
static double grabDouble(
|
||||
JSContextRef ctx,
|
||||
const JSValueRef arguments[],
|
||||
size_t argumentIndex,
|
||||
JSValueRef* exception) {
|
||||
return JSValueToNumber(ctx, arguments[argumentIndex], exception);
|
||||
}
|
||||
|
||||
// Safely translates JSValues to an array of doubles.
|
||||
static bool grabDoubles(
|
||||
size_t targetsCount,
|
||||
double targets[],
|
||||
JSContextRef ctx,
|
||||
size_t argumentCount,
|
||||
const JSValueRef arguments[],
|
||||
JSValueRef* exception) {
|
||||
if (argumentCount < targetsCount) {
|
||||
return false;
|
||||
}
|
||||
for (size_t i = 0 ; i < targetsCount ; i++) {
|
||||
targets[i] = grabDouble(ctx, arguments, i, exception);
|
||||
if (isNan(targets[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static local_ref<jstring> getJStringFromJSValueRef(JSContextRef ctx, JSValueRef ref) {
|
||||
JSStringRef jsStringRef = JSValueToStringCopy(ctx, ref, nullptr);
|
||||
const JSChar* chars = JSStringGetCharactersPtr(jsStringRef);
|
||||
const size_t length = JSStringGetLength(jsStringRef);
|
||||
local_ref<jstring> returnStr = adopt_local(Environment::current()->NewString(chars, length));
|
||||
JSStringRelease(jsStringRef);
|
||||
return returnStr;
|
||||
}
|
||||
|
||||
static JSValueRef nativeQPLMarkerStart(
|
||||
JSContextRef ctx,
|
||||
JSObjectRef function,
|
||||
JSObjectRef thisObject,
|
||||
size_t argumentCount,
|
||||
const JSValueRef arguments[],
|
||||
JSValueRef* exception) {
|
||||
double targets[3];
|
||||
if (isReady() && grabDoubles(3, targets, ctx, argumentCount, arguments, exception)) {
|
||||
int32_t markerId = (int32_t) targets[0];
|
||||
int32_t instanceKey = (int32_t) targets[1];
|
||||
int64_t timestamp = (int64_t) targets[2];
|
||||
JQuickPerformanceLoggerProvider::get()->markerStart(markerId, instanceKey, timestamp);
|
||||
}
|
||||
return JSValueMakeUndefined(ctx);
|
||||
}
|
||||
|
||||
static JSValueRef nativeQPLMarkerEnd(
|
||||
JSContextRef ctx,
|
||||
JSObjectRef function,
|
||||
JSObjectRef thisObject,
|
||||
size_t argumentCount,
|
||||
const JSValueRef arguments[],
|
||||
JSValueRef* exception) {
|
||||
double targets[4];
|
||||
if (isReady() && grabDoubles(4, targets, ctx, argumentCount, arguments, exception)) {
|
||||
int32_t markerId = (int32_t) targets[0];
|
||||
int32_t instanceKey = (int32_t) targets[1];
|
||||
// NOTE: avoid undefined behavior when the value does not find in int16_t.
|
||||
int16_t actionId = (int16_t) (int32_t) targets[2];
|
||||
int64_t timestamp = (int64_t) targets[3];
|
||||
JQuickPerformanceLoggerProvider::get()->markerEnd(markerId, instanceKey, actionId, timestamp);
|
||||
}
|
||||
return JSValueMakeUndefined(ctx);
|
||||
}
|
||||
|
||||
static JSValueRef nativeQPLMarkerTag(
|
||||
JSContextRef ctx,
|
||||
JSObjectRef function,
|
||||
JSObjectRef thisObject,
|
||||
size_t argumentCount,
|
||||
const JSValueRef arguments[],
|
||||
JSValueRef* exception) {
|
||||
double targets[2];
|
||||
if (isReady() && grabDoubles(2, targets, ctx, argumentCount, arguments, exception)) {
|
||||
int32_t markerId = (int32_t) targets[0];
|
||||
int32_t instanceKey = (int32_t) targets[1];
|
||||
local_ref<jstring> tag = getJStringFromJSValueRef(ctx, arguments[2]);
|
||||
JQuickPerformanceLoggerProvider::get()->markerTag(markerId, instanceKey, tag);
|
||||
}
|
||||
return JSValueMakeUndefined(ctx);
|
||||
}
|
||||
|
||||
static JSValueRef nativeQPLMarkerAnnotate(
|
||||
JSContextRef ctx,
|
||||
JSObjectRef function,
|
||||
JSObjectRef thisObject,
|
||||
size_t argumentCount,
|
||||
const JSValueRef arguments[],
|
||||
JSValueRef* exception) {
|
||||
double targets[2];
|
||||
if (isReady() && grabDoubles(2, targets, ctx, argumentCount, arguments, exception)) {
|
||||
int32_t markerId = (int32_t) targets[0];
|
||||
int32_t instanceKey = (int32_t) targets[1];
|
||||
local_ref<jstring> key = getJStringFromJSValueRef(ctx, arguments[2]);
|
||||
local_ref<jstring> value = getJStringFromJSValueRef(ctx, arguments[3]);
|
||||
JQuickPerformanceLoggerProvider::get()->markerAnnotate(markerId, instanceKey, key, value);
|
||||
}
|
||||
return JSValueMakeUndefined(ctx);
|
||||
}
|
||||
|
||||
static JSValueRef nativeQPLMarkerNote(
|
||||
JSContextRef ctx,
|
||||
JSObjectRef function,
|
||||
JSObjectRef thisObject,
|
||||
size_t argumentCount,
|
||||
const JSValueRef arguments[],
|
||||
JSValueRef* exception) {
|
||||
double targets[4];
|
||||
if (isReady() && grabDoubles(4, targets, ctx, argumentCount, arguments, exception)) {
|
||||
int32_t markerId = (int32_t) targets[0];
|
||||
int32_t instanceKey = (int32_t) targets[1];
|
||||
// NOTE: avoid undefined behavior when the value does not find in int16_t.
|
||||
int16_t actionId = (int16_t) (int32_t) targets[2];
|
||||
int64_t timestamp = (int64_t) targets[3];
|
||||
JQuickPerformanceLoggerProvider::get()->markerNote(markerId, instanceKey, actionId, timestamp);
|
||||
}
|
||||
return JSValueMakeUndefined(ctx);
|
||||
}
|
||||
|
||||
static JSValueRef nativeQPLMarkerCancel(
|
||||
JSContextRef ctx,
|
||||
JSObjectRef function,
|
||||
JSObjectRef thisObject,
|
||||
size_t argumentCount,
|
||||
const JSValueRef arguments[],
|
||||
JSValueRef* exception) {
|
||||
double targets[2];
|
||||
if (isReady() && grabDoubles(2, targets, ctx, argumentCount, arguments, exception)) {
|
||||
int32_t markerId = (int32_t) targets[0];
|
||||
int32_t instanceKey = (int32_t) targets[1];
|
||||
JQuickPerformanceLoggerProvider::get()->markerCancel(markerId, instanceKey);
|
||||
}
|
||||
return JSValueMakeUndefined(ctx);
|
||||
}
|
||||
|
||||
static JSValueRef nativeQPLTimestamp(
|
||||
JSContextRef ctx,
|
||||
JSObjectRef function,
|
||||
JSObjectRef thisObject,
|
||||
size_t argumentCount,
|
||||
const JSValueRef arguments[],
|
||||
JSValueRef* exception) {
|
||||
if (!isReady()) {
|
||||
return JSValueMakeNumber(ctx, 0);
|
||||
}
|
||||
int64_t timestamp = JQuickPerformanceLoggerProvider::get()->currentMonotonicTimestamp();
|
||||
// Since this is monotonic time, I assume the 52 bits of mantissa are enough in the double value.
|
||||
return JSValueMakeNumber(ctx, timestamp);
|
||||
}
|
||||
|
||||
static JSValueRef nativeQPLMarkerPoint(
|
||||
JSContextRef ctx,
|
||||
JSObjectRef function,
|
||||
JSObjectRef thisObject,
|
||||
size_t argumentCount,
|
||||
const JSValueRef arguments[],
|
||||
JSValueRef* exception) {
|
||||
if (isReady() && argumentCount == 4) {
|
||||
double markerIdArgument = grabDouble(ctx, arguments, 0, exception);
|
||||
double instanceKeyArgument = grabDouble(ctx, arguments, 2, exception);
|
||||
if (isNan(markerIdArgument) || isNan(instanceKeyArgument)) {
|
||||
return JSValueMakeUndefined(ctx);
|
||||
}
|
||||
|
||||
int32_t markerId = (int32_t) markerIdArgument;
|
||||
local_ref<jstring> name = getJStringFromJSValueRef(ctx, arguments[1]);
|
||||
int32_t instanceKey = (int32_t) instanceKeyArgument;
|
||||
// timestamp is not used as QuickPerformanceLogger::markerPoint with all
|
||||
// params is missing
|
||||
JQuickPerformanceLoggerProvider::get()->markerPoint(markerId, name, instanceKey);
|
||||
}
|
||||
return JSValueMakeUndefined(ctx);
|
||||
}
|
||||
|
||||
void addNativePerfLoggingHooks(JSGlobalContextRef ctx) {
|
||||
installGlobalFunction(ctx, "nativeQPLMarkerStart", nativeQPLMarkerStart);
|
||||
installGlobalFunction(ctx, "nativeQPLMarkerEnd", nativeQPLMarkerEnd);
|
||||
installGlobalFunction(ctx, "nativeQPLMarkerTag", nativeQPLMarkerTag);
|
||||
installGlobalFunction(ctx, "nativeQPLMarkerAnnotate", nativeQPLMarkerAnnotate);
|
||||
installGlobalFunction(ctx, "nativeQPLMarkerNote", nativeQPLMarkerNote);
|
||||
installGlobalFunction(ctx, "nativeQPLMarkerCancel", nativeQPLMarkerCancel);
|
||||
installGlobalFunction(ctx, "nativeQPLTimestamp", nativeQPLTimestamp);
|
||||
installGlobalFunction(ctx, "nativeQPLMarkerPoint", nativeQPLMarkerPoint);
|
||||
}
|
||||
|
||||
} }
|
@ -1,15 +0,0 @@
|
||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
||||
|
||||
// This source code is licensed under the MIT license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <JavaScriptCore/JSContextRef.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
void addNativePerfLoggingHooks(JSGlobalContextRef ctx);
|
||||
|
||||
} }
|
@ -1,786 +0,0 @@
|
||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
||||
|
||||
// This source code is licensed under the MIT license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
|
||||
#include "JSCExecutor.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/time.h>
|
||||
#include <algorithm>
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <system_error>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <folly/Conv.h>
|
||||
#include <folly/Exception.h>
|
||||
#include <folly/Memory.h>
|
||||
#include <folly/String.h>
|
||||
#include <folly/json.h>
|
||||
#include <glog/logging.h>
|
||||
#include <jschelpers/JSCHelpers.h>
|
||||
#include <jschelpers/Value.h>
|
||||
#include <jsinspector/InspectorInterfaces.h>
|
||||
|
||||
#include "JSBigString.h"
|
||||
#include "JSBundleType.h"
|
||||
#include "JSCLegacyTracing.h"
|
||||
#include "JSCMemory.h"
|
||||
#include "JSCPerfStats.h"
|
||||
#include "JSCSamplingProfiler.h"
|
||||
#include "JSCTracing.h"
|
||||
#include "JSCUtils.h"
|
||||
#include "JSModulesUnbundle.h"
|
||||
#include "MessageQueueThread.h"
|
||||
#include "ModuleRegistry.h"
|
||||
#include "Platform.h"
|
||||
#include "RAMBundleRegistry.h"
|
||||
#include "RecoverableError.h"
|
||||
#include "SystraceSection.h"
|
||||
|
||||
#if defined(WITH_FB_JSC_TUNING) && defined(__ANDROID__)
|
||||
#include <jsc_config_android.h>
|
||||
#endif
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
namespace {
|
||||
|
||||
template <JSValueRef (JSCExecutor::*method)(size_t, const JSValueRef[])>
|
||||
inline JSObjectCallAsFunctionCallback exceptionWrapMethod() {
|
||||
struct funcWrapper {
|
||||
static JSValueRef call(
|
||||
JSContextRef ctx,
|
||||
JSObjectRef function,
|
||||
JSObjectRef thisObject,
|
||||
size_t argumentCount,
|
||||
const JSValueRef arguments[],
|
||||
JSValueRef* exception) {
|
||||
try {
|
||||
auto executor = Object::getGlobalObject(ctx).getPrivate<JSCExecutor>();
|
||||
if (executor &&
|
||||
executor->getJavaScriptContext()) { // Executor not invalidated
|
||||
return (executor->*method)(argumentCount, arguments);
|
||||
}
|
||||
} catch (...) {
|
||||
*exception = translatePendingCppExceptionToJSError(ctx, function);
|
||||
}
|
||||
return Value::makeUndefined(ctx);
|
||||
}
|
||||
};
|
||||
|
||||
return &funcWrapper::call;
|
||||
}
|
||||
|
||||
template <JSValueRef (
|
||||
JSCExecutor::*method)(JSObjectRef object, JSStringRef propertyName)>
|
||||
inline JSObjectGetPropertyCallback exceptionWrapMethod() {
|
||||
struct funcWrapper {
|
||||
static JSValueRef call(
|
||||
JSContextRef ctx,
|
||||
JSObjectRef object,
|
||||
JSStringRef propertyName,
|
||||
JSValueRef* exception) {
|
||||
try {
|
||||
auto executor = Object::getGlobalObject(ctx).getPrivate<JSCExecutor>();
|
||||
if (executor &&
|
||||
executor->getJavaScriptContext()) { // Executor not invalidated
|
||||
return (executor->*method)(object, propertyName);
|
||||
}
|
||||
} catch (...) {
|
||||
*exception = translatePendingCppExceptionToJSError(ctx, object);
|
||||
}
|
||||
return Value::makeUndefined(ctx);
|
||||
}
|
||||
};
|
||||
|
||||
return &funcWrapper::call;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
#if DEBUG
|
||||
static JSValueRef nativeInjectHMRUpdate(
|
||||
JSContextRef ctx,
|
||||
JSObjectRef function,
|
||||
JSObjectRef thisObject,
|
||||
size_t argumentCount,
|
||||
const JSValueRef arguments[],
|
||||
JSValueRef* exception) {
|
||||
String execJSString = Value(ctx, arguments[0]).toString();
|
||||
String jsURL = Value(ctx, arguments[1]).toString();
|
||||
evaluateScript(ctx, execJSString, jsURL);
|
||||
return Value::makeUndefined(ctx);
|
||||
}
|
||||
#endif
|
||||
|
||||
std::unique_ptr<JSExecutor> JSCExecutorFactory::createJSExecutor(
|
||||
std::shared_ptr<ExecutorDelegate> delegate,
|
||||
std::shared_ptr<MessageQueueThread> jsQueue) {
|
||||
return folly::make_unique<JSCExecutor>(
|
||||
delegate, jsQueue, m_jscConfig);
|
||||
}
|
||||
|
||||
JSCExecutor::JSCExecutor(
|
||||
std::shared_ptr<ExecutorDelegate> delegate,
|
||||
std::shared_ptr<MessageQueueThread> messageQueueThread,
|
||||
const folly::dynamic& jscConfig) throw(JSException)
|
||||
: m_delegate(delegate),
|
||||
m_messageQueueThread(messageQueueThread),
|
||||
m_nativeModules(delegate ? delegate->getModuleRegistry() : nullptr),
|
||||
m_jscConfig(jscConfig) {
|
||||
initOnJSVMThread();
|
||||
|
||||
{
|
||||
SystraceSection s("nativeModuleProxy object");
|
||||
installGlobalProxy(
|
||||
m_context,
|
||||
"nativeModuleProxy",
|
||||
exceptionWrapMethod<&JSCExecutor::getNativeModule>());
|
||||
}
|
||||
}
|
||||
|
||||
JSCExecutor::~JSCExecutor() {
|
||||
CHECK(*m_isDestroyed)
|
||||
<< "JSCExecutor::destroy() must be called before its destructor!";
|
||||
}
|
||||
|
||||
void JSCExecutor::destroy() {
|
||||
*m_isDestroyed = true;
|
||||
if (m_messageQueueThread.get()) {
|
||||
m_messageQueueThread->runOnQueueSync([this]() { terminateOnJSVMThread(); });
|
||||
} else {
|
||||
terminateOnJSVMThread();
|
||||
}
|
||||
}
|
||||
|
||||
void JSCExecutor::setContextName(const std::string& name) {
|
||||
String jsName = String(m_context, name.c_str());
|
||||
JSC_JSGlobalContextSetName(m_context, jsName);
|
||||
}
|
||||
|
||||
static bool canUseInspector(JSContextRef context) {
|
||||
#ifdef WITH_INSPECTOR
|
||||
#if defined(__APPLE__)
|
||||
return isCustomJSCPtr(context); // WITH_INSPECTOR && Apple
|
||||
#else
|
||||
return true; // WITH_INSPECTOR && Android
|
||||
#endif
|
||||
#else
|
||||
return false; // !WITH_INSPECTOR
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool canUseSamplingProfiler(JSContextRef context) {
|
||||
#if defined(__APPLE__) || defined(WITH_JSC_EXTRA_TRACING)
|
||||
return JSC_JSSamplingProfilerEnabled(context);
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void JSCExecutor::initOnJSVMThread() throw(JSException) {
|
||||
SystraceSection s("JSCExecutor::initOnJSVMThread");
|
||||
|
||||
#if defined(__APPLE__)
|
||||
const bool useCustomJSC =
|
||||
m_jscConfig.getDefault("UseCustomJSC", false).getBool();
|
||||
if (useCustomJSC) {
|
||||
JSC_configureJSCForIOS(true, toJson(m_jscConfig));
|
||||
}
|
||||
#else
|
||||
const bool useCustomJSC = false;
|
||||
#endif
|
||||
|
||||
#if defined(WITH_FB_JSC_TUNING) && defined(__ANDROID__)
|
||||
configureJSCForAndroid(m_jscConfig);
|
||||
#endif
|
||||
|
||||
// Create a custom global class, so we can store data in it later using
|
||||
// JSObjectSetPrivate
|
||||
JSClassRef globalClass = nullptr;
|
||||
{
|
||||
SystraceSection s_("JSClassCreate");
|
||||
JSClassDefinition definition = kJSClassDefinitionEmpty;
|
||||
definition.attributes |= kJSClassAttributeNoAutomaticPrototype;
|
||||
globalClass = JSC_JSClassCreate(useCustomJSC, &definition);
|
||||
}
|
||||
{
|
||||
SystraceSection s_("JSGlobalContextCreateInGroup");
|
||||
m_context =
|
||||
JSC_JSGlobalContextCreateInGroup(useCustomJSC, nullptr, globalClass);
|
||||
}
|
||||
JSC_JSClassRelease(useCustomJSC, globalClass);
|
||||
|
||||
// Add a pointer to ourselves so we can retrieve it later in our hooks
|
||||
Object::getGlobalObject(m_context).setPrivate(this);
|
||||
|
||||
if (canUseInspector(m_context)) {
|
||||
const std::string ownerId =
|
||||
m_jscConfig.getDefault("OwnerIdentity", "unknown").getString();
|
||||
const std::string appId =
|
||||
m_jscConfig.getDefault("AppIdentity", "unknown").getString();
|
||||
const std::string deviceId =
|
||||
m_jscConfig.getDefault("DeviceIdentity", "unknown").getString();
|
||||
auto checkIsInspectedRemote = [ownerId, appId, deviceId]() {
|
||||
return isNetworkInspected(ownerId, appId, deviceId);
|
||||
};
|
||||
|
||||
auto& globalInspector = facebook::react::getInspectorInstance();
|
||||
JSC_JSGlobalContextEnableDebugger(
|
||||
m_context, globalInspector, ownerId.c_str(), checkIsInspectedRemote);
|
||||
}
|
||||
|
||||
installNativeHook<&JSCExecutor::nativeFlushQueueImmediate>(
|
||||
"nativeFlushQueueImmediate");
|
||||
installNativeHook<&JSCExecutor::nativeCallSyncHook>("nativeCallSyncHook");
|
||||
|
||||
installGlobalFunction(
|
||||
m_context, "nativeLoggingHook", JSCNativeHooks::loggingHook);
|
||||
installGlobalFunction(
|
||||
m_context, "nativePerformanceNow", JSCNativeHooks::nowHook);
|
||||
|
||||
#if DEBUG
|
||||
installGlobalFunction(
|
||||
m_context, "nativeInjectHMRUpdate", nativeInjectHMRUpdate);
|
||||
#endif
|
||||
|
||||
addNativeTracingHooks(m_context);
|
||||
addNativeTracingLegacyHooks(m_context);
|
||||
addJSCMemoryHooks(m_context);
|
||||
addJSCPerfStatsHooks(m_context);
|
||||
|
||||
JSCNativeHooks::installPerfHooks(m_context);
|
||||
|
||||
if (canUseSamplingProfiler(m_context)) {
|
||||
initSamplingProfilerOnMainJSCThread(m_context);
|
||||
}
|
||||
}
|
||||
|
||||
bool JSCExecutor::isNetworkInspected(
|
||||
const std::string& owner,
|
||||
const std::string& app,
|
||||
const std::string& device) {
|
||||
#ifdef WITH_FB_DBG_ATTACH_BEFORE_EXEC
|
||||
auto connect_socket = [](int socket_desc, std::string address, int port) {
|
||||
if (socket_desc < 0) {
|
||||
::close(socket_desc);
|
||||
return false;
|
||||
}
|
||||
|
||||
struct timeval tv;
|
||||
tv.tv_sec = 1;
|
||||
tv.tv_usec = 0;
|
||||
auto sock_opt_rcv_resp = setsockopt(
|
||||
socket_desc,
|
||||
SOL_SOCKET,
|
||||
SO_RCVTIMEO,
|
||||
(const char*)&tv,
|
||||
sizeof(struct timeval));
|
||||
if (sock_opt_rcv_resp < 0) {
|
||||
::close(socket_desc);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto sock_opt_snd_resp = setsockopt(
|
||||
socket_desc,
|
||||
SOL_SOCKET,
|
||||
SO_SNDTIMEO,
|
||||
(const char*)&tv,
|
||||
sizeof(struct timeval));
|
||||
if (sock_opt_snd_resp < 0) {
|
||||
::close(socket_desc);
|
||||
return false;
|
||||
}
|
||||
|
||||
struct sockaddr_in server;
|
||||
server.sin_addr.s_addr = inet_addr(address.c_str());
|
||||
server.sin_family = AF_INET;
|
||||
server.sin_port = htons(port);
|
||||
auto connect_resp =
|
||||
::connect(socket_desc, (struct sockaddr*)&server, sizeof(server));
|
||||
if (connect_resp < 0) {
|
||||
::close(socket_desc);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
int socket_desc = socket(AF_INET, SOCK_STREAM, 0);
|
||||
|
||||
if (!connect_socket(socket_desc, "127.0.0.1", 8082)) {
|
||||
#if defined(__ANDROID__)
|
||||
socket_desc = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (!connect_socket(socket_desc, "10.0.2.2", 8082) /* emulator */) {
|
||||
socket_desc = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (!connect_socket(socket_desc, "10.0.3.2", 8082) /* genymotion */) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#else //! defined(__ANDROID__)
|
||||
return false;
|
||||
#endif // defined(__ANDROID__)
|
||||
}
|
||||
|
||||
std::string escapedOwner =
|
||||
folly::uriEscape<std::string>(owner, folly::UriEscapeMode::QUERY);
|
||||
std::string escapedApp =
|
||||
folly::uriEscape<std::string>(app, folly::UriEscapeMode::QUERY);
|
||||
std::string escapedDevice =
|
||||
folly::uriEscape<std::string>(device, folly::UriEscapeMode::QUERY);
|
||||
std::string msg = folly::to<std::string>(
|
||||
"GET /autoattach?title=",
|
||||
escapedOwner,
|
||||
"&app=",
|
||||
escapedApp,
|
||||
"&device=",
|
||||
escapedDevice,
|
||||
" HTTP/1.1\r\n\r\n");
|
||||
auto send_resp = ::send(socket_desc, msg.c_str(), msg.length(), 0);
|
||||
if (send_resp < 0) {
|
||||
::close(socket_desc);
|
||||
return false;
|
||||
}
|
||||
|
||||
char server_reply[200];
|
||||
server_reply[199] = '\0';
|
||||
auto recv_resp =
|
||||
::recv(socket_desc, server_reply, sizeof(server_reply) - 1, 0);
|
||||
if (recv_resp < 0) {
|
||||
::close(socket_desc);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string response(server_reply);
|
||||
if (response.size() < 25) {
|
||||
::close(socket_desc);
|
||||
return false;
|
||||
}
|
||||
auto responseCandidate = response.substr(response.size() - 25);
|
||||
auto found =
|
||||
responseCandidate.find("{\"autoattach\":true}") != std::string::npos;
|
||||
::close(socket_desc);
|
||||
return found;
|
||||
#else //! WITH_FB_DBG_ATTACH_BEFORE_EXEC
|
||||
return false;
|
||||
#endif // WITH_FB_DBG_ATTACH_BEFORE_EXEC
|
||||
}
|
||||
|
||||
void JSCExecutor::terminateOnJSVMThread() {
|
||||
JSGlobalContextRef context = m_context;
|
||||
m_context = nullptr;
|
||||
Object::getGlobalObject(context).setPrivate(nullptr);
|
||||
m_nativeModules.reset();
|
||||
|
||||
if (canUseInspector(context)) {
|
||||
auto& globalInspector = facebook::react::getInspectorInstance();
|
||||
JSC_JSGlobalContextDisableDebugger(context, globalInspector);
|
||||
}
|
||||
|
||||
JSC_JSGlobalContextRelease(context);
|
||||
}
|
||||
|
||||
#ifdef WITH_FBJSCEXTENSIONS
|
||||
static const char* explainLoadSourceStatus(JSLoadSourceStatus status) {
|
||||
switch (status) {
|
||||
case JSLoadSourceIsCompiled:
|
||||
return "No error encountered during source load";
|
||||
|
||||
case JSLoadSourceErrorOnRead:
|
||||
return "Error reading source";
|
||||
|
||||
case JSLoadSourceIsNotCompiled:
|
||||
return "Source is not compiled";
|
||||
|
||||
case JSLoadSourceErrorVersionMismatch:
|
||||
return "Source version not supported";
|
||||
|
||||
default:
|
||||
return "Bad error code";
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// basename_r isn't in all iOS SDKs, so use this simple version instead.
|
||||
static std::string simpleBasename(const std::string& path) {
|
||||
size_t pos = path.rfind("/");
|
||||
return (pos != std::string::npos) ? path.substr(pos) : path;
|
||||
}
|
||||
|
||||
void JSCExecutor::loadApplicationScript(
|
||||
std::unique_ptr<const JSBigString> script,
|
||||
std::string sourceURL) {
|
||||
SystraceSection s(
|
||||
"JSCExecutor::loadApplicationScript", "sourceURL", sourceURL);
|
||||
|
||||
std::string scriptName = simpleBasename(sourceURL);
|
||||
ReactMarker::logTaggedMarker(
|
||||
ReactMarker::RUN_JS_BUNDLE_START, scriptName.c_str());
|
||||
String jsSourceURL(m_context, sourceURL.c_str());
|
||||
|
||||
// TODO t15069155: reduce the number of overrides here
|
||||
#ifdef WITH_FBJSCEXTENSIONS
|
||||
if (auto fileStr = dynamic_cast<const JSBigFileString*>(script.get())) {
|
||||
JSContextLock lock(m_context);
|
||||
JSLoadSourceStatus jsStatus;
|
||||
auto bcSourceCode = JSCreateSourceCodeFromFile(
|
||||
fileStr->fd(), jsSourceURL, nullptr, &jsStatus);
|
||||
|
||||
switch (jsStatus) {
|
||||
case JSLoadSourceIsCompiled:
|
||||
if (!bcSourceCode) {
|
||||
throw std::runtime_error("Unexpected error opening compiled bundle");
|
||||
}
|
||||
evaluateSourceCode(m_context, bcSourceCode, jsSourceURL);
|
||||
|
||||
flush();
|
||||
|
||||
ReactMarker::logMarker(ReactMarker::CREATE_REACT_CONTEXT_STOP);
|
||||
ReactMarker::logTaggedMarker(
|
||||
ReactMarker::RUN_JS_BUNDLE_STOP, scriptName.c_str());
|
||||
return;
|
||||
|
||||
case JSLoadSourceErrorVersionMismatch:
|
||||
throw RecoverableError(explainLoadSourceStatus(jsStatus));
|
||||
|
||||
case JSLoadSourceErrorOnRead:
|
||||
case JSLoadSourceIsNotCompiled:
|
||||
// Not bytecode, fall through.
|
||||
break;
|
||||
}
|
||||
}
|
||||
#elif defined(__APPLE__)
|
||||
BundleHeader header;
|
||||
memcpy(
|
||||
&header, script->c_str(), std::min(script->size(), sizeof(BundleHeader)));
|
||||
auto scriptTag = parseTypeFromHeader(header);
|
||||
|
||||
if (scriptTag == ScriptTag::BCBundle) {
|
||||
using file_ptr = std::unique_ptr<FILE, decltype(&fclose)>;
|
||||
file_ptr source(fopen(sourceURL.c_str(), "r"), fclose);
|
||||
int sourceFD = fileno(source.get());
|
||||
|
||||
JSValueRef jsError;
|
||||
JSValueRef result = JSC_JSEvaluateBytecodeBundle(
|
||||
m_context, NULL, sourceFD, jsSourceURL, &jsError);
|
||||
if (result == nullptr) {
|
||||
throw JSException(m_context, jsError, jsSourceURL);
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
String jsScript;
|
||||
JSContextLock lock(m_context);
|
||||
{
|
||||
SystraceSection s_(
|
||||
"JSCExecutor::loadApplicationScript-createExpectingAscii");
|
||||
ReactMarker::logMarker(ReactMarker::JS_BUNDLE_STRING_CONVERT_START);
|
||||
jsScript = adoptString(std::move(script));
|
||||
ReactMarker::logMarker(ReactMarker::JS_BUNDLE_STRING_CONVERT_STOP);
|
||||
}
|
||||
|
||||
SystraceSection s_("JSCExecutor::loadApplicationScript-evaluateScript");
|
||||
evaluateScript(m_context, jsScript, jsSourceURL);
|
||||
}
|
||||
|
||||
flush();
|
||||
|
||||
ReactMarker::logMarker(ReactMarker::CREATE_REACT_CONTEXT_STOP);
|
||||
ReactMarker::logTaggedMarker(
|
||||
ReactMarker::RUN_JS_BUNDLE_STOP, scriptName.c_str());
|
||||
}
|
||||
|
||||
void JSCExecutor::setBundleRegistry(
|
||||
std::unique_ptr<RAMBundleRegistry> bundleRegistry) {
|
||||
if (!m_bundleRegistry) {
|
||||
installNativeHook<&JSCExecutor::nativeRequire>("nativeRequire");
|
||||
}
|
||||
m_bundleRegistry = std::move(bundleRegistry);
|
||||
}
|
||||
|
||||
void JSCExecutor::registerBundle(
|
||||
uint32_t bundleId,
|
||||
const std::string& bundlePath) {
|
||||
if (m_bundleRegistry) {
|
||||
m_bundleRegistry->registerBundle(bundleId, bundlePath);
|
||||
} else {
|
||||
auto stPath = JSCExecutor::getSyntheticBundlePath(bundleId, bundlePath);
|
||||
auto sourceUrlStr = String(m_context, stPath.c_str());
|
||||
auto source = adoptString(JSBigFileString::fromPath(bundlePath));
|
||||
evaluateScript(m_context, source, sourceUrlStr);
|
||||
}
|
||||
}
|
||||
|
||||
void JSCExecutor::bindBridge() throw(JSException) {
|
||||
SystraceSection s("JSCExecutor::bindBridge");
|
||||
std::call_once(m_bindFlag, [this] {
|
||||
auto global = Object::getGlobalObject(m_context);
|
||||
auto batchedBridgeValue = global.getProperty("__fbBatchedBridge");
|
||||
if (batchedBridgeValue.isUndefined()) {
|
||||
auto requireBatchedBridge =
|
||||
global.getProperty("__fbRequireBatchedBridge");
|
||||
if (!requireBatchedBridge.isUndefined()) {
|
||||
batchedBridgeValue = requireBatchedBridge.asObject().callAsFunction({});
|
||||
}
|
||||
if (batchedBridgeValue.isUndefined()) {
|
||||
throw JSException(
|
||||
"Could not get BatchedBridge, make sure your bundle is packaged correctly");
|
||||
}
|
||||
}
|
||||
|
||||
auto batchedBridge = batchedBridgeValue.asObject();
|
||||
m_callFunctionReturnFlushedQueueJS =
|
||||
batchedBridge.getProperty("callFunctionReturnFlushedQueue").asObject();
|
||||
m_invokeCallbackAndReturnFlushedQueueJS =
|
||||
batchedBridge.getProperty("invokeCallbackAndReturnFlushedQueue")
|
||||
.asObject();
|
||||
m_flushedQueueJS = batchedBridge.getProperty("flushedQueue").asObject();
|
||||
m_callFunctionReturnResultAndFlushedQueueJS =
|
||||
batchedBridge.getProperty("callFunctionReturnResultAndFlushedQueue")
|
||||
.asObject();
|
||||
});
|
||||
}
|
||||
|
||||
void JSCExecutor::callNativeModules(Value&& value) {
|
||||
SystraceSection s("JSCExecutor::callNativeModules");
|
||||
// If this fails, you need to pass a fully functional delegate with a
|
||||
// module registry to the factory/ctor.
|
||||
CHECK(m_delegate) << "Attempting to use native modules without a delegate";
|
||||
try {
|
||||
auto calls = value.toJSONString();
|
||||
m_delegate->callNativeModules(*this, folly::parseJson(calls), true);
|
||||
} catch (...) {
|
||||
std::string message = "Error in callNativeModules()";
|
||||
try {
|
||||
message += ":" + value.toString().str();
|
||||
} catch (...) {
|
||||
// ignored
|
||||
}
|
||||
std::throw_with_nested(std::runtime_error(message));
|
||||
}
|
||||
}
|
||||
|
||||
void JSCExecutor::flush() {
|
||||
SystraceSection s("JSCExecutor::flush");
|
||||
|
||||
if (m_flushedQueueJS) {
|
||||
callNativeModules(m_flushedQueueJS->callAsFunction({}));
|
||||
return;
|
||||
}
|
||||
|
||||
// When a native module is called from JS, BatchedBridge.enqueueNativeCall()
|
||||
// is invoked. For that to work, require('BatchedBridge') has to be called,
|
||||
// and when that happens, __fbBatchedBridge is set as a side effect.
|
||||
auto global = Object::getGlobalObject(m_context);
|
||||
auto batchedBridgeValue = global.getProperty("__fbBatchedBridge");
|
||||
// So here, if __fbBatchedBridge doesn't exist, then we know no native calls
|
||||
// have happened, and we were able to determine this without forcing
|
||||
// BatchedBridge to be loaded as a side effect.
|
||||
if (!batchedBridgeValue.isUndefined()) {
|
||||
// If calls were made, we bind to the JS bridge methods, and use them to
|
||||
// get the pending queue of native calls.
|
||||
bindBridge();
|
||||
callNativeModules(m_flushedQueueJS->callAsFunction({}));
|
||||
} else if (m_delegate) {
|
||||
// If we have a delegate, we need to call it; we pass a null list to
|
||||
// callNativeModules, since we know there are no native calls, without
|
||||
// calling into JS again. If no calls were made and there's no delegate,
|
||||
// nothing happens, which is correct.
|
||||
callNativeModules(Value::makeNull(m_context));
|
||||
}
|
||||
}
|
||||
|
||||
void JSCExecutor::callFunction(
|
||||
const std::string& moduleId,
|
||||
const std::string& methodId,
|
||||
const folly::dynamic& arguments) {
|
||||
SystraceSection s("JSCExecutor::callFunction");
|
||||
// This weird pattern is because Value is not default constructible.
|
||||
// The lambda is inlined, so there's no overhead.
|
||||
auto result = [&] {
|
||||
JSContextLock lock(m_context);
|
||||
try {
|
||||
if (!m_callFunctionReturnResultAndFlushedQueueJS) {
|
||||
bindBridge();
|
||||
}
|
||||
return m_callFunctionReturnFlushedQueueJS->callAsFunction(
|
||||
{Value(m_context, String::createExpectingAscii(m_context, moduleId)),
|
||||
Value(m_context, String::createExpectingAscii(m_context, methodId)),
|
||||
Value::fromDynamic(m_context, std::move(arguments))});
|
||||
} catch (...) {
|
||||
std::throw_with_nested(
|
||||
std::runtime_error("Error calling " + moduleId + "." + methodId));
|
||||
}
|
||||
}();
|
||||
callNativeModules(std::move(result));
|
||||
}
|
||||
|
||||
void JSCExecutor::invokeCallback(
|
||||
const double callbackId,
|
||||
const folly::dynamic& arguments) {
|
||||
SystraceSection s("JSCExecutor::invokeCallback");
|
||||
auto result = [&] {
|
||||
JSContextLock lock(m_context);
|
||||
try {
|
||||
if (!m_invokeCallbackAndReturnFlushedQueueJS) {
|
||||
bindBridge();
|
||||
}
|
||||
return m_invokeCallbackAndReturnFlushedQueueJS->callAsFunction(
|
||||
{Value::makeNumber(m_context, callbackId),
|
||||
Value::fromDynamic(m_context, std::move(arguments))});
|
||||
} catch (...) {
|
||||
std::throw_with_nested(std::runtime_error(
|
||||
folly::to<std::string>("Error invoking callback ", callbackId)));
|
||||
}
|
||||
}();
|
||||
callNativeModules(std::move(result));
|
||||
}
|
||||
|
||||
void JSCExecutor::setGlobalVariable(
|
||||
std::string propName,
|
||||
std::unique_ptr<const JSBigString> jsonValue) {
|
||||
try {
|
||||
SystraceSection s("JSCExecutor::setGlobalVariable", "propName", propName);
|
||||
auto valueToInject = Value::fromJSON(adoptString(std::move(jsonValue)));
|
||||
Object::getGlobalObject(m_context).setProperty(
|
||||
propName.c_str(), valueToInject);
|
||||
} catch (...) {
|
||||
std::throw_with_nested(
|
||||
std::runtime_error("Error setting global variable: " + propName));
|
||||
}
|
||||
}
|
||||
|
||||
std::string JSCExecutor::getDescription() {
|
||||
#if defined(__APPLE__)
|
||||
if (isCustomJSCPtr(m_context)) {
|
||||
return "Custom JSC";
|
||||
} else {
|
||||
return "System JSC";
|
||||
}
|
||||
#else
|
||||
return "JSC";
|
||||
#endif
|
||||
}
|
||||
|
||||
String JSCExecutor::adoptString(std::unique_ptr<const JSBigString> script) {
|
||||
#if defined(WITH_FBJSCEXTENSIONS)
|
||||
const JSBigString* string = script.release();
|
||||
auto jsString = JSStringCreateAdoptingExternal(
|
||||
string->c_str(), string->size(), (void*)string, [](void* s) {
|
||||
delete static_cast<JSBigString*>(s);
|
||||
});
|
||||
return String::adopt(m_context, jsString);
|
||||
#else
|
||||
return script->isAscii()
|
||||
? String::createExpectingAscii(m_context, script->c_str(), script->size())
|
||||
: String(m_context, script->c_str());
|
||||
#endif
|
||||
}
|
||||
|
||||
void* JSCExecutor::getJavaScriptContext() {
|
||||
return m_context;
|
||||
}
|
||||
|
||||
bool JSCExecutor::isInspectable() {
|
||||
return canUseInspector(m_context);
|
||||
}
|
||||
|
||||
void JSCExecutor::handleMemoryPressure(int pressureLevel) {
|
||||
#ifdef WITH_JSC_MEMORY_PRESSURE
|
||||
JSHandleMemoryPressure(
|
||||
this, m_context, static_cast<JSMemoryPressure>(pressureLevel));
|
||||
#endif
|
||||
}
|
||||
|
||||
void JSCExecutor::flushQueueImmediate(Value&& queue) {
|
||||
auto queueStr = queue.toJSONString();
|
||||
m_delegate->callNativeModules(*this, folly::parseJson(queueStr), false);
|
||||
}
|
||||
|
||||
void JSCExecutor::loadModule(uint32_t bundleId, uint32_t moduleId) {
|
||||
auto module = m_bundleRegistry->getModule(bundleId, moduleId);
|
||||
auto sourceUrl = String::createExpectingAscii(m_context, module.name);
|
||||
auto source = adoptString(
|
||||
std::unique_ptr<JSBigString>(new JSBigStdString(module.code)));
|
||||
evaluateScript(m_context, source, sourceUrl);
|
||||
}
|
||||
|
||||
// Native JS hooks
|
||||
template <JSValueRef (JSCExecutor::*method)(size_t, const JSValueRef[])>
|
||||
void JSCExecutor::installNativeHook(const char* name) {
|
||||
installGlobalFunction(m_context, name, exceptionWrapMethod<method>());
|
||||
}
|
||||
|
||||
JSValueRef JSCExecutor::getNativeModule(
|
||||
JSObjectRef object,
|
||||
JSStringRef propertyName) {
|
||||
if (JSC_JSStringIsEqualToUTF8CString(m_context, propertyName, "name")) {
|
||||
return Value(m_context, String(m_context, "NativeModules"));
|
||||
}
|
||||
|
||||
return m_nativeModules.getModule(m_context, propertyName);
|
||||
}
|
||||
|
||||
JSValueRef JSCExecutor::nativeRequire(
|
||||
size_t count,
|
||||
const JSValueRef arguments[]) {
|
||||
if (count > 2 || count == 0) {
|
||||
throw std::invalid_argument("Got wrong number of args");
|
||||
}
|
||||
|
||||
uint32_t moduleId =
|
||||
folly::to<uint32_t>(Value(m_context, arguments[0]).getNumberOrThrow());
|
||||
uint32_t bundleId = count == 2
|
||||
? folly::to<uint32_t>(Value(m_context, arguments[1]).getNumberOrThrow())
|
||||
: 0;
|
||||
|
||||
ReactMarker::logMarker(ReactMarker::NATIVE_REQUIRE_START);
|
||||
loadModule(bundleId, moduleId);
|
||||
ReactMarker::logMarker(ReactMarker::NATIVE_REQUIRE_STOP);
|
||||
return Value::makeUndefined(m_context);
|
||||
}
|
||||
|
||||
JSValueRef JSCExecutor::nativeFlushQueueImmediate(
|
||||
size_t argumentCount,
|
||||
const JSValueRef arguments[]) {
|
||||
if (argumentCount != 1) {
|
||||
throw std::invalid_argument("Got wrong number of args");
|
||||
}
|
||||
|
||||
flushQueueImmediate(Value(m_context, arguments[0]));
|
||||
return Value::makeUndefined(m_context);
|
||||
}
|
||||
|
||||
JSValueRef JSCExecutor::nativeCallSyncHook(
|
||||
size_t argumentCount,
|
||||
const JSValueRef arguments[]) {
|
||||
if (argumentCount != 3) {
|
||||
throw std::invalid_argument("Got wrong number of args");
|
||||
}
|
||||
|
||||
unsigned int moduleId = Value(m_context, arguments[0]).asUnsignedInteger();
|
||||
unsigned int methodId = Value(m_context, arguments[1]).asUnsignedInteger();
|
||||
folly::dynamic args =
|
||||
folly::parseJson(Value(m_context, arguments[2]).toJSONString());
|
||||
|
||||
if (!args.isArray()) {
|
||||
throw std::invalid_argument(folly::to<std::string>(
|
||||
"method parameters should be array, but are ", args.typeName()));
|
||||
}
|
||||
|
||||
MethodCallResult result = m_delegate->callSerializableNativeHook(
|
||||
*this, moduleId, methodId, std::move(args));
|
||||
if (!result.hasValue()) {
|
||||
return Value::makeUndefined(m_context);
|
||||
}
|
||||
return Value::fromDynamic(m_context, result.value());
|
||||
}
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
@ -1,141 +0,0 @@
|
||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
||||
|
||||
// This source code is licensed under the MIT license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
#include <cxxreact/JSCNativeModules.h>
|
||||
#include <cxxreact/JSExecutor.h>
|
||||
#include <folly/Optional.h>
|
||||
#include <folly/json.h>
|
||||
#include <jschelpers/JSCHelpers.h>
|
||||
#include <jschelpers/JavaScriptCore.h>
|
||||
#include <jschelpers/Value.h>
|
||||
#include <privatedata/PrivateDataBase.h>
|
||||
|
||||
#ifndef RN_EXPORT
|
||||
#define RN_EXPORT __attribute__((visibility("default")))
|
||||
#endif
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
class MessageQueueThread;
|
||||
class RAMBundleRegistry;
|
||||
|
||||
class RN_EXPORT JSCExecutorFactory : public JSExecutorFactory {
|
||||
public:
|
||||
JSCExecutorFactory(const folly::dynamic& jscConfig) :
|
||||
m_jscConfig(jscConfig) {}
|
||||
std::unique_ptr<JSExecutor> createJSExecutor(
|
||||
std::shared_ptr<ExecutorDelegate> delegate,
|
||||
std::shared_ptr<MessageQueueThread> jsQueue) override;
|
||||
private:
|
||||
std::string m_cacheDir;
|
||||
folly::dynamic m_jscConfig;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct JSCValueEncoder {
|
||||
// If you get a build error here, it means the compiler can't see the template instantation of toJSCValue
|
||||
// applicable to your type.
|
||||
static const Value toJSCValue(JSGlobalContextRef ctx, T&& value);
|
||||
};
|
||||
|
||||
template<>
|
||||
struct JSCValueEncoder<folly::dynamic> {
|
||||
static const Value toJSCValue(JSGlobalContextRef ctx, const folly::dynamic &&value) {
|
||||
return Value::fromDynamic(ctx, value);
|
||||
}
|
||||
};
|
||||
|
||||
class RN_EXPORT JSCExecutor : public JSExecutor, public PrivateDataBase {
|
||||
public:
|
||||
/**
|
||||
* Must be invoked from thread this Executor will run on.
|
||||
*/
|
||||
explicit JSCExecutor(std::shared_ptr<ExecutorDelegate> delegate,
|
||||
std::shared_ptr<MessageQueueThread> messageQueueThread,
|
||||
const folly::dynamic& jscConfig) throw(JSException);
|
||||
~JSCExecutor() override;
|
||||
|
||||
virtual void loadApplicationScript(
|
||||
std::unique_ptr<const JSBigString> script,
|
||||
std::string sourceURL) override;
|
||||
|
||||
virtual void setBundleRegistry(std::unique_ptr<RAMBundleRegistry> bundleRegistry) override;
|
||||
virtual void registerBundle(uint32_t bundleId, const std::string& bundlePath) override;
|
||||
|
||||
virtual void callFunction(
|
||||
const std::string& moduleId,
|
||||
const std::string& methodId,
|
||||
const folly::dynamic& arguments) override;
|
||||
|
||||
virtual void invokeCallback(
|
||||
const double callbackId,
|
||||
const folly::dynamic& arguments) override;
|
||||
|
||||
virtual void setGlobalVariable(
|
||||
std::string propName,
|
||||
std::unique_ptr<const JSBigString> jsonValue) override;
|
||||
|
||||
virtual std::string getDescription() override;
|
||||
|
||||
virtual void* getJavaScriptContext() override;
|
||||
|
||||
virtual bool isInspectable() override;
|
||||
|
||||
virtual void handleMemoryPressure(int pressureLevel) override;
|
||||
|
||||
virtual void destroy() override;
|
||||
|
||||
void setContextName(const std::string& name);
|
||||
|
||||
private:
|
||||
JSGlobalContextRef m_context;
|
||||
std::shared_ptr<ExecutorDelegate> m_delegate;
|
||||
std::shared_ptr<bool> m_isDestroyed = std::shared_ptr<bool>(new bool(false));
|
||||
std::shared_ptr<MessageQueueThread> m_messageQueueThread;
|
||||
std::unique_ptr<RAMBundleRegistry> m_bundleRegistry;
|
||||
JSCNativeModules m_nativeModules;
|
||||
folly::dynamic m_jscConfig;
|
||||
std::once_flag m_bindFlag;
|
||||
|
||||
folly::Optional<Object> m_invokeCallbackAndReturnFlushedQueueJS;
|
||||
folly::Optional<Object> m_callFunctionReturnFlushedQueueJS;
|
||||
folly::Optional<Object> m_flushedQueueJS;
|
||||
folly::Optional<Object> m_callFunctionReturnResultAndFlushedQueueJS;
|
||||
|
||||
void initOnJSVMThread() throw(JSException);
|
||||
static bool isNetworkInspected(const std::string &owner, const std::string &app, const std::string &device);
|
||||
void terminateOnJSVMThread();
|
||||
void bindBridge() throw(JSException);
|
||||
void callNativeModules(Value&&);
|
||||
void flush();
|
||||
void flushQueueImmediate(Value&&);
|
||||
void loadModule(uint32_t bundleId, uint32_t moduleId);
|
||||
|
||||
String adoptString(std::unique_ptr<const JSBigString>);
|
||||
|
||||
template<JSValueRef (JSCExecutor::*method)(size_t, const JSValueRef[])>
|
||||
void installNativeHook(const char* name);
|
||||
|
||||
JSValueRef getNativeModule(JSObjectRef object, JSStringRef propertyName);
|
||||
|
||||
JSValueRef nativeRequire(
|
||||
size_t argumentCount,
|
||||
const JSValueRef arguments[]);
|
||||
JSValueRef nativeFlushQueueImmediate(
|
||||
size_t argumentCount,
|
||||
const JSValueRef arguments[]);
|
||||
JSValueRef nativeCallSyncHook(
|
||||
size_t argumentCount,
|
||||
const JSValueRef arguments[]);
|
||||
};
|
||||
|
||||
} }
|
@ -1,73 +0,0 @@
|
||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
||||
|
||||
// This source code is licensed under the MIT license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
|
||||
#include "JSCLegacyTracing.h"
|
||||
|
||||
#if defined(WITH_JSC_EXTRA_TRACING)
|
||||
|
||||
#include <fbsystrace.h>
|
||||
#include <JavaScriptCore/API/JSProfilerPrivate.h>
|
||||
#include <jschelpers/JSCHelpers.h>
|
||||
#include <jschelpers/Value.h>
|
||||
|
||||
static const char *ENABLED_FBSYSTRACE_PROFILE_NAME = "__fbsystrace__";
|
||||
|
||||
using namespace facebook::react;
|
||||
|
||||
static int64_t int64FromJSValue(JSContextRef ctx, JSValueRef value, JSValueRef* exception) {
|
||||
return static_cast<int64_t>(JSC_JSValueToNumber(ctx, value, exception));
|
||||
}
|
||||
|
||||
static JSValueRef nativeTraceBeginLegacy(
|
||||
JSContextRef ctx,
|
||||
JSObjectRef function,
|
||||
JSObjectRef thisObject,
|
||||
size_t argumentCount,
|
||||
const JSValueRef arguments[],
|
||||
JSValueRef* exception) {
|
||||
if (FBSYSTRACE_LIKELY(argumentCount >= 1)) {
|
||||
uint64_t tag = int64FromJSValue(ctx, arguments[0], exception);
|
||||
if (!fbsystrace_is_tracing(tag)) {
|
||||
return Value::makeUndefined(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
JSStartProfiling(ctx, String(ctx, ENABLED_FBSYSTRACE_PROFILE_NAME), true);
|
||||
|
||||
return Value::makeUndefined(ctx);
|
||||
}
|
||||
|
||||
static JSValueRef nativeTraceEndLegacy(
|
||||
JSContextRef ctx,
|
||||
JSObjectRef function,
|
||||
JSObjectRef thisObject,
|
||||
size_t argumentCount,
|
||||
const JSValueRef arguments[],
|
||||
JSValueRef* exception) {
|
||||
if (FBSYSTRACE_LIKELY(argumentCount >= 1)) {
|
||||
uint64_t tag = int64FromJSValue(ctx, arguments[0], exception);
|
||||
if (!fbsystrace_is_tracing(tag)) {
|
||||
return Value::makeUndefined(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
JSEndProfiling(ctx, String(ctx, ENABLED_FBSYSTRACE_PROFILE_NAME));
|
||||
|
||||
return Value::makeUndefined(ctx);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
void addNativeTracingLegacyHooks(JSGlobalContextRef ctx) {
|
||||
#if defined(WITH_JSC_EXTRA_TRACING)
|
||||
installGlobalFunction(ctx, "nativeTraceBeginLegacy", nativeTraceBeginLegacy);
|
||||
installGlobalFunction(ctx, "nativeTraceEndLegacy", nativeTraceEndLegacy);
|
||||
#endif
|
||||
}
|
||||
|
||||
} }
|
@ -1,15 +0,0 @@
|
||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
||||
|
||||
// This source code is licensed under the MIT license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <jschelpers/JavaScriptCore.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
void addNativeTracingLegacyHooks(JSGlobalContextRef ctx);
|
||||
|
||||
} }
|
@ -1,50 +0,0 @@
|
||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
||||
|
||||
// This source code is licensed under the MIT license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
|
||||
#include "JSCMemory.h"
|
||||
|
||||
#ifdef WITH_FB_MEMORY_PROFILING
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <JavaScriptCore/API/JSProfilerPrivate.h>
|
||||
#include <jschelpers/JSCHelpers.h>
|
||||
#include <jschelpers/Value.h>
|
||||
|
||||
using namespace facebook::react;
|
||||
|
||||
static JSValueRef nativeCaptureHeap(
|
||||
JSContextRef ctx,
|
||||
JSObjectRef function,
|
||||
JSObjectRef thisObject,
|
||||
size_t argumentCount,
|
||||
const JSValueRef arguments[],
|
||||
JSValueRef* exception) {
|
||||
if (argumentCount < 1) {
|
||||
if (exception) {
|
||||
*exception = Value::makeError(
|
||||
ctx,
|
||||
"nativeCaptureHeap requires the path to save the capture");
|
||||
}
|
||||
return Value::makeUndefined(ctx);
|
||||
}
|
||||
|
||||
auto outputFilename = Value(ctx, arguments[0]).toString();
|
||||
JSCaptureHeap(ctx, outputFilename.str().c_str(), exception);
|
||||
return Value::makeUndefined(ctx);
|
||||
}
|
||||
|
||||
#endif // WITH_FB_MEMORY_PROFILING
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
void addJSCMemoryHooks(JSGlobalContextRef ctx) {
|
||||
#ifdef WITH_FB_MEMORY_PROFILING
|
||||
installGlobalFunction(ctx, "nativeCaptureHeap", nativeCaptureHeap);
|
||||
#endif // WITH_FB_MEMORY_PROFILING
|
||||
}
|
||||
|
||||
} }
|
@ -1,15 +0,0 @@
|
||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
||||
|
||||
// This source code is licensed under the MIT license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <jschelpers/JavaScriptCore.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
void addJSCMemoryHooks(JSGlobalContextRef ctx);
|
||||
|
||||
} }
|
@ -1,76 +0,0 @@
|
||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
||||
|
||||
// This source code is licensed under the MIT license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
|
||||
#include "JSCNativeModules.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "ModuleRegistry.h"
|
||||
#include "ReactMarker.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
JSCNativeModules::JSCNativeModules(std::shared_ptr<ModuleRegistry> moduleRegistry) :
|
||||
m_moduleRegistry(std::move(moduleRegistry)) {}
|
||||
|
||||
JSValueRef JSCNativeModules::getModule(JSContextRef context, JSStringRef jsName) {
|
||||
if (!m_moduleRegistry) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::string moduleName = String::ref(context, jsName).str();
|
||||
|
||||
const auto it = m_objects.find(moduleName);
|
||||
if (it != m_objects.end()) {
|
||||
return static_cast<JSObjectRef>(it->second);
|
||||
}
|
||||
|
||||
auto module = createModule(moduleName, context);
|
||||
if (!module.hasValue()) {
|
||||
// Allow lookup to continue in the objects own properties, which allows for overrides of NativeModules
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Protect since we'll be holding on to this value, even though JS may not
|
||||
module->makeProtected();
|
||||
|
||||
auto result = m_objects.emplace(std::move(moduleName), std::move(*module)).first;
|
||||
return static_cast<JSObjectRef>(result->second);
|
||||
}
|
||||
|
||||
void JSCNativeModules::reset() {
|
||||
m_genNativeModuleJS = nullptr;
|
||||
m_objects.clear();
|
||||
}
|
||||
|
||||
folly::Optional<Object> JSCNativeModules::createModule(const std::string& name, JSContextRef context) {
|
||||
ReactMarker::logTaggedMarker(ReactMarker::NATIVE_MODULE_SETUP_START, name.c_str());
|
||||
|
||||
if (!m_genNativeModuleJS) {
|
||||
auto global = Object::getGlobalObject(context);
|
||||
m_genNativeModuleJS = global.getProperty("__fbGenNativeModule").asObject();
|
||||
m_genNativeModuleJS->makeProtected();
|
||||
}
|
||||
|
||||
auto result = m_moduleRegistry->getConfig(name);
|
||||
if (!result.hasValue()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Value moduleInfo = m_genNativeModuleJS->callAsFunction({
|
||||
Value::fromDynamic(context, result->config),
|
||||
Value::makeNumber(context, result->index)
|
||||
});
|
||||
CHECK(!moduleInfo.isNull()) << "Module returned from genNativeModule is null";
|
||||
|
||||
folly::Optional<Object> module(moduleInfo.asObject().getProperty("module").asObject());
|
||||
|
||||
ReactMarker::logTaggedMarker(ReactMarker::NATIVE_MODULE_SETUP_STOP, name.c_str());
|
||||
|
||||
return module;
|
||||
}
|
||||
|
||||
} }
|
@ -1,38 +0,0 @@
|
||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
||||
|
||||
// This source code is licensed under the MIT license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <folly/Optional.h>
|
||||
#include <jschelpers/Value.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
class ModuleRegistry;
|
||||
|
||||
/**
|
||||
* Holds and creates JS representations of the modules in ModuleRegistry
|
||||
*/
|
||||
class JSCNativeModules {
|
||||
|
||||
public:
|
||||
explicit JSCNativeModules(std::shared_ptr<ModuleRegistry> moduleRegistry);
|
||||
JSValueRef getModule(JSContextRef context, JSStringRef name);
|
||||
void reset();
|
||||
|
||||
private:
|
||||
folly::Optional<Object> m_genNativeModuleJS;
|
||||
std::shared_ptr<ModuleRegistry> m_moduleRegistry;
|
||||
std::unordered_map<std::string, Object> m_objects;
|
||||
|
||||
folly::Optional<Object> createModule(const std::string& name, JSContextRef context);
|
||||
};
|
||||
|
||||
}
|
||||
}
|
@ -1,98 +0,0 @@
|
||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
||||
|
||||
// This source code is licensed under the MIT license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
|
||||
#include "JSCPerfStats.h"
|
||||
|
||||
#ifdef JSC_HAS_PERF_STATS_API
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <sys/resource.h>
|
||||
|
||||
#include <JavaScriptCore/JSPerfStats.h>
|
||||
#include <jschelpers/JSCHelpers.h>
|
||||
#include <jschelpers/Value.h>
|
||||
|
||||
using namespace facebook::react;
|
||||
|
||||
static uint64_t toMillis(struct timeval tv) {
|
||||
return tv.tv_sec * 1000ULL + tv.tv_usec / 1000ULL;
|
||||
}
|
||||
|
||||
static JSValueRef nativeGetProcessPerfStats(
|
||||
JSContextRef ctx,
|
||||
JSObjectRef function,
|
||||
JSObjectRef thisObject,
|
||||
size_t argumentCount,
|
||||
const JSValueRef arguments[],
|
||||
JSValueRef* exception) {
|
||||
struct rusage usage{};
|
||||
if (getrusage(RUSAGE_SELF, &usage) != 0) {
|
||||
return Value::makeUndefined(ctx);
|
||||
}
|
||||
|
||||
auto result = Object::create(ctx);
|
||||
uint64_t cpu_time_ms = toMillis(usage.ru_utime) + toMillis(usage.ru_stime);
|
||||
result.setProperty("major_faults", Value::makeNumber(ctx, usage.ru_majflt));
|
||||
result.setProperty("minor_faults", Value::makeNumber(ctx, usage.ru_minflt));
|
||||
result.setProperty("cpu_time_ms", Value::makeNumber(ctx, cpu_time_ms));
|
||||
result.setProperty("input_blocks", Value::makeNumber(ctx, usage.ru_inblock));
|
||||
result.setProperty("output_blocks", Value::makeNumber(ctx, usage.ru_oublock));
|
||||
return static_cast<JSObjectRef>(result);
|
||||
}
|
||||
|
||||
static JSValueRef nativeGetHeapStats(
|
||||
JSContextRef ctx,
|
||||
JSObjectRef function,
|
||||
JSObjectRef thisObject,
|
||||
size_t argumentCount,
|
||||
const JSValueRef arguments[],
|
||||
JSValueRef* exception) {
|
||||
JSHeapStats heapStats = {0};
|
||||
JSGetHeapStats(ctx, &heapStats);
|
||||
|
||||
auto result = Object::create(ctx);
|
||||
result.setProperty("size", Value::makeNumber(ctx, heapStats.size));
|
||||
result.setProperty("extra_size", Value::makeNumber(ctx, heapStats.extraSize));
|
||||
result.setProperty("capacity", Value::makeNumber(ctx, heapStats.capacity));
|
||||
result.setProperty("object_count", Value::makeNumber(ctx, heapStats.objectCount));
|
||||
result.setProperty("object_size", Value::makeNumber(ctx, heapStats.objectSizeAfterLastCollect));
|
||||
result.setProperty("object_capacity", Value::makeNumber(ctx, heapStats.objectCapacityAfterLastCollect));
|
||||
result.setProperty("block_size", Value::makeNumber(ctx, heapStats.blockSize));
|
||||
result.setProperty("malloc_size", Value::makeNumber(ctx, heapStats.mallocSize));
|
||||
return static_cast<JSObjectRef>(result);
|
||||
}
|
||||
|
||||
static JSValueRef nativeGetGCStats(
|
||||
JSContextRef ctx,
|
||||
JSObjectRef function,
|
||||
JSObjectRef thisObject,
|
||||
size_t argumentCount,
|
||||
const JSValueRef arguments[],
|
||||
JSValueRef* exception) {
|
||||
JSGCStats gcStats = {0};
|
||||
JSGetGCStats(ctx, &gcStats);
|
||||
|
||||
auto result = Object::create(ctx);
|
||||
result.setProperty("last_full_gc_length", Value::makeNumber(ctx, gcStats.lastFullGCLength));
|
||||
result.setProperty("last_eden_gc_length", Value::makeNumber(ctx, gcStats.lastEdenGCLength));
|
||||
return static_cast<JSObjectRef>(result);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
void addJSCPerfStatsHooks(JSGlobalContextRef ctx) {
|
||||
#ifdef JSC_HAS_PERF_STATS_API
|
||||
installGlobalFunction(ctx, "nativeGetProcessPerfStats", nativeGetProcessPerfStats);
|
||||
installGlobalFunction(ctx, "nativeGetHeapStats", nativeGetHeapStats);
|
||||
installGlobalFunction(ctx, "nativeGetGCStats", nativeGetGCStats);
|
||||
#endif
|
||||
}
|
||||
|
||||
} }
|
@ -1,15 +0,0 @@
|
||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
||||
|
||||
// This source code is licensed under the MIT license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <jschelpers/JavaScriptCore.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
void addJSCPerfStatsHooks(JSGlobalContextRef ctx);
|
||||
|
||||
} }
|
@ -1,31 +0,0 @@
|
||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
||||
|
||||
// This source code is licensed under the MIT license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
|
||||
#include "JSCSamplingProfiler.h"
|
||||
|
||||
#include <jschelpers/JSCHelpers.h>
|
||||
|
||||
static JSValueRef pokeSamplingProfiler(
|
||||
JSContextRef ctx,
|
||||
JSObjectRef function,
|
||||
JSObjectRef thisObject,
|
||||
size_t argumentCount,
|
||||
const JSValueRef arguments[],
|
||||
JSValueRef* exception) {
|
||||
return JSC_JSPokeSamplingProfiler(ctx);
|
||||
}
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
void initSamplingProfilerOnMainJSCThread(JSGlobalContextRef ctx) {
|
||||
JSC_JSStartSamplingProfilingOnMainJSCThread(ctx);
|
||||
|
||||
// Allow the profiler to be poked from JS as well
|
||||
// (see SamplingProfiler.js for an example of how it could be used with the JSCSamplingProfiler module).
|
||||
installGlobalFunction(ctx, "pokeSamplingProfiler", pokeSamplingProfiler);
|
||||
}
|
||||
|
||||
} }
|
@ -1,15 +0,0 @@
|
||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
||||
|
||||
// This source code is licensed under the MIT license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <jschelpers/JavaScriptCore.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
void initSamplingProfilerOnMainJSCThread(JSGlobalContextRef ctx);
|
||||
|
||||
} }
|
@ -1,323 +0,0 @@
|
||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
||||
|
||||
// This source code is licensed under the MIT license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
|
||||
#include "JSCTracing.h"
|
||||
|
||||
#if defined(WITH_FBSYSTRACE) && (defined(WITH_JSC_EXTRA_TRACING) || DEBUG)
|
||||
#define USE_JSCTRACING 1
|
||||
#else
|
||||
#define USE_JSCTRACING 0
|
||||
#endif
|
||||
|
||||
#if USE_JSCTRACING
|
||||
|
||||
#include <algorithm>
|
||||
#include <fbsystrace.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <jschelpers/JavaScriptCore.h>
|
||||
#include <jschelpers/JSCHelpers.h>
|
||||
#include <jschelpers/Value.h>
|
||||
|
||||
using std::min;
|
||||
using namespace facebook::react;
|
||||
|
||||
static int64_t int64FromJSValue(JSContextRef ctx, JSValueRef value, JSValueRef* exception) {
|
||||
return static_cast<int64_t>(JSC_JSValueToNumber(ctx, value, exception));
|
||||
}
|
||||
|
||||
static size_t copyTruncatedAsciiChars(
|
||||
char* buf,
|
||||
size_t bufLen,
|
||||
JSContextRef ctx,
|
||||
JSValueRef value,
|
||||
size_t maxLen) {
|
||||
JSStringRef jsString = JSC_JSValueToStringCopy(ctx, value, NULL);
|
||||
size_t stringLen = JSC_JSStringGetLength(ctx, jsString);
|
||||
// Unlike the Java version, we truncate from the end of the string,
|
||||
// rather than the beginning.
|
||||
size_t toWrite = min(stringLen, min(bufLen, maxLen));
|
||||
|
||||
const char *startBuf = buf;
|
||||
const JSChar* chars = JSC_JSStringGetCharactersPtr(ctx, jsString);
|
||||
while (toWrite-- > 0) {
|
||||
*(buf++) = (char)*(chars++);
|
||||
}
|
||||
|
||||
JSC_JSStringRelease(ctx, jsString);
|
||||
|
||||
// Return the number of bytes written
|
||||
return buf - startBuf;
|
||||
}
|
||||
|
||||
static size_t copyArgsToBuffer(
|
||||
char* buf,
|
||||
size_t bufLen,
|
||||
size_t pos,
|
||||
JSContextRef ctx,
|
||||
size_t argumentCount,
|
||||
const JSValueRef arguments[]) {
|
||||
char separator = '|';
|
||||
for (
|
||||
size_t idx = 0;
|
||||
idx + 1 < argumentCount; // Make sure key and value are present.
|
||||
idx += 2) {
|
||||
JSValueRef key = arguments[idx];
|
||||
JSValueRef value = arguments[idx+1];
|
||||
|
||||
buf[pos++] = separator;
|
||||
separator = ';';
|
||||
if (FBSYSTRACE_UNLIKELY(pos >= bufLen)) { break; }
|
||||
pos += copyTruncatedAsciiChars(
|
||||
buf + pos, bufLen - pos, ctx, key, FBSYSTRACE_MAX_MESSAGE_LENGTH);
|
||||
if (FBSYSTRACE_UNLIKELY(pos >= bufLen)) { break; }
|
||||
buf[pos++] = '=';
|
||||
if (FBSYSTRACE_UNLIKELY(pos >= bufLen)) { break; }
|
||||
pos += copyTruncatedAsciiChars(
|
||||
buf + pos, bufLen - pos, ctx, value, FBSYSTRACE_MAX_MESSAGE_LENGTH);
|
||||
if (FBSYSTRACE_UNLIKELY(pos >= bufLen)) { break; }
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
static JSValueRef nativeTraceBeginSection(
|
||||
JSContextRef ctx,
|
||||
JSObjectRef function,
|
||||
JSObjectRef thisObject,
|
||||
size_t argumentCount,
|
||||
const JSValueRef arguments[],
|
||||
JSValueRef* exception) {
|
||||
if (FBSYSTRACE_UNLIKELY(argumentCount < 2)) {
|
||||
if (exception) {
|
||||
*exception = Value::makeError(
|
||||
ctx,
|
||||
"nativeTraceBeginSection: requires at least 2 arguments");
|
||||
}
|
||||
return Value::makeUndefined(ctx);
|
||||
}
|
||||
|
||||
uint64_t tag = int64FromJSValue(ctx, arguments[0], exception);
|
||||
if (!fbsystrace_is_tracing(tag)) {
|
||||
return Value::makeUndefined(ctx);
|
||||
}
|
||||
|
||||
char buf[FBSYSTRACE_MAX_MESSAGE_LENGTH];
|
||||
size_t pos = 0;
|
||||
|
||||
pos += snprintf(buf + pos, sizeof(buf) - pos, "B|%d|", getpid());
|
||||
// Skip the overflow check here because the int will be small.
|
||||
pos += copyTruncatedAsciiChars(buf + pos, sizeof(buf) - pos, ctx, arguments[1], FBSYSTRACE_MAX_SECTION_NAME_LENGTH);
|
||||
// Skip the overflow check here because the section name will be small-ish.
|
||||
|
||||
pos = copyArgsToBuffer(buf, sizeof(buf), pos, ctx, argumentCount - 2, arguments + 2);
|
||||
if (FBSYSTRACE_UNLIKELY(pos >= sizeof(buf))) {
|
||||
goto flush;
|
||||
}
|
||||
|
||||
flush:
|
||||
fbsystrace_trace_raw(buf, min(pos, sizeof(buf)-1));
|
||||
|
||||
return Value::makeUndefined(ctx);
|
||||
}
|
||||
|
||||
static JSValueRef nativeTraceEndSection(
|
||||
JSContextRef ctx,
|
||||
JSObjectRef function,
|
||||
JSObjectRef thisObject,
|
||||
size_t argumentCount,
|
||||
const JSValueRef arguments[],
|
||||
JSValueRef* exception) {
|
||||
if (FBSYSTRACE_UNLIKELY(argumentCount < 1)) {
|
||||
if (exception) {
|
||||
*exception = Value::makeError(
|
||||
ctx,
|
||||
"nativeTraceEndSection: requires at least 1 argument");
|
||||
}
|
||||
return Value::makeUndefined(ctx);
|
||||
}
|
||||
|
||||
uint64_t tag = int64FromJSValue(ctx, arguments[0], exception);
|
||||
if (!fbsystrace_is_tracing(tag)) {
|
||||
return Value::makeUndefined(ctx);
|
||||
}
|
||||
|
||||
if (FBSYSTRACE_LIKELY(argumentCount == 1)) {
|
||||
fbsystrace_end_section(tag);
|
||||
} else {
|
||||
char buf[FBSYSTRACE_MAX_MESSAGE_LENGTH];
|
||||
size_t pos = 0;
|
||||
|
||||
buf[pos++] = 'E';
|
||||
buf[pos++] = '|';
|
||||
buf[pos++] = '|';
|
||||
pos = copyArgsToBuffer(buf, sizeof(buf), pos, ctx, argumentCount - 1, arguments + 1);
|
||||
if (FBSYSTRACE_UNLIKELY(pos >= sizeof(buf))) {
|
||||
goto flush;
|
||||
}
|
||||
|
||||
flush:
|
||||
fbsystrace_trace_raw(buf, min(pos, sizeof(buf)-1));
|
||||
}
|
||||
|
||||
return Value::makeUndefined(ctx);
|
||||
}
|
||||
|
||||
static JSValueRef beginOrEndAsync(
|
||||
bool isEnd,
|
||||
bool isFlow,
|
||||
JSContextRef ctx,
|
||||
size_t argumentCount,
|
||||
const JSValueRef arguments[],
|
||||
JSValueRef* exception) {
|
||||
if (FBSYSTRACE_UNLIKELY(argumentCount < 3)) {
|
||||
if (exception) {
|
||||
*exception = Value::makeError(
|
||||
ctx,
|
||||
"beginOrEndAsync: requires at least 3 arguments");
|
||||
}
|
||||
return Value::makeUndefined(ctx);
|
||||
}
|
||||
|
||||
uint64_t tag = int64FromJSValue(ctx, arguments[0], exception);
|
||||
if (!fbsystrace_is_tracing(tag)) {
|
||||
return Value::makeUndefined(ctx);
|
||||
}
|
||||
|
||||
char buf[FBSYSTRACE_MAX_MESSAGE_LENGTH];
|
||||
size_t pos = 0;
|
||||
|
||||
// This uses an if-then-else instruction in ARMv7, which should be cheaper
|
||||
// than a full branch.
|
||||
buf[pos++] = ((isFlow) ? (isEnd ? 'f' : 's') : (isEnd ? 'F' : 'S'));
|
||||
pos += snprintf(buf + pos, sizeof(buf) - pos, "|%d|", getpid());
|
||||
// Skip the overflow check here because the int will be small.
|
||||
pos += copyTruncatedAsciiChars(buf + pos, sizeof(buf) - pos, ctx, arguments[1], FBSYSTRACE_MAX_SECTION_NAME_LENGTH);
|
||||
// Skip the overflow check here because the section name will be small-ish.
|
||||
|
||||
// I tried some trickery to avoid a branch here, but gcc did not cooperate.
|
||||
// We could consider changing the implementation to be lest branchy in the
|
||||
// future.
|
||||
// This is not required for flow use an or to avoid introducing another branch
|
||||
if (!(isEnd | isFlow)) {
|
||||
buf[pos++] = '<';
|
||||
buf[pos++] = '0';
|
||||
buf[pos++] = '>';
|
||||
}
|
||||
buf[pos++] = '|';
|
||||
|
||||
// Append the cookie. It should be an integer, but copyTruncatedAsciiChars
|
||||
// will automatically convert it to a string. We might be able to get more
|
||||
// performance by just getting the number and doing to string
|
||||
// conversion ourselves. We truncate to FBSYSTRACE_MAX_SECTION_NAME_LENGTH
|
||||
// just to make sure we can avoid the overflow check even if the caller
|
||||
// passes in something bad.
|
||||
pos += copyTruncatedAsciiChars(buf + pos, sizeof(buf) - pos, ctx, arguments[2], FBSYSTRACE_MAX_SECTION_NAME_LENGTH);
|
||||
|
||||
pos = copyArgsToBuffer(buf, sizeof(buf), pos, ctx, argumentCount - 3, arguments + 3);
|
||||
if (FBSYSTRACE_UNLIKELY(pos >= sizeof(buf))) {
|
||||
goto flush;
|
||||
}
|
||||
|
||||
flush:
|
||||
fbsystrace_trace_raw(buf, min(pos, sizeof(buf)-1));
|
||||
|
||||
return Value::makeUndefined(ctx);
|
||||
}
|
||||
|
||||
static JSValueRef nativeTraceBeginAsyncSection(
|
||||
JSContextRef ctx,
|
||||
JSObjectRef function,
|
||||
JSObjectRef thisObject,
|
||||
size_t argumentCount,
|
||||
const JSValueRef arguments[],
|
||||
JSValueRef* exception) {
|
||||
return beginOrEndAsync(false /* isEnd */, false /* isFlow */,
|
||||
ctx, argumentCount, arguments, exception);
|
||||
}
|
||||
|
||||
static JSValueRef nativeTraceEndAsyncSection(
|
||||
JSContextRef ctx,
|
||||
JSObjectRef function,
|
||||
JSObjectRef thisObject,
|
||||
size_t argumentCount,
|
||||
const JSValueRef arguments[],
|
||||
JSValueRef* exception) {
|
||||
return beginOrEndAsync(true /* isEnd */, false /* isFlow */,
|
||||
ctx, argumentCount, arguments, exception);
|
||||
}
|
||||
|
||||
static JSValueRef nativeTraceBeginAsyncFlow(
|
||||
JSContextRef ctx,
|
||||
JSObjectRef function,
|
||||
JSObjectRef thisObject,
|
||||
size_t argumentCount,
|
||||
const JSValueRef arguments[],
|
||||
JSValueRef* exception) {
|
||||
return beginOrEndAsync(false /* isEnd */, true /* isFlow */,
|
||||
ctx, argumentCount, arguments, exception);
|
||||
}
|
||||
|
||||
static JSValueRef nativeTraceEndAsyncFlow(
|
||||
JSContextRef ctx,
|
||||
JSObjectRef function,
|
||||
JSObjectRef thisObject,
|
||||
size_t argumentCount,
|
||||
const JSValueRef arguments[],
|
||||
JSValueRef* exception) {
|
||||
return beginOrEndAsync(true /* isEnd */, true /* isFlow */,
|
||||
ctx, argumentCount, arguments, exception);
|
||||
}
|
||||
|
||||
static JSValueRef nativeTraceCounter(
|
||||
JSContextRef ctx,
|
||||
JSObjectRef function,
|
||||
JSObjectRef thisObject,
|
||||
size_t argumentCount,
|
||||
const JSValueRef arguments[],
|
||||
JSValueRef* exception) {
|
||||
if (FBSYSTRACE_UNLIKELY(argumentCount < 3)) {
|
||||
if (exception) {
|
||||
*exception = Value::makeError(
|
||||
ctx,
|
||||
"nativeTraceCounter: requires at least 3 arguments");
|
||||
}
|
||||
return Value::makeUndefined(ctx);
|
||||
}
|
||||
|
||||
uint64_t tag = int64FromJSValue(ctx, arguments[0], exception);
|
||||
if (!fbsystrace_is_tracing(tag)) {
|
||||
return Value::makeUndefined(ctx);
|
||||
}
|
||||
|
||||
char buf[FBSYSTRACE_MAX_MESSAGE_LENGTH];
|
||||
size_t len = copyTruncatedAsciiChars(buf, sizeof(buf), ctx,
|
||||
arguments[1], FBSYSTRACE_MAX_SECTION_NAME_LENGTH);
|
||||
buf[min(len,(FBSYSTRACE_MAX_MESSAGE_LENGTH-1))] = 0;
|
||||
int64_t value = int64FromJSValue(ctx, arguments[2], exception);
|
||||
|
||||
fbsystrace_counter(tag, buf, value);
|
||||
|
||||
return Value::makeUndefined(ctx);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
void addNativeTracingHooks(JSGlobalContextRef ctx) {
|
||||
#if USE_JSCTRACING
|
||||
installGlobalFunction(ctx, "nativeTraceBeginSection", nativeTraceBeginSection);
|
||||
installGlobalFunction(ctx, "nativeTraceEndSection", nativeTraceEndSection);
|
||||
installGlobalFunction(ctx, "nativeTraceBeginAsyncSection", nativeTraceBeginAsyncSection);
|
||||
installGlobalFunction(ctx, "nativeTraceEndAsyncSection", nativeTraceEndAsyncSection);
|
||||
installGlobalFunction(ctx, "nativeTraceBeginAsyncFlow", nativeTraceBeginAsyncFlow);
|
||||
installGlobalFunction(ctx, "nativeTraceEndAsyncFlow", nativeTraceEndAsyncFlow);
|
||||
installGlobalFunction(ctx, "nativeTraceCounter", nativeTraceCounter);
|
||||
#endif
|
||||
}
|
||||
|
||||
} }
|
@ -1,15 +0,0 @@
|
||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
||||
|
||||
// This source code is licensed under the MIT license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <jschelpers/JavaScriptCore.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
void addNativeTracingHooks(JSGlobalContextRef ctx);
|
||||
|
||||
} }
|
@ -1,24 +0,0 @@
|
||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
||||
|
||||
// This source code is licensed under the MIT license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
|
||||
#include "JSCUtils.h"
|
||||
|
||||
#include "RAMBundleRegistry.h"
|
||||
|
||||
#include <folly/Conv.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
String jsStringFromBigString(JSContextRef ctx, const JSBigString& bigstr) {
|
||||
if (bigstr.isAscii()) {
|
||||
return String::createExpectingAscii(ctx, bigstr.c_str(), bigstr.size());
|
||||
} else {
|
||||
return String(ctx, bigstr.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
||||
|
||||
// This source code is licensed under the MIT license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cxxreact/JSBigString.h>
|
||||
#include <jschelpers/JavaScriptCore.h>
|
||||
#include <jschelpers/Value.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
String jsStringFromBigString(JSContextRef ctx, const JSBigString& bigstr);
|
||||
|
||||
}
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
||||
|
||||
// This source code is licensed under the MIT license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
|
||||
#include "Platform.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
#if __clang__
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wglobal-constructors"
|
||||
#endif
|
||||
|
||||
namespace JSCNativeHooks {
|
||||
|
||||
Hook loggingHook = nullptr;
|
||||
Hook nowHook = nullptr;
|
||||
ConfigurationHook installPerfHooks = nullptr;
|
||||
|
||||
}
|
||||
|
||||
#if __clang__
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
} }
|
@ -1,39 +0,0 @@
|
||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
||||
|
||||
// This source code is licensed under the MIT license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <cxxreact/ReactMarker.h>
|
||||
#include <jschelpers/JavaScriptCore.h>
|
||||
|
||||
#ifndef RN_EXPORT
|
||||
#define RN_EXPORT __attribute__((visibility("default")))
|
||||
#endif
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
namespace JSCNativeHooks {
|
||||
|
||||
using Hook = JSValueRef(*)(
|
||||
JSContextRef ctx,
|
||||
JSObjectRef function,
|
||||
JSObjectRef thisObject,
|
||||
size_t argumentCount,
|
||||
const JSValueRef arguments[],
|
||||
JSValueRef *exception);
|
||||
extern RN_EXPORT Hook loggingHook;
|
||||
extern RN_EXPORT Hook nowHook;
|
||||
|
||||
typedef void(*ConfigurationHook)(JSGlobalContextRef);
|
||||
extern RN_EXPORT ConfigurationHook installPerfHooks;
|
||||
|
||||
}
|
||||
|
||||
} }
|
@ -1,273 +0,0 @@
|
||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
||||
|
||||
// This source code is licensed under the MIT license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <cxxreact/JSCExecutor.h>
|
||||
#include <cxxreact/MessageQueueThread.h>
|
||||
#include <cxxreact/MethodCall.h>
|
||||
|
||||
using namespace facebook;
|
||||
using namespace facebook::react;
|
||||
|
||||
|
||||
// TODO(12340362): Fix these tests. And add checks for sizes.
|
||||
/*
|
||||
|
||||
namespace {
|
||||
|
||||
std::string capturedMethodCalls;
|
||||
|
||||
struct NullDelegate : ExecutorDelegate {
|
||||
virtual void registerExecutor(std::unique_ptr<JSExecutor> executor,
|
||||
std::shared_ptr<MessageQueueThread> queue) {
|
||||
std::terminate();
|
||||
}
|
||||
|
||||
virtual std::unique_ptr<JSExecutor> unregisterExecutor(JSExecutor& executor) {
|
||||
std::terminate();
|
||||
}
|
||||
|
||||
virtual std::vector<std::string> moduleNames() {
|
||||
return std::vector<std::string>{};
|
||||
}
|
||||
|
||||
virtual folly::dynamic getModuleConfig(const std::string& name) {
|
||||
std::terminate();
|
||||
}
|
||||
virtual void callNativeModules(
|
||||
JSExecutor& executor, std::string callJSON, bool isEndOfBatch) {
|
||||
// TODO: capture calljson
|
||||
std::terminate();
|
||||
}
|
||||
virtual MethodCallResult callSerializableNativeHook(
|
||||
JSExecutor& executor, unsigned int moduleId, unsigned int methodId, folly::dynamic&& args) {
|
||||
std::terminate();
|
||||
}
|
||||
};
|
||||
|
||||
struct FakeMessageQueue : MessageQueueThread {
|
||||
virtual void runOnQueue(std::function<void()>&& runnable) {
|
||||
// This is wrong, but oh well.
|
||||
runnable();
|
||||
}
|
||||
|
||||
virtual void runOnQueueSync(std::function<void()>&& runnable) {
|
||||
runnable();
|
||||
}
|
||||
|
||||
virtual void quitSynchronous() {
|
||||
std::terminate();
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<MethodCall> executeForMethodCalls(
|
||||
JSCExecutor& e,
|
||||
int moduleId,
|
||||
int methodId,
|
||||
folly::dynamic args = folly::dynamic::array()) {
|
||||
e.callFunction(folly::to<std::string>(moduleId), folly::to<std::string>(methodId), std::move(args));
|
||||
return parseMethodCalls(capturedMethodCalls);
|
||||
}
|
||||
|
||||
void loadApplicationScript(JSCExecutor& e, std::string jsText) {
|
||||
e.loadApplicationScript(std::unique_ptr<JSBigString>(new JSBigStdString(jsText)), "");
|
||||
}
|
||||
|
||||
void setGlobalVariable(JSCExecutor& e, std::string name, std::string jsonObject) {
|
||||
e.setGlobalVariable(name, std::unique_ptr<JSBigString>(new JSBigStdString(jsonObject)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
TEST(JSCExecutor, Initialize) {
|
||||
JSCExecutor executor(std::make_shared<NullDelegate>(), std::make_shared<FakeMessageQueue>(), "", folly::dynamic::object);
|
||||
}
|
||||
|
||||
TEST(JSCExecutor, Two) {
|
||||
JSCExecutor exec1(std::make_shared<NullDelegate>(), std::make_shared<FakeMessageQueue>(), "", folly::dynamic::object);
|
||||
JSCExecutor exec2(std::make_shared<NullDelegate>(), std::make_shared<FakeMessageQueue>(), "", folly::dynamic::object);
|
||||
}
|
||||
|
||||
TEST(JSCExecutor, CallFunction) {
|
||||
auto jsText = ""
|
||||
"var Bridge = {"
|
||||
" callFunctionReturnFlushedQueue: function (module, method, args) {"
|
||||
" return [[module + 1], [method + 1], [args]];"
|
||||
" },"
|
||||
"};"
|
||||
"function require() { return Bridge; }"
|
||||
"";
|
||||
JSCExecutor e(std::make_shared<NullDelegate>(), std::make_shared<FakeMessageQueue>(), "", folly::dynamic::object);
|
||||
loadApplicationScript(e, jsText);
|
||||
folly::dynamic args = folly::dynamic::array();
|
||||
args.push_back(true);
|
||||
args.push_back(0.4);
|
||||
args.push_back("hello, world");
|
||||
args.push_back(4.0);
|
||||
auto returnedCalls = executeForMethodCalls(e, 10, 9, args);
|
||||
ASSERT_EQ(1, returnedCalls.size());
|
||||
auto returnedCall = returnedCalls[0];
|
||||
EXPECT_EQ(11, returnedCall.moduleId);
|
||||
EXPECT_EQ(10, returnedCall.methodId);
|
||||
ASSERT_EQ(4, returnedCall.arguments.size());
|
||||
EXPECT_EQ(args[0], returnedCall.arguments[0]);
|
||||
EXPECT_EQ(args[1], returnedCall.arguments[1]);
|
||||
EXPECT_EQ(args[2], returnedCall.arguments[2]);
|
||||
EXPECT_EQ(folly::dynamic(4.0), returnedCall.arguments[3]);
|
||||
}
|
||||
|
||||
TEST(JSCExecutor, CallFunctionWithMap) {
|
||||
auto jsText = ""
|
||||
"var Bridge = {"
|
||||
" callFunctionReturnFlushedQueue: function (module, method, args) {"
|
||||
" var s = args[0].foo + args[0].bar + args[0].baz;"
|
||||
" return [[module], [method], [[s]]];"
|
||||
" },"
|
||||
"};"
|
||||
"function require() { return Bridge; }"
|
||||
"";
|
||||
JSCExecutor e(std::make_shared<NullDelegate>(), std::make_shared<FakeMessageQueue>(), "", folly::dynamic::object);
|
||||
loadApplicationScript(e, jsText);
|
||||
folly::dynamic args = folly::dynamic::array();
|
||||
folly::dynamic map = folly::dynamic::object
|
||||
("foo", folly::dynamic("hello"))
|
||||
("bar", folly::dynamic(4.0))
|
||||
("baz", folly::dynamic(true))
|
||||
;
|
||||
args.push_back(std::move(map));
|
||||
auto returnedCalls = executeForMethodCalls(e, 10, 9, args);
|
||||
ASSERT_EQ(1, returnedCalls.size());
|
||||
auto returnedCall = returnedCalls[0];
|
||||
ASSERT_EQ(1, returnedCall.arguments.size());
|
||||
EXPECT_EQ("hello4true", returnedCall.arguments[0].getString());
|
||||
}
|
||||
|
||||
TEST(JSCExecutor, CallFunctionReturningMap) {
|
||||
auto jsText = ""
|
||||
"var Bridge = {"
|
||||
" callFunctionReturnFlushedQueue: function (module, method, args) {"
|
||||
" var s = { foo: 4, bar: true };"
|
||||
" return [[module], [method], [[s]]];"
|
||||
" },"
|
||||
"};"
|
||||
"function require() { return Bridge; }"
|
||||
"";
|
||||
JSCExecutor e(std::make_shared<NullDelegate>(), std::make_shared<FakeMessageQueue>(), "", folly::dynamic::object);
|
||||
loadApplicationScript(e, jsText);
|
||||
auto returnedCalls = executeForMethodCalls(e, 10, 9);
|
||||
ASSERT_EQ(1, returnedCalls.size());
|
||||
auto returnedCall = returnedCalls[0];
|
||||
ASSERT_EQ(1, returnedCall.arguments.size());
|
||||
ASSERT_EQ(folly::dynamic::OBJECT, returnedCall.arguments[0].type());
|
||||
auto& returnedMap = returnedCall.arguments[0];
|
||||
auto foo = returnedMap.at("foo");
|
||||
EXPECT_EQ(folly::dynamic(4.0), foo);
|
||||
auto bar = returnedMap.at("bar");
|
||||
EXPECT_EQ(folly::dynamic(true), bar);
|
||||
}
|
||||
|
||||
TEST(JSCExecutor, CallFunctionWithArray) {
|
||||
auto jsText = ""
|
||||
"var Bridge = {"
|
||||
" callFunctionReturnFlushedQueue: function (module, method, args) {"
|
||||
" var s = args[0][0]+ args[0][1] + args[0][2] + args[0].length;"
|
||||
" return [[module], [method], [[s]]];"
|
||||
" },"
|
||||
"};"
|
||||
"function require() { return Bridge; }"
|
||||
"";
|
||||
JSCExecutor e(std::make_shared<NullDelegate>(), std::make_shared<FakeMessageQueue>(), "", folly::dynamic::object);
|
||||
loadApplicationScript(e, jsText);
|
||||
std::vector<folly::dynamic> args;
|
||||
std::vector<folly::dynamic> array {
|
||||
folly::dynamic("hello"),
|
||||
folly::dynamic(4.0),
|
||||
folly::dynamic(true),
|
||||
};
|
||||
args.push_back(std::move(array));
|
||||
auto returnedCalls = executeForMethodCalls(e, 10, 9, args);
|
||||
ASSERT_EQ(1, returnedCalls.size());
|
||||
auto returnedCall = returnedCalls[0];
|
||||
ASSERT_EQ(1, returnedCall.arguments.size());
|
||||
EXPECT_EQ("hello4true3", returnedCall.arguments[0].getString());
|
||||
}
|
||||
|
||||
TEST(JSCExecutor, CallFunctionReturningNumberArray) {
|
||||
auto jsText = ""
|
||||
"var Bridge = {"
|
||||
" callFunctionReturnFlushedQueue: function (module, method, args) {"
|
||||
" var s = [3, 1, 4];"
|
||||
" return [[module], [method], [[s]]];"
|
||||
" },"
|
||||
"};"
|
||||
"function require() { return Bridge; }"
|
||||
"";
|
||||
JSCExecutor e(std::make_shared<NullDelegate>(), std::make_shared<FakeMessageQueue>(), "", folly::dynamic::object);
|
||||
loadApplicationScript(e, jsText);
|
||||
auto returnedCalls = executeForMethodCalls(e, 10, 9);
|
||||
ASSERT_EQ(1, returnedCalls.size());
|
||||
auto returnedCall = returnedCalls[0];
|
||||
ASSERT_EQ(1, returnedCall.arguments.size());
|
||||
ASSERT_EQ(folly::dynamic::ARRAY, returnedCall.arguments[0].type());
|
||||
|
||||
auto& array = returnedCall.arguments[0];
|
||||
EXPECT_EQ(3, array.size());
|
||||
EXPECT_EQ(folly::dynamic(3.0), array[0]);
|
||||
EXPECT_EQ(folly::dynamic(4.0), array[2]);
|
||||
}
|
||||
|
||||
TEST(JSCExecutor, SetSimpleGlobalVariable) {
|
||||
auto jsText = ""
|
||||
"var Bridge = {"
|
||||
" callFunctionReturnFlushedQueue: function (module, method, args) {"
|
||||
" return [[module], [method], [[__foo]]];"
|
||||
" },"
|
||||
"};"
|
||||
"function require() { return Bridge; }"
|
||||
"";
|
||||
JSCExecutor e(std::make_shared<NullDelegate>(), std::make_shared<FakeMessageQueue>(), "", folly::dynamic::object);
|
||||
loadApplicationScript(e, jsText);
|
||||
setGlobalVariable(e, "__foo", "42");
|
||||
auto returnedCalls = executeForMethodCalls(e, 10, 9);
|
||||
ASSERT_EQ(1, returnedCalls.size());
|
||||
auto returnedCall = returnedCalls[0];
|
||||
ASSERT_EQ(1, returnedCall.arguments.size());
|
||||
ASSERT_EQ(42.0, returnedCall.arguments[0].getDouble());
|
||||
}
|
||||
|
||||
TEST(JSCExecutor, SetObjectGlobalVariable) {
|
||||
auto jsText = ""
|
||||
"var Bridge = {"
|
||||
" callFunctionReturnFlushedQueue: function (module, method, args) {"
|
||||
" return [[module], [method], [[__foo]]];"
|
||||
" },"
|
||||
"};"
|
||||
"function require() { return Bridge; }"
|
||||
"";
|
||||
JSCExecutor e(std::make_shared<NullDelegate>(), std::make_shared<FakeMessageQueue>(), "", folly::dynamic::object);
|
||||
loadApplicationScript(e, jsText);
|
||||
auto jsonObject = ""
|
||||
"{"
|
||||
" \"foo\": \"hello\","
|
||||
" \"bar\": 4,"
|
||||
" \"baz\": true"
|
||||
"}"
|
||||
"";
|
||||
setGlobalVariable(e, "__foo", jsonObject);
|
||||
auto returnedCalls = executeForMethodCalls(e, 10, 9);
|
||||
ASSERT_EQ(1, returnedCalls.size());
|
||||
auto returnedCall = returnedCalls[0];
|
||||
ASSERT_EQ(1, returnedCall.arguments.size());
|
||||
ASSERT_EQ(folly::dynamic::OBJECT, returnedCall.arguments[0].type());
|
||||
auto& returnedMap = returnedCall.arguments[0];
|
||||
auto foo = returnedMap.at("foo");
|
||||
EXPECT_EQ(folly::dynamic("hello"), foo);
|
||||
auto bar = returnedMap.at("bar");
|
||||
EXPECT_EQ(folly::dynamic(4.0), bar);
|
||||
auto baz = returnedMap.at("baz");
|
||||
EXPECT_EQ(folly::dynamic(true), baz);
|
||||
}
|
||||
|
||||
*/
|
@ -1,47 +0,0 @@
|
||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
||||
|
||||
// This source code is licensed under the MIT license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <cxxreact/JSCExecutor.h>
|
||||
|
||||
using namespace facebook;
|
||||
using namespace facebook::react;
|
||||
|
||||
/*
|
||||
static const char* expectedLogMessageSubstring = NULL;
|
||||
static bool hasSeenExpectedLogMessage = false;
|
||||
|
||||
static void mockLogHandler(int pri, const char *tag, const char *msg) {
|
||||
if (expectedLogMessageSubstring == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
hasSeenExpectedLogMessage |= (strstr(msg, expectedLogMessageSubstring) != NULL);
|
||||
}
|
||||
|
||||
class JSCLoggingTest : public testing::Test {
|
||||
protected:
|
||||
virtual void SetUp() override {
|
||||
setLogHandler(&mockLogHandler);
|
||||
}
|
||||
|
||||
virtual void TearDown() override {
|
||||
setLogHandler(NULL);
|
||||
expectedLogMessageSubstring = NULL;
|
||||
hasSeenExpectedLogMessage = false;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
TEST_F(JSCLoggingTest, LogException) {
|
||||
auto jsText = "throw new Error('I am a banana!');";
|
||||
expectedLogMessageSubstring = "I am a banana!";
|
||||
|
||||
JSCExecutor e;
|
||||
e.loadApplicationScript(jsText, "");
|
||||
|
||||
ASSERT_TRUE(hasSeenExpectedLogMessage);
|
||||
}
|
||||
*/
|
@ -1,108 +0,0 @@
|
||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
||||
|
||||
// This source code is licensed under the MIT license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
#include <string>
|
||||
#include <gtest/gtest.h>
|
||||
#include <folly/json.h>
|
||||
#include <jschelpers/Value.h>
|
||||
|
||||
#ifdef WITH_FBJSCEXTENSION
|
||||
#undef ASSERT
|
||||
#include <JavaScriptCore/config.h>
|
||||
#include "OpaqueJSString.h"
|
||||
#endif
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
using namespace facebook::react;
|
||||
|
||||
#ifdef ANDROID
|
||||
#include <android/looper.h>
|
||||
static void prepare() {
|
||||
ALooper_prepare(0);
|
||||
}
|
||||
#else
|
||||
static void prepare() {}
|
||||
#endif
|
||||
|
||||
TEST(Value, Undefined) {
|
||||
prepare();
|
||||
JSGlobalContextRef ctx = JSC_JSGlobalContextCreateInGroup(false, nullptr, nullptr);
|
||||
auto v = Value::makeUndefined(ctx);
|
||||
auto s = String::adopt(ctx, JSC_JSValueToStringCopy(ctx, v, nullptr));
|
||||
EXPECT_EQ("undefined", s.str());
|
||||
JSC_JSGlobalContextRelease(ctx);
|
||||
}
|
||||
|
||||
TEST(Value, FromJSON) {
|
||||
prepare();
|
||||
JSGlobalContextRef ctx = JSC_JSGlobalContextCreateInGroup(false, nullptr, nullptr);
|
||||
String s(ctx, "{\"a\": 4}");
|
||||
Value v(Value::fromJSON(s));
|
||||
EXPECT_TRUE(v.isObject());
|
||||
JSC_JSGlobalContextRelease(ctx);
|
||||
}
|
||||
|
||||
TEST(Value, ToJSONString) {
|
||||
prepare();
|
||||
JSGlobalContextRef ctx = JSC_JSGlobalContextCreateInGroup(false, nullptr, nullptr);
|
||||
String s(ctx, "{\"a\": 4}");
|
||||
Value v(Value::fromJSON(s));
|
||||
folly::dynamic dyn = folly::parseJson(v.toJSONString());
|
||||
ASSERT_NE(nullptr, dyn);
|
||||
EXPECT_TRUE(dyn.isObject());
|
||||
auto val = dyn.at("a");
|
||||
ASSERT_NE(nullptr, val);
|
||||
ASSERT_TRUE(val.isNumber());
|
||||
EXPECT_EQ(4, val.asInt());
|
||||
EXPECT_EQ(4.0f, val.asDouble());
|
||||
|
||||
JSC_JSGlobalContextRelease(ctx);
|
||||
}
|
||||
|
||||
#ifdef WITH_FBJSCEXTENSION
|
||||
// Just test that handling invalid data doesn't crash.
|
||||
TEST(Value, FromBadUtf8) {
|
||||
prepare();
|
||||
JSGlobalContextRef ctx = JSC_JSGlobalContextCreateInGroup(false, nullptr, nullptr);
|
||||
// 110xxxxx 10xxxxxx
|
||||
auto dyn = folly::dynamic("\xC0");
|
||||
Value::fromDynamic(ctx, dyn);
|
||||
dyn = folly::dynamic("\xC0\x00");
|
||||
Value::fromDynamic(ctx, dyn);
|
||||
// 1110xxxx 10xxxxxx 10xxxxxx
|
||||
dyn = "\xE0";
|
||||
Value::fromDynamic(ctx, dyn);
|
||||
Value(ctx, Value::fromDynamic(ctx, dyn)).toJSONString();
|
||||
dyn = "\xE0\x00";
|
||||
Value::fromDynamic(ctx, dyn);
|
||||
Value(ctx, Value::fromDynamic(ctx, dyn)).toJSONString();
|
||||
dyn = "\xE0\x00\x00";
|
||||
Value::fromDynamic(ctx, dyn);
|
||||
Value(ctx, Value::fromDynamic(ctx, dyn)).toJSONString();
|
||||
dyn = "\xE0\xA0\x00";
|
||||
Value::fromDynamic(ctx, dyn);
|
||||
// 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
|
||||
dyn = "\xF0";
|
||||
Value::fromDynamic(ctx, dyn);
|
||||
Value(ctx, Value::fromDynamic(ctx, dyn)).toJSONString();
|
||||
dyn = "\xF0\x00\x00\x00";
|
||||
Value::fromDynamic(ctx, dyn);
|
||||
dyn = "\xF0\x80\x80\x00";
|
||||
Value::fromDynamic(ctx, dyn);
|
||||
Value(ctx, Value::fromDynamic(ctx, dyn)).toJSONString();
|
||||
JSC_JSGlobalContextRelease(ctx);
|
||||
}
|
||||
|
||||
// Just test that handling invalid data doesn't crash.
|
||||
TEST(Value, BadUtf16) {
|
||||
prepare();
|
||||
JSGlobalContextRef ctx = JSC_JSGlobalContextCreateInGroup(false, nullptr, nullptr);
|
||||
UChar buf[] = { 0xDD00, 0xDD00, 0xDD00, 0x1111 };
|
||||
JSStringRef ref = OpaqueJSString::create(buf, 4).leakRef();
|
||||
Value v(ctx, ref);
|
||||
v.toJSONString(0);
|
||||
JSC_JSGlobalContextRelease(ctx);
|
||||
}
|
||||
#endif
|
Loading…
x
Reference in New Issue
Block a user