Cleanup and document Value wrapper (retry)
Reviewed By: mhorowitz Differential Revision: D5120975 fbshipit-source-id: 6e9c80a57fdcf7f3dad21d5521fb928b52c924c7
This commit is contained in:
parent
11424a8bc6
commit
03e1f40c1e
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -2,34 +2,54 @@
|
||||||
|
|
||||||
#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>
|
||||||
|
explicit JSException(const char* fmt, Args... args)
|
||||||
|
: msg_(folly::stringPrintf(fmt, args...)) {}
|
||||||
|
|
||||||
|
explicit JSException(JSContextRef ctx, JSValueRef exn, const char* msg) {
|
||||||
|
buildMessage(ctx, exn, nullptr, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit JSException(JSContextRef ctx, JSValueRef exn, JSStringRef sourceURL) {
|
||||||
|
buildMessage(ctx, exn, sourceURL, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
inline void throwJSExecutionException(const char* fmt, Args... args) {
|
explicit JSException(JSContextRef ctx, JSValueRef exn, JSStringRef sourceURL, const char* fmt, Args... args) {
|
||||||
int msgSize = snprintf(nullptr, 0, fmt, args...);
|
buildMessage(ctx, exn, sourceURL, folly::stringPrintf(fmt, args...).c_str());
|
||||||
msgSize = std::min(512, msgSize + 1);
|
|
||||||
char *msg = (char*) alloca(msgSize);
|
|
||||||
snprintf(msg, msgSize, fmt, args...);
|
|
||||||
throw JSException(msg);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename... Args>
|
const std::string& getStack() const {
|
||||||
inline void throwJSExecutionExceptionWithStack(const char* msg, const char* stack) {
|
return stack_;
|
||||||
throw JSException(msg, 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[])>;
|
||||||
|
|
||||||
JSObjectRef makeFunction(
|
JSObjectRef makeFunction(
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue