diff --git a/include/jsc/JavaScriptUtils.h b/include/jsc/JavaScriptUtils.h index dde6464..8d5da77 100644 --- a/include/jsc/JavaScriptUtils.h +++ b/include/jsc/JavaScriptUtils.h @@ -117,4 +117,10 @@ rtError rtGetRandomValuesBinding(int numArgs, const rtValue* args, rtValue* resu rtError rtInstallTimeout(int numArgs, const rtValue* args, rtValue* result, bool repeat); JSValueRef requireCallback(JSContextRef ctx, JSObjectRef, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef *exception); +JSValueRef consoleLogCallback(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception); +JSValueRef consoleWarnCallback(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception); +JSValueRef consoleErrorCallback(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception); +JSValueRef consoleDebugCallback(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception); +JSValueRef consoleInfoCallback(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception); + #endif /* JAVASCRIPTMISC_H */ diff --git a/src/jsc/JavaScriptContext.cpp b/src/jsc/JavaScriptContext.cpp index b89c7f6..81bae51 100644 --- a/src/jsc/JavaScriptContext.cpp +++ b/src/jsc/JavaScriptContext.cpp @@ -125,7 +125,7 @@ if (mModuleSettings.enablePlayer) delete mNetworkMetricsData; mNetworkMetricsData = nullptr; } - + JSGlobalContextRelease(mContext); JSContextGroupRelease(mContextGroup); rtLogInfo("%s end", __FUNCTION__); @@ -141,7 +141,7 @@ void JavaScriptContext::loadAAMPJSBindingsLib() static const char *aampJSBindingsLib = "libaampjsbindings.so"; if(!getenv("ETHAN_LOGGING_PIPE")) { - // This is required for NativeJS Plugin + // This is required for NativeJS Plugin static const char *jscLib = "libJavaScriptCore.so"; jscLibHandle = dlopen(jscLib, RTLD_NOW | RTLD_GLOBAL); if (!jscLibHandle) @@ -422,6 +422,70 @@ if (mModuleSettings.enablePlayer) JSStringRelease(funcName); }; injectFun(mContext, "require", requireCallback); + +#ifdef USE_ETHANLOG + if (getenv("ETHAN_LOGGING_PIPE") != nullptr) { + + injectFun(mContext, "__ethanLog", consoleLogCallback); + injectFun(mContext, "__ethanWarn", consoleWarnCallback); + injectFun(mContext, "__ethanError", consoleErrorCallback); + injectFun(mContext, "__ethanDebug", consoleDebugCallback); + injectFun(mContext, "__ethanInfo", consoleInfoCallback); + + const char* wrapper = R"JS( + (function() { + if (typeof console === "undefined") return; + + var originalMethods = { + log : console.log, + warn : console.warn, + error : console.error, + debug : console.debug, + info : console.info + }; + + function getCallerLocation() { + const stack = new Error().stack; + if (!stack) return null; + const lines = stack.split("\n"); + const callerLine = lines[2] || ""; + const match = callerLine.match(/([^\s@]+\.js:\d+:\d+)/); + return match ? match[1] : null; + } + + function consoleMethod(methodName, original, ethanFn) { + return function() { + var caller = getCallerLocation(); + var args = Array.prototype.slice.call(arguments); + if (caller) args.unshift('[' + caller + ']'); + if (original) original.apply(console, args); + ethanFn.apply(null, args); + }; + } + console.log = consoleMethod('log', originalMethods.log, __ethanLog); + console.warn = consoleMethod('warn', originalMethods.warn, __ethanWarn); + console.error = consoleMethod('error', originalMethods.error, __ethanError); + console.debug = consoleMethod('debug', originalMethods.debug, __ethanDebug); + console.info = consoleMethod('info', originalMethods.info, __ethanInfo); + })(); + )JS"; + + JSStringRef codeStr = JSStringCreateWithUTF8CString(wrapper); + JSValueRef exception = nullptr; + JSEvaluateScript(mContext, codeStr, nullptr, nullptr, 0, &exception); + JSStringRelease(codeStr); + + if (exception) { + JSStringRef exceptStr = JSValueToStringCopy(mContext, exception, nullptr); + rtString errorStr = jsToRtString(exceptStr); + JSStringRelease(exceptStr); + rtLogError("Failed to inject console wrapper: %s", errorStr.cString()); + } else { + NativeJSLogger::log(INFO, "Console extended - added ethanlog routing\n"); + } + } +#endif + if(mModuleSettings.enablePlayer) { runFile("video.js", nullptr); diff --git a/src/jsc/JavaScriptUtils.cpp b/src/jsc/JavaScriptUtils.cpp index f91c026..cef1dec 100644 --- a/src/jsc/JavaScriptUtils.cpp +++ b/src/jsc/JavaScriptUtils.cpp @@ -171,7 +171,7 @@ void assertIsMainThread() { } - void rtHttpRequestEx::onDownloadProgressImpl(double progress) + void rtHttpRequestEx::onDownloadProgressImpl(double progress) { AddRef(); @@ -186,7 +186,7 @@ void assertIsMainThread() }); } - void rtHttpRequestEx::onDownloadCompleteImpl(rtFileDownloadRequest* downloadRequest) + void rtHttpRequestEx::onDownloadCompleteImpl(rtFileDownloadRequest* downloadRequest) { AddRef(); if (!downloadRequest->errorString().isEmpty()) { @@ -324,7 +324,7 @@ rtError rtReadBinaryBinding(int numArgs, const rtValue* args, rtValue* result, v { FILE *ptr = nullptr; ptr = fopen("hello.wasm","rb"); // r for read, b for binary - + if (!ptr) { rtLogError("Failed to open file: hello.wasm"); @@ -340,7 +340,7 @@ rtError rtReadBinaryBinding(int numArgs, const rtValue* args, rtValue* result, v fclose(ptr); return RT_ERROR; } - + if (buf.st_size <= 0) { rtLogError("File size is zero or negative: %ld", (long)buf.st_size); @@ -734,7 +734,7 @@ rtError rtJSRuntimeDownloadMetrics(int numArgs, const rtValue* args, rtValue* re rtValue keys; if (map->Get("allKeys", &keys) != RT_OK) { rtLogWarn("Could not retrieve url for network metrics data."); - delete netMetricsArray; + delete netMetricsArray; return RT_FAIL; } rtObjectRef objRef = keys.toObject(); @@ -742,7 +742,7 @@ rtError rtJSRuntimeDownloadMetrics(int numArgs, const rtValue* args, rtValue* re if (!keysArray) { rtLogWarn("No url found in the network metrics data."); - delete netMetricsArray; + delete netMetricsArray; return RT_FAIL; } @@ -758,7 +758,7 @@ rtError rtJSRuntimeDownloadMetrics(int numArgs, const rtValue* args, rtValue* re NetworkMetrics* metrics = (NetworkMetrics*)storedValue.toVoidPtr(); if (!metrics) { rtLogError("Failed to cast stored value to NetworkMetrics structure for url: %s.", key.cString()); - delete netMetricsArray; + delete netMetricsArray; return RT_FAIL; } rtMapObject* metricsMap = new rtMapObject(); @@ -819,7 +819,7 @@ rtError rtSetExternalAppHandlerBinding(int numArgs, const rtValue* args, rtValue return RT_OK; } -rtError rtGetRandomValuesBinding(int numArgs, const rtValue* args, rtValue* /*result*/, void* /*context*/) +rtError rtGetRandomValuesBinding(int numArgs, const rtValue* args, rtValue* /*result*/, void* /*context*/) { if (numArgs < 1 || args[0].getType() != RT_objectType) { rtLogError("rtGetRandomValuesBinding: Invalid arguments"); @@ -827,15 +827,83 @@ rtError rtGetRandomValuesBinding(int numArgs, const rtValue* args, rtValue* /*re } rtObjectRef arrObj = args[0].toObject(); - + uint32_t len = arrObj.get("length"); - + std::random_device rd; std::mt19937 gen(rd()); std::uniform_int_distribution dist; - + for (uint32_t i = 0; i < len; i++) { - arrObj.set(i, (double)dist(gen)); + arrObj.set(i, (double)dist(gen)); } return RT_OK; } + +//Wrapper for console logs, used when running inside widget +static JSValueRef consoleCallbackImpl(JSContextRef ctx, size_t argumentCount, + const JSValueRef arguments[], JSValueRef* exception, + const char* methodName) +{ + + if (argumentCount == 0) + return JSValueMakeUndefined(ctx); + + std::ostringstream oss; + oss << methodName << " "; + + for (size_t i = 0; i < argumentCount; i++) { + JSStringRef jsStr = JSValueToStringCopy(ctx, arguments[i], exception); + if (jsStr) { + size_t size = JSStringGetMaximumUTF8CStringSize(jsStr); + char* buffer = new char[size]; + JSStringGetUTF8CString(jsStr, buffer, size); + + if (i == 0 && buffer) { + oss << buffer; + } else { + oss << " " << buffer; + } + delete[] buffer; + JSStringRelease(jsStr); + } + } + + NativeJSLogger::log(INFO, "%s", oss.str().c_str()); + return JSValueMakeUndefined(ctx); +} + +JSValueRef consoleLogCallback(JSContextRef ctx, JSObjectRef function, + JSObjectRef thisObject, size_t argumentCount, + const JSValueRef arguments[], JSValueRef* exception) +{ + return consoleCallbackImpl(ctx, argumentCount, arguments, exception, "console.log"); +} + +JSValueRef consoleWarnCallback(JSContextRef ctx, JSObjectRef function, + JSObjectRef thisObject, size_t argumentCount, + const JSValueRef arguments[], JSValueRef* exception) +{ + return consoleCallbackImpl(ctx, argumentCount, arguments, exception, "console.warn"); +} + +JSValueRef consoleErrorCallback(JSContextRef ctx, JSObjectRef function, + JSObjectRef thisObject, size_t argumentCount, + const JSValueRef arguments[], JSValueRef* exception) +{ + return consoleCallbackImpl(ctx, argumentCount, arguments, exception, "console.error"); +} + +JSValueRef consoleDebugCallback(JSContextRef ctx, JSObjectRef function, + JSObjectRef thisObject, size_t argumentCount, + const JSValueRef arguments[], JSValueRef* exception) +{ + return consoleCallbackImpl(ctx, argumentCount, arguments, exception,"console.debug"); +} + +JSValueRef consoleInfoCallback(JSContextRef ctx, JSObjectRef function, + JSObjectRef thisObject, size_t argumentCount, + const JSValueRef arguments[], JSValueRef* exception) +{ + return consoleCallbackImpl(ctx, argumentCount, arguments, exception, "console.info"); +}