This app provides monitoring and information features for the common freifunk user and the technical stuff of a freifunk community.
Code base is taken from a TUM Practical Course project and added here to see if Freifunk Altdorf can use it.
https://www.freifunk-altdorf.de
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
296 lines
9.0 KiB
296 lines
9.0 KiB
// Copyright 2004-present Facebook. All Rights Reserved. |
|
|
|
#include "JSCHelpers.h" |
|
|
|
#ifdef WITH_FBSYSTRACE |
|
#include <fbsystrace.h> |
|
#endif |
|
|
|
#include <glog/logging.h> |
|
|
|
#if WITH_FBJSCEXTENSIONS |
|
#include <pthread.h> |
|
#endif |
|
|
|
#include "JavaScriptCore.h" |
|
#include "Value.h" |
|
#include <privatedata/PrivateDataBase.h> |
|
|
|
#if WITH_FBJSCEXTENSIONS |
|
#undef ASSERT |
|
#undef WTF_EXPORT_PRIVATE |
|
|
|
#include <JavaScriptCore/config.h> |
|
#include <wtf/WTFThreadData.h> |
|
|
|
#undef TRUE |
|
#undef FALSE |
|
#endif |
|
|
|
namespace facebook { |
|
namespace react { |
|
|
|
namespace { |
|
|
|
class JSFunctionPrivateData : public PrivateDataBase { |
|
public: |
|
explicit JSFunctionPrivateData(JSFunction&& function) : jsFunction_{std::move(function)} {} |
|
|
|
JSFunction& getJSFunction() { |
|
return jsFunction_; |
|
} |
|
|
|
private: |
|
JSFunction jsFunction_; |
|
}; |
|
|
|
JSValueRef functionCaller( |
|
JSContextRef ctx, |
|
JSObjectRef function, |
|
JSObjectRef thisObject, |
|
size_t argumentCount, |
|
const JSValueRef arguments[], |
|
JSValueRef* exception) { |
|
const bool isCustomJSC = isCustomJSCPtr(ctx); |
|
auto* privateData = PrivateDataBase::cast<JSFunctionPrivateData>( |
|
JSC_JSObjectGetPrivate(isCustomJSC, function)); |
|
return (privateData->getJSFunction())(ctx, thisObject, argumentCount, arguments); |
|
} |
|
|
|
JSClassRef createFuncClass(JSContextRef ctx) { |
|
JSClassDefinition definition = kJSClassDefinitionEmpty; |
|
definition.attributes |= kJSClassAttributeNoAutomaticPrototype; |
|
|
|
// Need to duplicate the two different finalizer blocks, since there's no way |
|
// for it to capture this static information. |
|
const bool isCustomJSC = isCustomJSCPtr(ctx); |
|
if (isCustomJSC) { |
|
definition.finalize = [](JSObjectRef object) { |
|
auto* privateData = PrivateDataBase::cast<JSFunctionPrivateData>( |
|
JSC_JSObjectGetPrivate(true, object)); |
|
delete privateData; |
|
}; |
|
} else { |
|
definition.finalize = [](JSObjectRef object) { |
|
auto* privateData = PrivateDataBase::cast<JSFunctionPrivateData>( |
|
JSC_JSObjectGetPrivate(false, object)); |
|
delete privateData; |
|
}; |
|
} |
|
definition.callAsFunction = exceptionWrapMethod<&functionCaller>(); |
|
|
|
return JSC_JSClassCreate(isCustomJSC, &definition); |
|
} |
|
|
|
JSObjectRef makeFunction( |
|
JSContextRef ctx, |
|
JSStringRef name, |
|
JSFunction function) { |
|
static JSClassRef kClassDef = NULL, kCustomJSCClassDef = NULL; |
|
JSClassRef *classRef = isCustomJSCPtr(ctx) ? &kCustomJSCClassDef : &kClassDef; |
|
if (!*classRef) { |
|
*classRef = createFuncClass(ctx); |
|
} |
|
|
|
// dealloc in kClassDef.finalize |
|
JSFunctionPrivateData *functionDataPtr = new JSFunctionPrivateData(std::move(function)); |
|
auto functionObject = Object(ctx, JSC_JSObjectMake(ctx, *classRef, functionDataPtr)); |
|
functionObject.setProperty("name", Value(ctx, name)); |
|
return functionObject; |
|
} |
|
|
|
} |
|
|
|
void JSException::buildMessage(JSContextRef ctx, JSValueRef exn, JSStringRef sourceURL, const char* errorMsg) { |
|
std::ostringstream msgBuilder; |
|
if (errorMsg && strlen(errorMsg) > 0) { |
|
msgBuilder << errorMsg << ": "; |
|
} |
|
|
|
Object exnObject = Value(ctx, exn).asObject(); |
|
Value exnMessage = exnObject.getProperty("message"); |
|
msgBuilder << (exnMessage.isString() ? exnMessage : (Value)exnObject).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() : ""; |
|
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); |
|
} |
|
} |
|
|
|
namespace ExceptionHandling { |
|
|
|
PlatformErrorExtractor platformErrorExtractor; |
|
|
|
} |
|
|
|
JSObjectRef makeFunction( |
|
JSContextRef ctx, |
|
const char* name, |
|
JSFunction function) { |
|
return makeFunction(ctx, String(ctx, name), std::move(function)); |
|
} |
|
|
|
void installGlobalFunction( |
|
JSGlobalContextRef ctx, |
|
const char* name, |
|
JSFunction function) { |
|
auto jsName = String(ctx, name); |
|
auto functionObj = makeFunction(ctx, jsName, std::move(function)); |
|
Object::getGlobalObject(ctx).setProperty(jsName, Value(ctx, functionObj)); |
|
} |
|
|
|
JSObjectRef makeFunction( |
|
JSGlobalContextRef ctx, |
|
const char* name, |
|
JSObjectCallAsFunctionCallback callback) { |
|
auto jsName = String(ctx, name); |
|
return JSC_JSObjectMakeFunctionWithCallback(ctx, jsName, callback); |
|
} |
|
|
|
void installGlobalFunction( |
|
JSGlobalContextRef ctx, |
|
const char* name, |
|
JSObjectCallAsFunctionCallback callback) { |
|
String jsName(ctx, name); |
|
JSObjectRef functionObj = JSC_JSObjectMakeFunctionWithCallback( |
|
ctx, jsName, callback); |
|
Object::getGlobalObject(ctx).setProperty(jsName, Value(ctx, functionObj)); |
|
} |
|
|
|
void installGlobalProxy( |
|
JSGlobalContextRef ctx, |
|
const char* name, |
|
JSObjectGetPropertyCallback callback) { |
|
JSClassDefinition proxyClassDefintion = kJSClassDefinitionEmpty; |
|
proxyClassDefintion.attributes |= kJSClassAttributeNoAutomaticPrototype; |
|
proxyClassDefintion.getProperty = callback; |
|
|
|
const bool isCustomJSC = isCustomJSCPtr(ctx); |
|
JSClassRef proxyClass = JSC_JSClassCreate(isCustomJSC, &proxyClassDefintion); |
|
JSObjectRef proxyObj = JSC_JSObjectMake(ctx, proxyClass, nullptr); |
|
JSC_JSClassRelease(isCustomJSC, proxyClass); |
|
|
|
Object::getGlobalObject(ctx).setProperty(name, Value(ctx, proxyObj)); |
|
} |
|
|
|
void removeGlobal(JSGlobalContextRef ctx, const char* name) { |
|
Object::getGlobalObject(ctx).setProperty(name, Value::makeUndefined(ctx)); |
|
} |
|
|
|
JSValueRef evaluateScript(JSContextRef context, JSStringRef script, JSStringRef sourceURL) { |
|
JSValueRef exn, result; |
|
result = JSC_JSEvaluateScript(context, script, NULL, sourceURL, 0, &exn); |
|
if (result == nullptr) { |
|
throw JSException(context, exn, sourceURL); |
|
} |
|
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) { |
|
throw JSException(context, exn, sourceURL); |
|
} |
|
return result; |
|
} |
|
#endif |
|
|
|
JSContextLock::JSContextLock(JSGlobalContextRef ctx) noexcept |
|
#if WITH_FBJSCEXTENSIONS |
|
: ctx_(ctx), |
|
globalLock_(PTHREAD_MUTEX_INITIALIZER) |
|
{ |
|
WTFThreadData& threadData = wtfThreadData(); |
|
|
|
// Code below is responsible for acquiring locks. It should execute |
|
// atomically, thus none of the functions invoked from now on are allowed to |
|
// throw an exception |
|
try { |
|
if (!threadData.isDebuggerThread()) { |
|
CHECK(0 == pthread_mutex_lock(&globalLock_)); |
|
} |
|
JSLock(ctx_); |
|
} catch (...) { |
|
abort(); |
|
} |
|
} |
|
#else |
|
{} |
|
#endif |
|
|
|
|
|
JSContextLock::~JSContextLock() noexcept { |
|
#if WITH_FBJSCEXTENSIONS |
|
WTFThreadData& threadData = wtfThreadData(); |
|
|
|
JSUnlock(ctx_); |
|
if (!threadData.isDebuggerThread()) { |
|
CHECK(0 == pthread_mutex_unlock(&globalLock_)); |
|
} |
|
#endif |
|
} |
|
|
|
|
|
JSValueRef translatePendingCppExceptionToJSError(JSContextRef ctx, const char *exceptionLocation) { |
|
try { |
|
throw; |
|
} catch (const std::bad_alloc& ex) { |
|
throw; // We probably shouldn't try to handle this in JS |
|
} catch (const std::exception& ex) { |
|
if (ExceptionHandling::platformErrorExtractor) { |
|
auto extractedEror = ExceptionHandling::platformErrorExtractor(ex, exceptionLocation); |
|
if (extractedEror.message.length() > 0) { |
|
return Value::makeError(ctx, extractedEror.message.c_str(), extractedEror.stack.c_str()); |
|
} |
|
} |
|
auto msg = folly::to<std::string>("C++ exception in '", exceptionLocation, "'\n\n", ex.what()); |
|
return Value::makeError(ctx, msg.c_str()); |
|
} catch (const char* ex) { |
|
auto msg = folly::to<std::string>("C++ exception (thrown as a char*) in '", exceptionLocation, "'\n\n", ex); |
|
return Value::makeError(ctx, msg.c_str()); |
|
} catch (...) { |
|
auto msg = folly::to<std::string>("Unknown C++ exception in '", exceptionLocation, "'"); |
|
return Value::makeError(ctx, msg.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 Value::makeError(ctx, "Failed to translate native exception"); |
|
} |
|
} |
|
|
|
} }
|
|
|