Cleanup and document Value wrapper (retry)

Reviewed By: mhorowitz

Differential Revision: D5120975

fbshipit-source-id: 6e9c80a57fdcf7f3dad21d5521fb928b52c924c7
This commit is contained in:
Pieter De Baets 2017-05-26 03:43:20 -07:00 committed by Facebook Github Bot
parent 11424a8bc6
commit 03e1f40c1e
7 changed files with 158 additions and 146 deletions

View File

@ -14,6 +14,7 @@
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
#import <React/RCTBridge+Private.h> #import <React/RCTBridge+Private.h>
#import <React/RCTCxxUtils.h>
#import <React/RCTLog.h> #import <React/RCTLog.h>
#import <cxxreact/Platform.h> #import <cxxreact/Platform.h>
#import <jschelpers/Value.h> #import <jschelpers/Value.h>
@ -30,8 +31,9 @@ JSValueRef nativeLoggingHook(
level = MAX(level, (RCTLogLevel)Value(ctx, arguments[1]).asNumber()); level = MAX(level, (RCTLogLevel)Value(ctx, arguments[1]).asNumber());
} }
if (argumentCount > 0) { if (argumentCount > 0) {
String message = Value(ctx, arguments[0]).toString(); JSContext *contextObj = contextForGlobalContextRef(JSC_JSContextGetGlobalContext(ctx));
_RCTLogJavaScriptInternal(level, @(message.str().c_str())); JSValue *msg = [JSC_JSValue(ctx) valueWithJSValueRef:arguments[0] inContext:contextObj];
_RCTLogJavaScriptInternal(level, [msg toString]);
} }
return Value::makeUndefined(ctx); return Value::makeUndefined(ctx);
} }
@ -45,7 +47,7 @@ JSValueRef nativePerformanceNow(
} }
void RCTPrepareJSCExecutor() { void RCTPrepareJSCExecutor() {
ReactMarker::logTaggedMarker = [](const ReactMarker::ReactMarkerId, const char* tag) {}; ReactMarker::logTaggedMarker = [](const ReactMarker::ReactMarkerId, const char *tag) {};
PerfLogging::installNativeHooks = RCTFBQuickPerformanceLoggerConfigureHooks; PerfLogging::installNativeHooks = RCTFBQuickPerformanceLoggerConfigureHooks;
JSNativeHooks::loggingHook = nativeLoggingHook; JSNativeHooks::loggingHook = nativeLoggingHook;
JSNativeHooks::nowHook = nativePerformanceNow; JSNativeHooks::nowHook = nativePerformanceNow;

View File

@ -346,7 +346,7 @@ void JSCExecutor::loadApplicationScript(std::unique_ptr<const JSBigString> scrip
JSValueRef jsError; JSValueRef jsError;
JSValueRef result = JSC_JSEvaluateBytecodeBundle(m_context, NULL, sourceFD, jsSourceURL, &jsError); JSValueRef result = JSC_JSEvaluateBytecodeBundle(m_context, NULL, sourceFD, jsSourceURL, &jsError);
if (result == nullptr) { if (result == nullptr) {
formatAndThrowJSException(m_context, jsError, jsSourceURL); throw JSException(m_context, jsError, jsSourceURL);
} }
} else } else
#endif #endif
@ -390,7 +390,7 @@ void JSCExecutor::bindBridge() throw(JSException) {
batchedBridgeValue = requireBatchedBridge.asObject().callAsFunction({}); batchedBridgeValue = requireBatchedBridge.asObject().callAsFunction({});
} }
if (batchedBridgeValue.isUndefined()) { if (batchedBridgeValue.isUndefined()) {
throwJSExecutionException("Could not get BatchedBridge, make sure your bundle is packaged correctly"); throw JSException("Could not get BatchedBridge, make sure your bundle is packaged correctly");
} }
} }

View File

