2016-05-04 02:29:58 +00:00
|
|
|
// Copyright 2004-present Facebook. All Rights Reserved.
|
|
|
|
|
|
|
|
#include "JSCHelpers.h"
|
|
|
|
|
2016-11-01 18:38:43 +00:00
|
|
|
#ifdef WITH_FBSYSTRACE
|
|
|
|
#include <fbsystrace.h>
|
|
|
|
#endif
|
|
|
|
|
2016-05-04 02:29:58 +00:00
|
|
|
#include <JavaScriptCore/JSStringRef.h>
|
2016-05-14 00:14:49 +00:00
|
|
|
#include <folly/String.h>
|
2016-05-04 02:29:58 +00:00
|
|
|
#include <glog/logging.h>
|
|
|
|
|
|
|
|
#include "Value.h"
|
|
|
|
|
|
|
|
namespace facebook {
|
|
|
|
namespace react {
|
|
|
|
|
2016-11-01 18:38:47 +00:00
|
|
|
namespace {
|
|
|
|
|
|
|
|
JSValueRef functionCaller(
|
|
|
|
JSContextRef ctx,
|
|
|
|
JSObjectRef function,
|
|
|
|
JSObjectRef thisObject,
|
|
|
|
size_t argumentCount,
|
|
|
|
const JSValueRef arguments[],
|
|
|
|
JSValueRef* exception) {
|
|
|
|
auto* f = static_cast<JSFunction*>(JSObjectGetPrivate(function));
|
|
|
|
return (*f)(ctx, thisObject, argumentCount, arguments);
|
|
|
|
}
|
|
|
|
|
|
|
|
JSClassRef createFuncClass() {
|
|
|
|
auto definition = kJSClassDefinitionEmpty;
|
|
|
|
definition.finalize = [](JSObjectRef object) {
|
|
|
|
auto* function = static_cast<JSFunction*>(JSObjectGetPrivate(object));
|
|
|
|
delete function;
|
|
|
|
};
|
|
|
|
definition.callAsFunction = exceptionWrapMethod<&functionCaller>();
|
|
|
|
|
|
|
|
return JSClassCreate(&definition);
|
|
|
|
}
|
|
|
|
|
|
|
|
JSObjectRef makeFunction(
|
|
|
|
JSContextRef ctx,
|
|
|
|
JSStringRef name,
|
|
|
|
JSFunction function) {
|
|
|
|
static auto kClassDef = createFuncClass();
|
|
|
|
auto functionObject = Object(ctx, JSObjectMake(ctx, kClassDef, new JSFunction(std::move(function))));
|
|
|
|
functionObject.setProperty("name", Value(ctx, name));
|
|
|
|
return functionObject;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
JSObjectRef makeFunction(
|
|
|
|
JSContextRef ctx,
|
|
|
|
const char* name,
|
|
|
|
JSFunction function) {
|
2016-11-18 14:25:29 +00:00
|
|
|
return makeFunction(ctx, String(ctx, name), std::move(function));
|
2016-11-01 18:38:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void installGlobalFunction(
|
|
|
|
JSGlobalContextRef ctx,
|
|
|
|
const char* name,
|
|
|
|
JSFunction function) {
|
2016-11-18 14:25:29 +00:00
|
|
|
auto jsName = String(ctx, name);
|
2016-11-01 18:38:47 +00:00
|
|
|
auto functionObj = makeFunction(ctx, jsName, std::move(function));
|
2016-11-18 14:25:24 +00:00
|
|
|
Object::getGlobalObject(ctx).setProperty(jsName, Value(ctx, functionObj));
|
2016-11-01 18:38:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
JSObjectRef makeFunction(
|
|
|
|
JSGlobalContextRef ctx,
|
|
|
|
const char* name,
|
|
|
|
JSObjectCallAsFunctionCallback callback) {
|
2016-11-18 14:25:29 +00:00
|
|
|
auto jsName = String(ctx, name);
|
2016-11-01 18:38:47 +00:00
|
|
|
return JSObjectMakeFunctionWithCallback(ctx, jsName, callback);
|
|
|
|
}
|
|
|
|
|
2016-05-04 02:29:58 +00:00
|
|
|
void installGlobalFunction(
|
|
|
|
JSGlobalContextRef ctx,
|
|
|
|
const char* name,
|
|
|
|
JSObjectCallAsFunctionCallback callback) {
|
2016-11-18 14:25:29 +00:00
|
|
|
String jsName(ctx, name);
|
2016-05-04 02:29:58 +00:00
|
|
|
JSObjectRef functionObj = JSObjectMakeFunctionWithCallback(
|
2016-11-18 14:25:29 +00:00
|
|
|
ctx, jsName, callback);
|
2016-11-18 14:25:24 +00:00
|
|
|
Object::getGlobalObject(ctx).setProperty(jsName, Value(ctx, functionObj));
|
2016-05-04 02:29:58 +00:00
|
|
|
}
|
|
|
|
|
2016-10-11 14:19:31 +00:00
|
|
|
void installGlobalProxy(
|
|
|
|
JSGlobalContextRef ctx,
|
|
|
|
const char* name,
|
|
|
|
JSObjectGetPropertyCallback callback) {
|
|
|
|
JSClassDefinition proxyClassDefintion = kJSClassDefinitionEmpty;
|
|
|
|
proxyClassDefintion.className = "_FBProxyClass";
|
|
|
|
proxyClassDefintion.getProperty = callback;
|
|
|
|
|
2016-11-18 14:25:24 +00:00
|
|
|
JSClassRef proxyClass = JSClassCreate(&proxyClassDefintion);
|
2016-10-11 14:19:31 +00:00
|
|
|
JSObjectRef proxyObj = JSObjectMake(ctx, proxyClass, nullptr);
|
|
|
|
JSClassRelease(proxyClass);
|
|
|
|
|
2016-11-18 14:25:24 +00:00
|
|
|
Object::getGlobalObject(ctx).setProperty(name, Value(ctx, proxyObj));
|
2016-11-02 19:18:11 +00:00
|
|
|
}
|
|
|
|
|
2016-11-18 14:25:24 +00:00
|
|
|
void removeGlobal(JSGlobalContextRef ctx, const char* name) {
|
|
|
|
Object::getGlobalObject(ctx).setProperty(name, Value::makeUndefined(ctx));
|
2016-05-04 02:29:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
JSValueRef evaluateScript(JSContextRef context, JSStringRef script, JSStringRef source) {
|
2016-11-18 14:25:24 +00:00
|
|
|
#ifdef WITH_FBSYSTRACE
|
2016-11-01 18:38:43 +00:00
|
|
|
fbsystrace::FbSystraceSection s(TRACE_TAG_REACT_CXX_BRIDGE, "evaluateScript");
|
2016-11-18 14:25:24 +00:00
|
|
|
#endif
|
2016-05-04 02:29:58 +00:00
|
|
|
JSValueRef exn, result;
|
|
|
|
result = JSEvaluateScript(context, script, NULL, source, 0, &exn);
|
|
|
|
if (result == nullptr) {
|
2016-07-11 13:52:06 +00:00
|
|
|
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
|
2016-07-07 15:37:27 +00:00
|
|
|
|
2016-07-11 13:52:06 +00:00
|
|
|
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.
|
2016-11-18 14:25:29 +00:00
|
|
|
std::string locationInfo = source != nullptr ? String::ref(context, source).str() : "";
|
2016-07-11 13:52:06 +00:00
|
|
|
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());
|
2016-07-07 21:46:12 +00:00
|
|
|
}
|
2016-07-11 13:52:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!locationInfo.empty()) {
|
|
|
|
exceptionText += " (" + locationInfo + ")";
|
|
|
|
}
|
2016-05-14 00:14:49 +00:00
|
|
|
|
2016-07-11 13:52:06 +00:00
|
|
|
LOG(ERROR) << "Got JS Exception: " << exceptionText;
|
2016-06-16 21:28:28 +00:00
|
|
|
|
2016-07-11 13:52:06 +00:00
|
|
|
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(
|
2016-06-16 21:28:28 +00:00
|
|
|
exceptionText.c_str(), jsStack.toString().str().c_str());
|
2016-05-04 02:29:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-10 12:02:56 +00:00
|
|
|
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();
|
2016-11-18 14:25:24 +00:00
|
|
|
return Value::makeError(ctx, msg.str().c_str());
|
2016-05-10 12:02:56 +00:00
|
|
|
} catch (const char* ex) {
|
|
|
|
msg << "C++ Exception (thrown as a char*) in '" << exceptionLocation << "': " << ex;
|
2016-11-18 14:25:24 +00:00
|
|
|
return Value::makeError(ctx, msg.str().c_str());
|
2016-05-10 12:02:56 +00:00
|
|
|
} catch (...) {
|
|
|
|
msg << "Unknown C++ Exception in '" << exceptionLocation << "'";
|
2016-11-18 14:25:24 +00:00
|
|
|
return Value::makeError(ctx, msg.str().c_str());
|
2016-05-10 12:02:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-07 11:48:45 +00:00
|
|
|
JSValueRef translatePendingCppExceptionToJSError(JSContextRef ctx, JSObjectRef jsFunctionCause) {
|
|
|
|
try {
|
|
|
|
auto functionName = Object(ctx, jsFunctionCause).getProperty("name").toString().str();
|
|
|
|
return translatePendingCppExceptionToJSError(ctx, functionName.c_str());
|
|
|
|
} catch (...) {
|
2016-11-18 14:25:24 +00:00
|
|
|
return Value::makeError(ctx, "Failed to get function name while handling exception");
|
2016-07-07 11:48:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-04 02:29:58 +00:00
|
|
|
} }
|