Move JSCHelpers.h and Value.h into separate package

Reviewed By: javache

Differential Revision: D3950748

fbshipit-source-id: 6315ea07f3217b485aeb4374b5f6e36333957848
This commit is contained in:
Alexander Blom
2016-11-01 11:38:43 -07:00
committed by Facebook Github Bot
parent 95cb4ea752
commit ddb8cb7cf0
34 changed files with 168 additions and 54 deletions

View File

@@ -9,7 +9,6 @@ LOCAL_SRC_FILES := \
Executor.cpp \
Instance.cpp \
JSCExecutor.cpp \
JSCHelpers.cpp \
JSCLegacyProfiler.cpp \
JSCLegacyTracing.cpp \
JSCMemory.cpp \
@@ -21,8 +20,7 @@ LOCAL_SRC_FILES := \
ModuleRegistry.cpp \
NativeToJsBridge.cpp \
Platform.cpp \
Value.cpp \
Unicode.cpp \
JSCUtils.cpp \
LOCAL_C_INCLUDES := $(LOCAL_PATH)/..
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_C_INCLUDES)
@@ -35,6 +33,7 @@ CXX11_FLAGS := -std=c++11
LOCAL_CFLAGS += $(CXX11_FLAGS)
LOCAL_EXPORT_CPPFLAGS := $(CXX11_FLAGS)
LOCAL_STATIC_LIBRARIES := jschelpers
LOCAL_SHARED_LIBRARIES := libfb libfolly_json libjsc libglog
include $(BUILD_STATIC_LIBRARY)
@@ -43,3 +42,4 @@ $(call import-module,fb)
$(call import-module,folly)
$(call import-module,jsc)
$(call import-module,glog)
$(call import-module,jschelpers)

View File

@@ -115,7 +115,6 @@ CXXREACT_PUBLIC_HEADERS = [
'ExecutorTokenFactory.h',
'Instance.h',
'JSCExecutor.h',
'JSCHelpers.h',
'JSCNativeModules.h',
'JSCWebWorker.h',
'JSModulesUnbundle.h',
@@ -124,11 +123,8 @@ CXXREACT_PUBLIC_HEADERS = [
'ModuleRegistry.h',
'NativeModule.h',
'NativeToJsBridge.h',
'noncopyable.h',
'Platform.h',
'SystraceSection.h',
'Unicode.h',
'Value.h',
]
react_library(
@@ -153,6 +149,7 @@ react_library(
deps = [
':module',
'//xplat/fbsystrace:fbsystrace',
react_native_xplat_target('jschelpers:jschelpers'),
react_native_xplat_target('microprofiler:microprofiler'),
],
visibility = [ 'PUBLIC' ],

View File

@@ -11,6 +11,8 @@
#include <folly/dynamic.h>
#include "JSModulesUnbundle.h"
namespace facebook {
namespace react {

View File

@@ -16,12 +16,14 @@
#include <fcntl.h>
#include <sys/time.h>
#include "JSCHelpers.h"
#include <jschelpers/JSCHelpers.h>
#include <jschelpers/Value.h>
#include "Platform.h"
#include "SystraceSection.h"
#include "Value.h"
#include "JSCNativeModules.h"
#include "JSCSamplingProfiler.h"
#include "JSCUtils.h"
#include "JSModulesUnbundle.h"
#include "ModuleRegistry.h"

View File

@@ -11,10 +11,11 @@
#include <folly/json.h>
#include <folly/Optional.h>
#include <jschelpers/JSCHelpers.h>
#include <jschelpers/Value.h>
#include "Executor.h"
#include "ExecutorToken.h"
#include "JSCHelpers.h"
#include "Value.h"
#include "JSCNativeModules.h"
namespace facebook {

View File

@@ -1,162 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include "JSCHelpers.h"
#include <JavaScriptCore/JSStringRef.h>
#include <folly/String.h>
#include <glog/logging.h>
#include "SystraceSection.h"
#include "Value.h"
namespace facebook {
namespace react {
void installGlobalFunction(
JSGlobalContextRef ctx,
const char* name,
JSObjectCallAsFunctionCallback callback) {
JSStringRef jsName = JSStringCreateWithUTF8CString(name);
JSObjectRef functionObj = JSObjectMakeFunctionWithCallback(
ctx, jsName, callback);
JSObjectRef globalObject = JSContextGetGlobalObject(ctx);
JSObjectSetProperty(ctx, globalObject, jsName, functionObj, 0, NULL);
JSStringRelease(jsName);
}
void installGlobalProxy(
JSGlobalContextRef ctx,
const char* name,
JSObjectGetPropertyCallback callback) {
JSClassDefinition proxyClassDefintion = kJSClassDefinitionEmpty;
proxyClassDefintion.className = "_FBProxyClass";
proxyClassDefintion.getProperty = callback;
JSClassRef proxyClass = JSClassCreate(&proxyClassDefintion);
JSObjectRef proxyObj = JSObjectMake(ctx, proxyClass, nullptr);
JSObjectRef globalObject = JSContextGetGlobalObject(ctx);
JSStringRef jsName = JSStringCreateWithUTF8CString(name);
JSObjectSetProperty(ctx, globalObject, jsName, proxyObj, 0, NULL);
JSStringRelease(jsName);
JSClassRelease(proxyClass);
}
JSValueRef makeJSCException(
JSContextRef ctx,
const char* exception_text) {
JSStringRef message = JSStringCreateWithUTF8CString(exception_text);
JSValueRef exceptionString = JSValueMakeString(ctx, message);
JSStringRelease(message);
return JSValueToObject(ctx, exceptionString, NULL);
}
String jsStringFromBigString(const JSBigString& bigstr) {
if (bigstr.isAscii()) {
return String::createExpectingAscii(bigstr.c_str(), bigstr.size());
} else {
return String(bigstr.c_str());
}
}
JSValueRef evaluateScript(JSContextRef context, JSStringRef script, JSStringRef source) {
SystraceSection s("evaluateScript");
JSValueRef exn, result;
result = JSEvaluateScript(context, script, NULL, source, 0, &exn);
if (result == nullptr) {
formatAndThrowJSException(context, exn, source);
}
return result;
}
#if WITH_FBJSCEXTENSIONS
JSValueRef evaluateSourceCode(JSContextRef context, JSSourceCodeRef source, JSStringRef sourceURL) {
JSValueRef exn, result;
result = JSEvaluateSourceCode(context, source, NULL, &exn);
if (result == nullptr) {
formatAndThrowJSException(context, exn, sourceURL);
}
return result;
}
#endif
void formatAndThrowJSException(JSContextRef context, JSValueRef exn, JSStringRef source) {
Value exception = Value(context, exn);
std::string exceptionText = exception.toString().str();
// The null/empty-ness of source tells us if the JS came from a
// file/resource, or was a constructed statement. The location
// info will include that source, if any.
std::string locationInfo = source != nullptr ? String::ref(source).str() : "";
Object exObject = exception.asObject();
auto line = exObject.getProperty("line");
if (line != nullptr && line.isNumber()) {
if (locationInfo.empty() && line.asInteger() != 1) {
// If there is a non-trivial line number, but there was no
// location info, we include a placeholder, and the line
// number.
locationInfo = folly::to<std::string>("<unknown file>:", line.asInteger());
} else if (!locationInfo.empty()) {
// If there is location info, we always include the line
// number, regardless of its value.
locationInfo += folly::to<std::string>(":", line.asInteger());
}
}
if (!locationInfo.empty()) {
exceptionText += " (" + locationInfo + ")";
}
LOG(ERROR) << "Got JS Exception: " << exceptionText;
Value jsStack = exObject.getProperty("stack");
if (jsStack.isNull() || !jsStack.isString()) {
throwJSExecutionException("%s", exceptionText.c_str());
} else {
LOG(ERROR) << "Got JS Stack: " << jsStack.toString().str();
throwJSExecutionExceptionWithStack(
exceptionText.c_str(), jsStack.toString().str().c_str());
}
}
JSValueRef makeJSError(JSContextRef ctx, const char *error) {
JSValueRef nestedException = nullptr;
JSValueRef args[] = { Value(ctx, String(error)) };
JSObjectRef errorObj = JSObjectMakeError(ctx, 1, args, &nestedException);
if (nestedException != nullptr) {
return std::move(args[0]);
}
return errorObj;
}
JSValueRef translatePendingCppExceptionToJSError(JSContextRef ctx, const char *exceptionLocation) {
std::ostringstream msg;
try {
throw;
} catch (const std::bad_alloc& ex) {
throw; // We probably shouldn't try to handle this in JS
} catch (const std::exception& ex) {
msg << "C++ Exception in '" << exceptionLocation << "': " << ex.what();
return makeJSError(ctx, msg.str().c_str());
} catch (const char* ex) {
msg << "C++ Exception (thrown as a char*) in '" << exceptionLocation << "': " << ex;
return makeJSError(ctx, msg.str().c_str());
} catch (...) {
msg << "Unknown C++ Exception in '" << exceptionLocation << "'";
return makeJSError(ctx, msg.str().c_str());
}
}
JSValueRef translatePendingCppExceptionToJSError(JSContextRef ctx, JSObjectRef jsFunctionCause) {
try {
auto functionName = Object(ctx, jsFunctionCause).getProperty("name").toString().str();
return translatePendingCppExceptionToJSError(ctx, functionName.c_str());
} catch (...) {
return makeJSError(ctx, "Failed to get function name while handling exception");
}
}
} }

View File

@@ -1,101 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include "Executor.h"
#include "Value.h"
#include <JavaScriptCore/JSContextRef.h>
#include <JavaScriptCore/JSObjectRef.h>
#include <JavaScriptCore/JSValueRef.h>
#include <stdexcept>
#include <algorithm>
namespace facebook {
namespace react {
inline void throwJSExecutionException(const char* msg) {
throw JSException(msg);
}
template <typename... Args>
inline void throwJSExecutionException(const char* fmt, Args... args) {
int msgSize = snprintf(nullptr, 0, fmt, args...);
msgSize = std::min(512, msgSize + 1);
char *msg = (char*) alloca(msgSize);
snprintf(msg, msgSize, fmt, args...);
throw JSException(msg);
}
template <typename... Args>
inline void throwJSExecutionExceptionWithStack(const char* msg, const char* stack) {
throw JSException(msg, stack);
}
void installGlobalFunction(
JSGlobalContextRef ctx,
const char* name,
JSObjectCallAsFunctionCallback callback);
void installGlobalProxy(
JSGlobalContextRef ctx,
const char* name,
JSObjectGetPropertyCallback callback);
JSValueRef makeJSCException(
JSContextRef ctx,
const char* exception_text);
String jsStringFromBigString(const JSBigString& bigstr);
JSValueRef evaluateScript(
JSContextRef ctx,
JSStringRef script,
JSStringRef sourceURL);
#if WITH_FBJSCEXTENSIONS
JSValueRef evaluateSourceCode(
JSContextRef ctx,
JSSourceCodeRef source,
JSStringRef sourceURL);
#endif
void formatAndThrowJSException(
JSContextRef ctx,
JSValueRef exn,
JSStringRef sourceURL);
JSValueRef makeJSError(JSContextRef ctx, const char *error);
JSValueRef translatePendingCppExceptionToJSError(JSContextRef ctx, const char *exceptionLocation);
JSValueRef translatePendingCppExceptionToJSError(JSContextRef ctx, JSObjectRef jsFunctionCause);
template<JSValueRef (method)(JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef *exception)>
inline JSObjectCallAsFunctionCallback exceptionWrapMethod() {
struct funcWrapper {
static JSValueRef call(
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef *exception) {
try {
return (*method)(ctx, function, thisObject, argumentCount, arguments, exception);
} catch (...) {
*exception = translatePendingCppExceptionToJSError(ctx, function);
return JSValueMakeUndefined(ctx);
}
}
};
return &funcWrapper::call;
}
} }

View File

@@ -7,9 +7,9 @@
#include <JavaScriptCore/JavaScript.h>
#include <JavaScriptCore/API/JSProfilerPrivate.h>
#include <jsc_legacy_profiler.h>
#include "JSCHelpers.h"
#include <jschelpers/JSCHelpers.h>
#include <jschelpers/Value.h>
#include "JSCLegacyProfiler.h"
#include "Value.h"
static JSValueRef nativeProfilerStart(
JSContextRef ctx,

View File

@@ -9,7 +9,8 @@
#include <fbsystrace.h>
#include "JSCHelpers.h"
#include <jschelpers/JSCHelpers.h>
#include "JSCTracing.h"
static const char *ENABLED_FBSYSTRACE_PROFILE_NAME = "__fbsystrace__";

View File

@@ -7,9 +7,8 @@
#include <stdio.h>
#include <string.h>
#include <JavaScriptCore/API/JSProfilerPrivate.h>
#include "JSCHelpers.h"
#include "Value.h"
#include <jschelpers/JSCHelpers.h>
#include <jschelpers/Value.h>
static JSValueRef nativeCaptureHeap(
JSContextRef ctx,
@@ -43,7 +42,7 @@ void addNativeMemoryHooks(JSGlobalContextRef ctx) {
#ifdef WITH_FB_MEMORY_PROFILING
installGlobalFunction(ctx, "nativeCaptureHeap", nativeCaptureHeap);
#endif // WITH_FB_MEMORY_PROFILING
}
} }

View File

@@ -7,7 +7,8 @@
#include <memory>
#include <string>
#include "Value.h"
#include <jschelpers/Value.h>
#include "ModuleRegistry.h"
namespace facebook {

View File

@@ -7,8 +7,8 @@
#include <JavaScriptCore/JSPerfStats.h>
#include <JavaScriptCore/JSValueRef.h>
#include "JSCHelpers.h"
#include "Value.h"
#include <jschelpers/JSCHelpers.h>
#include <jschelpers/Value.h>
static JSValueRef nativeGetHeapStats(
JSContextRef ctx,

View File

@@ -7,9 +7,8 @@
#include <stdio.h>
#include <string.h>
#include <JavaScriptCore/API/JSProfilerPrivate.h>
#include "JSCHelpers.h"
#include "Value.h"
#include <jschelpers/JSCHelpers.h>
#include <jschelpers/Value.h>
namespace facebook {
namespace react {

View File

@@ -9,7 +9,7 @@
#include <fbsystrace.h>
#include <sys/types.h>
#include <unistd.h>
#include "JSCHelpers.h"
#include <jschelpers/JSCHelpers.h>
using std::min;

View File

@@ -0,0 +1,17 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include "JSCUtils.h"
namespace facebook {
namespace react {
String jsStringFromBigString(const JSBigString& bigstr) {
if (bigstr.isAscii()) {
return String::createExpectingAscii(bigstr.c_str(), bigstr.size());
} else {
return String(bigstr.c_str());
}
}
}
}

View File

@@ -0,0 +1,15 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <jschelpers/Value.h>
#include "Executor.h"
namespace facebook {
namespace react {
String jsStringFromBigString(const JSBigString& bigstr);
}
}

View File

@@ -9,10 +9,11 @@
#include <folly/Memory.h>
#include "JSCHelpers.h"
#include <jschelpers/JSCHelpers.h>
#include <jschelpers/Value.h>
#include "MessageQueueThread.h"
#include "Platform.h"
#include "Value.h"
#include "JSCUtils.h"
#include <glog/logging.h>
@@ -122,7 +123,7 @@ JSValueRef JSCWebWorker::nativePostMessage(
}
webWorker->postMessageToOwner(msg);
return JSValueMakeUndefined(ctx);
}

View File

@@ -9,7 +9,7 @@
#include <JavaScriptCore/JSValueRef.h>
#include "Value.h"
#include <jschelpers/Value.h>
namespace facebook {
namespace react {
@@ -51,7 +51,7 @@ public:
~JSCWebWorker();
/**
* Post a message to be received by the worker on its thread. This must be called from
* Post a message to be received by the worker on its thread. This must be called from
* ownerMessageQueueThread_.
*/
void postMessage(JSValueRef msg);

View File

@@ -6,7 +6,7 @@
#include <string>
#include <stdexcept>
#include "noncopyable.h"
#include <jschelpers/noncopyable.h>
namespace facebook {
namespace react {

View File

@@ -9,6 +9,8 @@
#include <folly/dynamic.h>
#include <jschelpers/Value.h>
#include "Executor.h"
#include "ExecutorToken.h"
#include "JSCExecutor.h"
@@ -16,7 +18,6 @@
#include "MessageQueueThread.h"
#include "MethodCall.h"
#include "NativeModule.h"
#include "Value.h"
namespace folly {

View File

@@ -1,88 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include "Unicode.h"
namespace facebook {
namespace react {
namespace unicode {
namespace {
// TODO(12827176): Don't duplicate this code here and fbjni.
const uint16_t kUtf8OneByteBoundary = 0x80;
const uint16_t kUtf8TwoBytesBoundary = 0x800;
const uint16_t kUtf16HighSubLowBoundary = 0xD800;
const uint16_t kUtf16HighSubHighBoundary = 0xDC00;
const uint16_t kUtf16LowSubHighBoundary = 0xE000;
// Calculate how many bytes are needed to convert an UTF16 string into UTF8
// UTF16 string
size_t utf16toUTF8Length(const uint16_t* utf16String, size_t utf16StringLen) {
if (!utf16String || utf16StringLen == 0) {
return 0;
}
uint32_t utf8StringLen = 0;
auto utf16StringEnd = utf16String + utf16StringLen;
auto idx16 = utf16String;
while (idx16 < utf16StringEnd) {
auto ch = *idx16++;
if (ch < kUtf8OneByteBoundary) {
utf8StringLen++;
} else if (ch < kUtf8TwoBytesBoundary) {
utf8StringLen += 2;
} else if (
(ch >= kUtf16HighSubLowBoundary) && (ch < kUtf16HighSubHighBoundary) &&
(idx16 < utf16StringEnd) &&
(*idx16 >= kUtf16HighSubHighBoundary) && (*idx16 < kUtf16LowSubHighBoundary)) {
utf8StringLen += 4;
idx16++;
} else {
utf8StringLen += 3;
}
}
return utf8StringLen;
}
} // namespace
std::string utf16toUTF8(const uint16_t* utf16String, size_t utf16StringLen) noexcept {
if (!utf16String || utf16StringLen <= 0) {
return "";
}
std::string utf8String(utf16toUTF8Length(utf16String, utf16StringLen), '\0');
auto idx8 = utf8String.begin();
auto idx16 = utf16String;
auto utf16StringEnd = utf16String + utf16StringLen;
while (idx16 < utf16StringEnd) {
auto ch = *idx16++;
if (ch < kUtf8OneByteBoundary) {
*idx8++ = (ch & 0x7F);
} else if (ch < kUtf8TwoBytesBoundary) {
*idx8++ = 0b11000000 | (ch >> 6);
*idx8++ = 0b10000000 | (ch & 0x3F);
} else if (
(ch >= kUtf16HighSubLowBoundary) && (ch < kUtf16HighSubHighBoundary) &&
(idx16 < utf16StringEnd) &&
(*idx16 >= kUtf16HighSubHighBoundary) && (*idx16 < kUtf16LowSubHighBoundary)) {
auto ch2 = *idx16++;
uint8_t trunc_byte = (((ch >> 6) & 0x0F) + 1);
*idx8++ = 0b11110000 | (trunc_byte >> 2);
*idx8++ = 0b10000000 | ((trunc_byte & 0x03) << 4) | ((ch >> 2) & 0x0F);
*idx8++ = 0b10000000 | ((ch & 0x03) << 4) | ((ch2 >> 6) & 0x0F);
*idx8++ = 0b10000000 | (ch2 & 0x3F);
} else {
*idx8++ = 0b11100000 | (ch >> 12);
*idx8++ = 0b10000000 | ((ch >> 6) & 0x3F);
*idx8++ = 0b10000000 | (ch & 0x3F);
}
}
return utf8String;
}
} // namespace unicode
} // namespace react
} // namespace facebook

View File

@@ -1,14 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <string>
#include <cstdint>
namespace facebook {
namespace react {
namespace unicode {
std::string utf16toUTF8(const uint16_t* utf16, size_t length) noexcept;
}
}
}

View File

@@ -1,257 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include <folly/json.h>
#include "Value.h"
#include "JSCHelpers.h"
// See the comment under Value::fromDynamic()
#if !defined(__APPLE__) && defined(WITH_FB_JSC_TUNING)
#define USE_FAST_FOLLY_DYNAMIC_CONVERSION 1
#else
#define USE_FAST_FOLLY_DYNAMIC_CONVERSION 0
#endif
namespace facebook {
namespace react {
Value::Value(JSContextRef context, JSValueRef value) :
m_context(context),
m_value(value)
{
}
Value::Value(JSContextRef context, JSStringRef str) :
m_context(context),
m_value(JSValueMakeString(context, str))
{
}
Value::Value(Value&& other) :
m_context(other.m_context),
m_value(other.m_value)
{
other.m_value = nullptr;
}
JSContextRef Value::context() const {
return m_context;
}
std::string Value::toJSONString(unsigned indent) const {
JSValueRef exn;
auto stringToAdopt = JSValueCreateJSONString(m_context, m_value, indent, &exn);
if (stringToAdopt == nullptr) {
std::string exceptionText = Value(m_context, exn).toString().str();
throwJSExecutionException("Exception creating JSON string: %s", exceptionText.c_str());
}
return String::adopt(stringToAdopt).str();
}
/* static */
Value Value::fromJSON(JSContextRef ctx, const String& json) {
auto result = JSValueMakeFromJSONString(ctx, json);
if (!result) {
throwJSExecutionException("Failed to create String from JSON: %s", json.str().c_str());
}
return Value(ctx, result);
}
JSValueRef Value::fromDynamic(JSContextRef ctx, const folly::dynamic& value) {
// JavaScriptCore's iOS APIs have their own version of this direct conversion.
// In addition, using this requires exposing some of JSC's private APIs,
// so it's limited to non-apple platforms and to builds that use the custom JSC.
// Otherwise, we use the old way of converting through JSON.
#if USE_FAST_FOLLY_DYNAMIC_CONVERSION
// Defer GC during the creation of the JSValue, as we don't want
// intermediate objects to be collected.
// We could use JSValueProtect(), but it will make the process much slower.
JSDeferredGCRef deferGC = JSDeferGarbageCollection(ctx);
// Set a global lock for the whole process,
// instead of re-acquiring the lock for each operation.
JSLock(ctx);
JSValueRef jsVal = Value::fromDynamicInner(ctx, value);
JSUnlock(ctx);
JSResumeGarbageCollection(ctx, deferGC);
return jsVal;
#else
auto json = folly::toJson(value);
return fromJSON(ctx, String(json.c_str()));
#endif
}
JSValueRef Value::fromDynamicInner(JSContextRef ctx, const folly::dynamic& obj) {
switch (obj.type()) {
// For premitive types (and strings), just create and return an equivalent JSValue
case folly::dynamic::Type::NULLT:
return JSValueMakeNull(ctx);
case folly::dynamic::Type::BOOL:
return JSValueMakeBoolean(ctx, obj.getBool());
case folly::dynamic::Type::DOUBLE:
return JSValueMakeNumber(ctx, obj.getDouble());
case folly::dynamic::Type::INT64:
return JSValueMakeNumber(ctx, obj.asDouble());
case folly::dynamic::Type::STRING:
return JSValueMakeString(ctx, String(obj.getString().c_str()));
case folly::dynamic::Type::ARRAY: {
// Collect JSValue for every element in the array
JSValueRef vals[obj.size()];
for (size_t i = 0; i < obj.size(); ++i) {
vals[i] = fromDynamicInner(ctx, obj[i]);
}
// Create a JSArray with the values
JSValueRef arr = JSObjectMakeArray(ctx, obj.size(), vals, nullptr);
return arr;
}
case folly::dynamic::Type::OBJECT: {
// Create an empty object
JSObjectRef jsObj = JSObjectMake(ctx, nullptr, nullptr);
// Create a JSValue for each of the object's children and set them in the object
for (auto it = obj.items().begin(); it != obj.items().end(); ++it) {
JSObjectSetProperty(
ctx,
jsObj,
String(it->first.asString().c_str()),
fromDynamicInner(ctx, it->second),
kJSPropertyAttributeNone,
nullptr);
}
return jsObj;
}
default:
// Assert not reached
LOG(FATAL) << "Trying to convert a folly object of unsupported type.";
return JSValueMakeNull(ctx);
}
}
Object Value::asObject() {
JSValueRef exn;
JSObjectRef jsObj = JSValueToObject(context(), m_value, &exn);
if (!jsObj) {
std::string exceptionText = Value(m_context, exn).toString().str();
throwJSExecutionException("Failed to convert to object: %s", exceptionText.c_str());
}
Object ret = Object(context(), jsObj);
m_value = nullptr;
return ret;
}
Object::operator Value() const {
return Value(m_context, m_obj);
}
Value Object::callAsFunction(std::initializer_list<JSValueRef> args) const {
return callAsFunction(nullptr, args.size(), args.begin());
}
Value Object::callAsFunction(const Object& thisObj, std::initializer_list<JSValueRef> args) const {
return callAsFunction((JSObjectRef) thisObj, args.size(), args.begin());
}
Value Object::callAsFunction(int nArgs, const JSValueRef args[]) const {
return callAsFunction(nullptr, nArgs, args);
}
Value Object::callAsFunction(const Object& thisObj, int nArgs, const JSValueRef args[]) const {
return callAsFunction((JSObjectRef) thisObj, nArgs, args);
}
Value Object::callAsFunction(JSObjectRef thisObj, int nArgs, const JSValueRef args[]) const {
JSValueRef exn;
JSValueRef result = JSObjectCallAsFunction(m_context, m_obj, thisObj, nArgs, args, &exn);
if (!result) {
std::string exceptionText = Value(m_context, exn).toString().str();
throwJSExecutionException("Exception calling object as function: %s", exceptionText.c_str());
}
return Value(m_context, result);
}
Object Object::callAsConstructor(std::initializer_list<JSValueRef> args) const {
JSValueRef exn;
JSObjectRef result = JSObjectCallAsConstructor(m_context, m_obj, args.size(), args.begin(), &exn);
if (!result) {
std::string exceptionText = Value(m_context, exn).toString().str();
throwJSExecutionException("Exception calling object as constructor: %s", exceptionText.c_str());
}
return Object(m_context, result);
}
Value Object::getProperty(const String& propName) const {
JSValueRef exn;
JSValueRef property = JSObjectGetProperty(m_context, m_obj, propName, &exn);
if (!property) {
std::string exceptionText = Value(m_context, exn).toString().str();
throwJSExecutionException("Failed to get property: %s", exceptionText.c_str());
}
return Value(m_context, property);
}
Value Object::getPropertyAtIndex(unsigned index) const {
JSValueRef exn;
JSValueRef property = JSObjectGetPropertyAtIndex(m_context, m_obj, index, &exn);
if (!property) {
std::string exceptionText = Value(m_context, exn).toString().str();
throwJSExecutionException("Failed to get property at index %u: %s", index, exceptionText.c_str());
}
return Value(m_context, property);
}
Value Object::getProperty(const char *propName) const {
return getProperty(String(propName));
}
void Object::setProperty(const String& propName, const Value& value) const {
JSValueRef exn = NULL;
JSObjectSetProperty(m_context, m_obj, propName, value, kJSPropertyAttributeNone, &exn);
if (exn) {
std::string exceptionText = Value(m_context, exn).toString().str();
throwJSExecutionException("Failed to set property: %s", exceptionText.c_str());
}
}
void Object::setProperty(const char *propName, const Value& value) const {
setProperty(String(propName), value);
}
std::vector<String> Object::getPropertyNames() const {
auto namesRef = JSObjectCopyPropertyNames(m_context, m_obj);
size_t count = JSPropertyNameArrayGetCount(namesRef);
std::vector<String> names;
names.reserve(count);
for (size_t i = 0; i < count; i++) {
names.emplace_back(String::ref(JSPropertyNameArrayGetNameAtIndex(namesRef, i)));
}
JSPropertyNameArrayRelease(namesRef);
return names;
}
std::unordered_map<std::string, std::string> Object::toJSONMap() const {
std::unordered_map<std::string, std::string> map;
auto namesRef = JSObjectCopyPropertyNames(m_context, m_obj);
size_t count = JSPropertyNameArrayGetCount(namesRef);
for (size_t i = 0; i < count; i++) {
auto key = String::ref(JSPropertyNameArrayGetNameAtIndex(namesRef, i));
map.emplace(key.str(), getProperty(key).toJSONString());
}
JSPropertyNameArrayRelease(namesRef);
return map;
}
/* static */
Object Object::create(JSContextRef ctx) {
JSObjectRef newObj = JSObjectMake(
ctx,
NULL, // create instance of default object class
NULL); // no private data
return Object(ctx, newObj);
}
} }

View File

@@ -1,308 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <memory>
#include <sstream>
#include <unordered_map>
#include <vector>
#include <JavaScriptCore/JSContextRef.h>
#include <JavaScriptCore/JSObjectRef.h>
#include <JavaScriptCore/JSStringRef.h>
#include <JavaScriptCore/JSValueRef.h>
#include <folly/dynamic.h>
#include "noncopyable.h"
#include "Unicode.h"
#if WITH_FBJSCEXTENSIONS
#include <jsc_stringref.h>
#endif
namespace facebook {
namespace react {
class Value;
class Context;
class JSException : public std::runtime_error {
public:
explicit JSException(const char* msg)
: std::runtime_error(msg)
, stack_("") {}
JSException(const char* msg, const char* stack)
: std::runtime_error(msg)
, stack_(stack) {}
const std::string& getStack() const {
return stack_;
}
private:
std::string stack_;
};
class String : public noncopyable {
public:
explicit String(const char* utf8) :
m_string(JSStringCreateWithUTF8CString(utf8))
{}
String(String&& other) :
m_string(other.m_string)
{
other.m_string = nullptr;
}
String(const String& other) :
m_string(other.m_string)
{
if (m_string) {
JSStringRetain(m_string);
}
}
~String() {
if (m_string) {
JSStringRelease(m_string);
}
}
operator JSStringRef() const {
return m_string;
}
// Length in characters
size_t length() const {
return JSStringGetLength(m_string);
}
// Length in bytes of a null-terminated utf8 encoded value
size_t utf8Size() const {
return JSStringGetMaximumUTF8CStringSize(m_string);
}
/*
* JavaScriptCore is built with strict utf16 -> utf8 conversion.
* This means if JSC's built-in conversion function encounters a JavaScript
* string which contains half of a 32-bit UTF-16 symbol, it produces an error
* rather than returning a string.
*
* Instead of relying on this, we use our own utf16 -> utf8 conversion function
* which is more lenient and always returns a string. When an invalid UTF-16
* string is provided, it'll likely manifest as a rendering glitch in the app for
* the invalid symbol.
*
* For details on JavaScript's unicode support see:
* https://mathiasbynens.be/notes/javascript-unicode
*/
std::string str() const {
const JSChar* utf16 = JSStringGetCharactersPtr(m_string);
int stringLength = JSStringGetLength(m_string);
return unicode::utf16toUTF8(utf16, stringLength);
}
// Assumes that utf8 is null terminated
bool equals(const char* utf8) {
return JSStringIsEqualToUTF8CString(m_string, utf8);
}
// This assumes ascii is nul-terminated.
static String createExpectingAscii(const char* ascii, size_t len) {
#if WITH_FBJSCEXTENSIONS
return String(JSStringCreateWithUTF8CStringExpectAscii(ascii, len), true);
#else
return String(JSStringCreateWithUTF8CString(ascii), true);
#endif
}
static String createExpectingAscii(std::string const &ascii) {
return createExpectingAscii(ascii.c_str(), ascii.size());
}
static String ref(JSStringRef string) {
return String(string, false);
}
static String adopt(JSStringRef string) {
return String(string, true);
}
private:
explicit String(JSStringRef string, bool adopt) :
m_string(string)
{
if (!adopt && string) {
JSStringRetain(string);
}
}
JSStringRef m_string;
};
class Object : public noncopyable {
public:
Object(JSContextRef context, JSObjectRef obj) :
m_context(context),
m_obj(obj)
{}
Object(Object&& other) :
m_context(other.m_context),
m_obj(other.m_obj),
m_isProtected(other.m_isProtected) {
other.m_obj = nullptr;
other.m_isProtected = false;
}
~Object() {
if (m_isProtected && m_obj) {
JSValueUnprotect(m_context, m_obj);
}
}
Object& operator=(Object&& other) {
std::swap(m_context, other.m_context);
std::swap(m_obj, other.m_obj);
std::swap(m_isProtected, other.m_isProtected);
return *this;
}
operator JSObjectRef() const {
return m_obj;
}
operator Value() const;
bool isFunction() const {
return JSObjectIsFunction(m_context, m_obj);
}
Value callAsFunction(std::initializer_list<JSValueRef> args) const;
Value callAsFunction(const Object& thisObj, std::initializer_list<JSValueRef> args) const;
Value callAsFunction(int nArgs, const JSValueRef args[]) const;
Value callAsFunction(const Object& thisObj, int nArgs, const JSValueRef args[]) const;
Object callAsConstructor(std::initializer_list<JSValueRef> args) const;
Value getProperty(const String& propName) const;
Value getProperty(const char *propName) const;
Value getPropertyAtIndex(unsigned index) const;
void setProperty(const String& propName, const Value& value) const;
void setProperty(const char *propName, const Value& value) const;
std::vector<String> getPropertyNames() const;
std::unordered_map<std::string, std::string> toJSONMap() const;
void makeProtected() {
if (!m_isProtected && m_obj) {
JSValueProtect(m_context, m_obj);
m_isProtected = true;
}
}
template<typename ReturnType>
ReturnType* getPrivate() const {
return static_cast<ReturnType*>(JSObjectGetPrivate(m_obj));
}
JSContextRef context() const {
return m_context;
}
static Object getGlobalObject(JSContextRef ctx) {
auto globalObj = JSContextGetGlobalObject(ctx);
return Object(ctx, globalObj);
}
/**
* Creates an instance of the default object class.
*/
static Object create(JSContextRef ctx);
private:
JSContextRef m_context;
JSObjectRef m_obj;
bool m_isProtected = false;
Value callAsFunction(JSObjectRef thisObj, int nArgs, const JSValueRef args[]) const;
};
class Value : public noncopyable {
public:
Value(JSContextRef context, JSValueRef value);
Value(JSContextRef context, JSStringRef value);
Value(Value&&);
operator JSValueRef() const {
return m_value;
}
JSType getType() const {
return JSValueGetType(m_context, m_value);
}
bool isBoolean() const {
return JSValueIsBoolean(context(), m_value);
}
bool asBoolean() const {
return JSValueToBoolean(context(), m_value);
}
bool isNumber() const {
return JSValueIsNumber(context(), m_value);
}
bool isNull() const {
return JSValueIsNull(context(), m_value);
}
bool isUndefined() const {
return JSValueIsUndefined(context(), m_value);
}
double asNumber() const {
if (isNumber()) {
return JSValueToNumber(context(), m_value, nullptr);
} else {
return 0.0f;
}
}
int32_t asInteger() const {
return static_cast<int32_t>(asNumber());
}
uint32_t asUnsignedInteger() const {
return static_cast<uint32_t>(asNumber());
}
bool isObject() const {
return JSValueIsObject(context(), m_value);
}
Object asObject();
bool isString() const {
return JSValueIsString(context(), m_value);
}
String toString() noexcept {
return String::adopt(JSValueToStringCopy(context(), m_value, nullptr));
}
std::string toJSONString(unsigned indent = 0) const;
static Value fromJSON(JSContextRef ctx, const String& json);
static JSValueRef fromDynamic(JSContextRef ctx, const folly::dynamic& value);
JSContextRef context() const;
protected:
JSContextRef m_context;
JSValueRef m_value;
static JSValueRef fromDynamicInner(JSContextRef ctx, const folly::dynamic& obj);
};
} }

View File

@@ -1,12 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
namespace facebook {
namespace react {
struct noncopyable {
noncopyable(const noncopyable&) = delete;
noncopyable& operator=(const noncopyable&) = delete;
protected:
noncopyable() = default;
};
}}

View File

@@ -37,6 +37,7 @@ if THIS_IS_FBOBJC:
deps = [
'//xplat/third-party/gmock:gtest',
react_native_xplat_target('cxxreact:bridge'),
react_native_xplat_target('jschelpers:jschelpers'),
],
visibility = [react_native_xplat_target('cxxreact/...')],
)

View File

@@ -2,7 +2,7 @@
#include <string>
#include <gtest/gtest.h>
#include <folly/json.h>
#include <cxxreact/Value.h>
#include <jschelpers/Value.h>
#ifdef WITH_FBJSCEXTENSION
#undef ASSERT
@@ -105,4 +105,3 @@ TEST(Value, BadUtf16) {
JSGlobalContextRelease(ctx);
}
#endif