@ -15,6 +15,10 @@
#include <jschelpers/JavaScriptCore.h> #include <jschelpers/JavaScriptCore.h>
#include <jschelpers/Value.h> #include <jschelpers/Value.h>
#ifndef RN_EXPORT
#define RN_EXPORT __attribute__((visibility("default")))
#endif
namespace facebook { namespace facebook {
namespace react { namespace react {

View File

@ -68,6 +68,51 @@ JSObjectRef makeFunction(
} }
void JSException::buildMessage(JSContextRef ctx, JSValueRef exn, JSStringRef sourceURL, const char* errorMsg) {
std::ostringstream msgBuilder;
if (errorMsg && strlen(errorMsg) > 0) {
msgBuilder << errorMsg << ": ";
}
Value exnValue = Value(ctx, exn);
msgBuilder << exnValue.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 = sourceURL != nullptr ? String::ref(ctx, sourceURL).str() : "";
Object exnObject = exnValue.asObject();
auto line = exnObject.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()) {
msgBuilder << " (" << locationInfo << ")";
}
auto exceptionText = msgBuilder.str();
LOG(ERROR) << "Got JS Exception: " << exceptionText;
msg_ = std::move(exceptionText);
Value jsStack = exnObject.getProperty("stack");
if (jsStack.isString()) {
auto stackText = jsStack.toString().str();
LOG(ERROR) << "Got JS Stack: " << stackText;
stack_ = std::move(stackText);
}
}
JSObjectRef makeFunction( JSObjectRef makeFunction(
JSContextRef ctx, JSContextRef ctx,
const char* name, const char* name,
@ -122,11 +167,11 @@ void removeGlobal(JSGlobalContextRef ctx, const char* name) {
Object::getGlobalObject(ctx).setProperty(name, Value::makeUndefined(ctx)); Object::getGlobalObject(ctx).setProperty(name, Value::makeUndefined(ctx));
} }
JSValueRef evaluateScript(JSContextRef context, JSStringRef script, JSStringRef source) { JSValueRef evaluateScript(JSContextRef context, JSStringRef script, JSStringRef sourceURL) {
JSValueRef exn, result; JSValueRef exn, result;
result = JSC_JSEvaluateScript(context, script, NULL, source, 0, &exn); result = JSC_JSEvaluateScript(context, script, NULL, sourceURL, 0, &exn);
if (result == nullptr) { if (result == nullptr) {
formatAndThrowJSException(context, exn, source); throw JSException(context, exn, sourceURL);
} }
return result; return result;
} }
@ -136,51 +181,12 @@ JSValueRef evaluateSourceCode(JSContextRef context, JSSourceCodeRef source, JSSt
JSValueRef exn, result; JSValueRef exn, result;
result = JSEvaluateSourceCode(context, source, NULL, &exn); result = JSEvaluateSourceCode(context, source, NULL, &exn);
if (result == nullptr) { if (result == nullptr) {
formatAndThrowJSException(context, exn, sourceURL); throw JSException(context, exn, sourceURL);
} }
return result; return result;
} }
#endif #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(context, 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 translatePendingCppExceptionToJSError(JSContextRef ctx, const char *exceptionLocation) { JSValueRef translatePendingCppExceptionToJSError(JSContextRef ctx, const char *exceptionLocation) {
std::ostringstream msg; std::ostringstream msg;
try { try {

View File

@ -2,33 +2,53 @@
#pragma once #pragma once
#include <jschelpers/Value.h>
#include <jschelpers/JavaScriptCore.h>
#include <stdexcept>
#include <algorithm> #include <algorithm>
#include <functional> #include <functional>
#include <stdexcept>
#include <folly/String.h>
#include <jschelpers/JavaScriptCore.h>
#include <jschelpers/Value.h>
namespace facebook { namespace facebook {
namespace react { namespace react {
inline void throwJSExecutionException(const char* msg) { class JSException : public std::exception {
throw JSException(msg); public:
} explicit JSException(const char* msg)
: msg_(msg) {}
template <typename... Args> template <typename... Args>
inline void throwJSExecutionException(const char* fmt, Args... args) { explicit JSException(const char* fmt, Args... args)
int msgSize = snprintf(nullptr, 0, fmt, args...); : msg_(folly::stringPrintf(fmt, args...)) {}
msgSize = std::min(512, msgSize + 1);
char *msg = (char*) alloca(msgSize);
snprintf(msg, msgSize, fmt, args...);
throw JSException(msg);
}
template <typename... Args> explicit JSException(JSContextRef ctx, JSValueRef exn, const char* msg) {
inline void throwJSExecutionExceptionWithStack(const char* msg, const char* stack) { buildMessage(ctx, exn, nullptr, msg);
throw JSException(msg, stack); }
}
explicit JSException(JSContextRef ctx, JSValueRef exn, JSStringRef sourceURL) {
buildMessage(ctx, exn, sourceURL, nullptr);
}
template <typename... Args>
explicit JSException(JSContextRef ctx, JSValueRef exn, JSStringRef sourceURL, const char* fmt, Args... args) {
buildMessage(ctx, exn, sourceURL, folly::stringPrintf(fmt, args...).c_str());
}
const std::string& getStack() const {
return stack_;
}
virtual const char* what() const noexcept override {
return msg_.c_str();
}
private:
std::string msg_;
std::string stack_;
void buildMessage(JSContextRef ctx, JSValueRef exn, JSStringRef sourceURL, const char* errorMsg);
};
using JSFunction = std::function<JSValueRef(JSContextRef, JSObjectRef, size_t, const JSValueRef[])>; using JSFunction = std::function<JSValueRef(JSContextRef, JSObjectRef, size_t, const JSValueRef[])>;
@ -71,11 +91,6 @@ JSValueRef evaluateSourceCode(
JSStringRef sourceURL); JSStringRef sourceURL);
#endif #endif
void formatAndThrowJSException(
JSContextRef ctx,
JSValueRef exn,
JSStringRef sourceURL);
JSValueRef translatePendingCppExceptionToJSError(JSContextRef ctx, const char *exceptionLocation); JSValueRef translatePendingCppExceptionToJSError(JSContextRef ctx, const char *exceptionLocation);
JSValueRef translatePendingCppExceptionToJSError(JSContextRef ctx, JSObjectRef jsFunctionCause); JSValueRef translatePendingCppExceptionToJSError(JSContextRef ctx, JSObjectRef jsFunctionCause);

View File

@ -17,24 +17,11 @@
namespace facebook { namespace facebook {
namespace react { namespace react {
Value::Value(JSContextRef context, JSValueRef value) : Value::Value(JSContextRef context, JSValueRef value)
m_context(context), : m_context(context), m_value(value) {}
m_value(value)
{
}
Value::Value(JSContextRef context, JSStringRef str) : Value::Value(JSContextRef context, JSStringRef str)
m_context(context), : m_context(context), m_value(JSC_JSValueMakeString(context, str)) {}
m_value(JSC_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 { JSContextRef Value::context() const {
return m_context; return m_context;
@ -43,9 +30,8 @@ JSContextRef Value::context() const {
std::string Value::toJSONString(unsigned indent) const { std::string Value::toJSONString(unsigned indent) const {
JSValueRef exn; JSValueRef exn;
auto stringToAdopt = JSC_JSValueCreateJSONString(m_context, m_value, indent, &exn); auto stringToAdopt = JSC_JSValueCreateJSONString(m_context, m_value, indent, &exn);
if (stringToAdopt == nullptr) { if (!stringToAdopt) {
std::string exceptionText = Value(m_context, exn).toString().str(); throw JSException(m_context, exn, "Exception creating JSON string");
throwJSExecutionException("Exception creating JSON string: %s", exceptionText.c_str());
} }
return String::adopt(m_context, stringToAdopt).str(); return String::adopt(m_context, stringToAdopt).str();
} }
@ -54,7 +40,7 @@ std::string Value::toJSONString(unsigned indent) const {
Value Value::fromJSON(JSContextRef ctx, const String& json) { Value Value::fromJSON(JSContextRef ctx, const String& json) {
auto result = JSC_JSValueMakeFromJSONString(ctx, json); auto result = JSC_JSValueMakeFromJSONString(ctx, json);
if (!result) { if (!result) {
throwJSExecutionException("Failed to create String from JSON: %s", json.str().c_str()); throw JSException("Failed to create Value from JSON: %s", json.str().c_str());
} }
return Value(ctx, result); return Value(ctx, result);
} }
@ -133,24 +119,31 @@ JSValueRef Value::fromDynamicInner(JSContextRef ctx, const folly::dynamic& obj)
} }
} }
Object Value::asObject() { Object Value::asObject() const {
JSValueRef exn; JSValueRef exn;
JSObjectRef jsObj = JSC_JSValueToObject(context(), m_value, &exn); JSObjectRef jsObj = JSC_JSValueToObject(context(), m_value, &exn);
if (!jsObj) { if (!jsObj) {
std::string exceptionText = Value(m_context, exn).toString().str(); throw JSException(m_context, exn, "Failed to convert to object");
throwJSExecutionException("Failed to convert to object: %s", exceptionText.c_str());
} }
return Object(context(), jsObj); return Object(context(), jsObj);
} }
String Value::toString() const {
JSValueRef exn;
JSStringRef jsStr = JSC_JSValueToStringCopy(context(), m_value, &exn);
if (!jsStr) {
throw JSException(m_context, exn, "Failed to convert to string");
}
return String::adopt(context(), jsStr);
}
Value Value::makeError(JSContextRef ctx, const char *error) Value Value::makeError(JSContextRef ctx, const char *error)
{ {
JSValueRef exn; JSValueRef exn;
JSValueRef args[] = { Value(ctx, String(ctx, error)) }; JSValueRef args[] = { Value(ctx, String(ctx, error)) };
JSObjectRef errorObj = JSC_JSObjectMakeError(ctx, 1, args, &exn); JSObjectRef errorObj = JSC_JSObjectMakeError(ctx, 1, args, &exn);
if (!errorObj) { if (!errorObj) {
std::string exceptionText = Value(ctx, exn).toString().str(); throw JSException(ctx, exn, "Exception making error");
throwJSExecutionException("%s", exceptionText.c_str());
} }
return Value(ctx, errorObj); return Value(ctx, errorObj);
} }
@ -179,8 +172,7 @@ Value Object::callAsFunction(JSObjectRef thisObj, int nArgs, const JSValueRef ar
JSValueRef exn; JSValueRef exn;
JSValueRef result = JSC_JSObjectCallAsFunction(m_context, m_obj, thisObj, nArgs, args, &exn); JSValueRef result = JSC_JSObjectCallAsFunction(m_context, m_obj, thisObj, nArgs, args, &exn);
if (!result) { if (!result) {
std::string exceptionText = Value(m_context, exn).toString().str(); throw JSException(m_context, exn, "Exception calling object as function");
throwJSExecutionException("Exception calling object as function: %s", exceptionText.c_str());
} }
return Value(m_context, result); return Value(m_context, result);
} }
@ -189,8 +181,7 @@ Object Object::callAsConstructor(std::initializer_list<JSValueRef> args) const {
JSValueRef exn; JSValueRef exn;
JSObjectRef result = JSC_JSObjectCallAsConstructor(m_context, m_obj, args.size(), args.begin(), &exn); JSObjectRef result = JSC_JSObjectCallAsConstructor(m_context, m_obj, args.size(), args.begin(), &exn);
if (!result) { if (!result) {
std::string exceptionText = Value(m_context, exn).toString().str(); throw JSException(m_context, exn, "Exception calling object as constructor");
throwJSExecutionException("Exception calling object as constructor: %s", exceptionText.c_str());
} }
return Object(m_context, result); return Object(m_context, result);
} }
@ -199,8 +190,7 @@ Value Object::getProperty(const String& propName) const {
JSValueRef exn; JSValueRef exn;
JSValueRef property = JSC_JSObjectGetProperty(m_context, m_obj, propName, &exn); JSValueRef property = JSC_JSObjectGetProperty(m_context, m_obj, propName, &exn);
if (!property) { if (!property) {
std::string exceptionText = Value(m_context, exn).toString().str(); throw JSException(m_context, exn, nullptr, "Failed to get property '%s'", propName.str().c_str());
throwJSExecutionException("Failed to get property: %s", exceptionText.c_str());
} }
return Value(m_context, property); return Value(m_context, property);
} }
@ -209,8 +199,7 @@ Value Object::getPropertyAtIndex(unsigned int index) const {
JSValueRef exn; JSValueRef exn;
JSValueRef property = JSC_JSObjectGetPropertyAtIndex(m_context, m_obj, index, &exn); JSValueRef property = JSC_JSObjectGetPropertyAtIndex(m_context, m_obj, index, &exn);
if (!property) { if (!property) {
std::string exceptionText = Value(m_context, exn).toString().str(); throw JSException(m_context, exn, nullptr, "Failed to get property at index %d", index);
throwJSExecutionException("Failed to get property at index %u: %s", index, exceptionText.c_str());
} }
return Value(m_context, property); return Value(m_context, property);
} }
@ -223,8 +212,7 @@ void Object::setProperty(const String& propName, const Value& value) {
JSValueRef exn = nullptr; JSValueRef exn = nullptr;
JSC_JSObjectSetProperty(m_context, m_obj, propName, value, kJSPropertyAttributeNone, &exn); JSC_JSObjectSetProperty(m_context, m_obj, propName, value, kJSPropertyAttributeNone, &exn);
if (exn) { if (exn) {
std::string exceptionText = Value(m_context, exn).toString().str(); throw JSException(m_context, exn, nullptr, "Failed to set property '%s'", propName.str().c_str());
throwJSExecutionException("Failed to set property: %s", exceptionText.c_str());
} }
} }
@ -232,8 +220,7 @@ void Object::setPropertyAtIndex(unsigned int index, const Value& value) {
JSValueRef exn = nullptr; JSValueRef exn = nullptr;
JSC_JSObjectSetPropertyAtIndex(m_context, m_obj, index, value, &exn); JSC_JSObjectSetPropertyAtIndex(m_context, m_obj, index, value, &exn);
if (exn) { if (exn) {
std::string exceptionText = Value(m_context, exn).toString().str(); throw JSException(m_context, exn, nullptr, "Failed to set property at index %d", index);
throwJSExecutionException("Failed to set property: %s", exceptionText.c_str());
} }
} }

View File

@ -15,30 +15,13 @@
namespace facebook { namespace facebook {
namespace react { namespace react {
#ifndef RN_EXPORT
#define RN_EXPORT __attribute__((visibility("default")))
#endif
class Value; class Value;
class Context;
class JSException : public std::exception {
public:
explicit JSException(const char* msg)
: msg_(msg), stack_("") {}
JSException(const char* msg, const char* stack)
: msg_(msg), stack_(stack) {}
const std::string& getStack() const {
return stack_;
}
virtual const char* what() const noexcept override {
return msg_.c_str();
}
private:
std::string msg_;
std::string stack_;
};
// C++ object wrapper for JSStringRef
class String : public noncopyable { class String : public noncopyable {
public: public:
explicit String(): m_context(nullptr), m_string(nullptr) {} // dummy empty constructor explicit String(): m_context(nullptr), m_string(nullptr) {} // dummy empty constructor
@ -82,12 +65,16 @@ public:
return m_string; return m_string;
} }
JSContextRef context() const {
return m_context;
}
// Length in characters // Length in characters
size_t length() const { size_t length() const {
return m_string ? JSC_JSStringGetLength(m_context, m_string) : 0; return m_string ? JSC_JSStringGetLength(m_context, m_string) : 0;
} }
// Length in bytes of a null-terminated utf8 encoded value // Length in bytes of a nul-terminated utf8 encoded value
size_t utf8Size() const { size_t utf8Size() const {
return m_string ? JSC_JSStringGetMaximumUTF8CStringSize(m_context, m_string) : 0; return m_string ? JSC_JSStringGetMaximumUTF8CStringSize(m_context, m_string) : 0;
} }
@ -115,7 +102,7 @@ public:
return unicode::utf16toUTF8(utf16, stringLength); return unicode::utf16toUTF8(utf16, stringLength);
} }
// Assumes that utf8 is null terminated // Assumes that utf8 is nul-terminated
bool equals(const char* utf8) { bool equals(const char* utf8) {
return m_string ? JSC_JSStringIsEqualToUTF8CString(m_context, m_string, utf8) : false; return m_string ? JSC_JSStringIsEqualToUTF8CString(m_context, m_string, utf8) : false;
} }
@ -133,10 +120,13 @@ public:
return createExpectingAscii(context, ascii.c_str(), ascii.size()); return createExpectingAscii(context, ascii.c_str(), ascii.size());
} }
// Creates a String wrapper and increases the refcount of the JSStringRef
static String ref(JSContextRef context, JSStringRef string) { static String ref(JSContextRef context, JSStringRef string) {
return String(context, string, false); return String(context, string, false);
} }
// Creates a String wrapper that takes over ownership of the string. The
// JSStringRef passed in must previously have been created or retained.
static String adopt(JSContextRef context, JSStringRef string) { static String adopt(JSContextRef context, JSStringRef string) {
return String(context, string, true); return String(context, string, true);
} }
@ -154,6 +144,9 @@ private:
JSStringRef m_string; JSStringRef m_string;
}; };
// C++ object wrapper for JSObjectRef. The underlying JSObjectRef can be
// optionally protected. You must protect the object if it is ever
// heap-allocated, since otherwise you may end up with an invalid reference.
class Object : public noncopyable { class Object : public noncopyable {
public: public:
Object(JSContextRef context, JSObjectRef obj) : Object(JSContextRef context, JSObjectRef obj) :
@ -248,11 +241,16 @@ private:
Value callAsFunction(JSObjectRef thisObj, int nArgs, const JSValueRef args[]) const; Value callAsFunction(JSObjectRef thisObj, int nArgs, const JSValueRef args[]) const;
}; };
// C++ object wrapper for JSValueRef. The underlying JSValueRef is not
// protected, so this class should always be used as a stack-allocated
// variable.
class Value : public noncopyable { class Value : public noncopyable {
public: public:
__attribute__((visibility("default"))) Value(JSContextRef context, JSValueRef value); RN_EXPORT Value(JSContextRef context, JSValueRef value);
__attribute__((visibility("default"))) Value(JSContextRef context, JSStringRef value); RN_EXPORT Value(JSContextRef context, JSStringRef value);
__attribute__((visibility("default"))) Value(Value&&);
RN_EXPORT Value(const Value &o) : Value(o.m_context, o.m_value) {}
RN_EXPORT Value(const String &o) : Value(o.context(), o) {}
Value& operator=(Value&& other) { Value& operator=(Value&& other) {
m_context = other.m_context; m_context = other.m_context;
@ -309,15 +307,13 @@ public:
return getType() == kJSTypeObject; return getType() == kJSTypeObject;
} }
Object asObject(); Object asObject() const;
bool isString() const { bool isString() const {
return getType() == kJSTypeString; return getType() == kJSTypeString;
} }
String toString() noexcept { String toString() const;
return String::adopt(context(), JSC_JSValueToStringCopy(context(), m_value, nullptr));
}
static Value makeError(JSContextRef ctx, const char *error); static Value makeError(JSContextRef ctx, const char *error);
@ -333,13 +329,15 @@ public:
return Value(ctx, JSC_JSValueMakeNull(ctx)); return Value(ctx, JSC_JSValueMakeNull(ctx));
} }
__attribute__((visibility("default"))) std::string toJSONString(unsigned indent = 0) const; RN_EXPORT std::string toJSONString(unsigned indent = 0) const;
__attribute__((visibility("default"))) static Value fromJSON(JSContextRef ctx, const String& json); RN_EXPORT static Value fromJSON(JSContextRef ctx, const String& json);
__attribute__((visibility("default"))) static Value fromDynamic(JSContextRef ctx, const folly::dynamic& value); RN_EXPORT static Value fromDynamic(JSContextRef ctx, const folly::dynamic& value);
__attribute__((visibility("default"))) JSContextRef context() const; RN_EXPORT JSContextRef context() const;
protected:
private:
JSContextRef m_context; JSContextRef m_context;
JSValueRef m_value; JSValueRef m_value;
static JSValueRef fromDynamicInner(JSContextRef ctx, const folly::dynamic& obj); static JSValueRef fromDynamicInner(JSContextRef ctx, const folly::dynamic& obj);
}; };