diff --git a/NativeScript/NativeScript-Prefix.pch b/NativeScript/NativeScript-Prefix.pch index ec0604b8..0773a04d 100644 --- a/NativeScript/NativeScript-Prefix.pch +++ b/NativeScript/NativeScript-Prefix.pch @@ -1,7 +1,7 @@ #ifndef NativeScript_Prefix_pch #define NativeScript_Prefix_pch -#define NATIVESCRIPT_VERSION "8.9.0" +#define NATIVESCRIPT_VERSION "9.0.0-esm.8" #ifdef DEBUG #define SIZEOF_OFF_T 8 diff --git a/NativeScript/NativeScript.mm b/NativeScript/NativeScript.mm index 0aea242c..ca639e7f 100644 --- a/NativeScript/NativeScript.mm +++ b/NativeScript/NativeScript.mm @@ -1,15 +1,20 @@ -#include #include "NativeScript.h" +#include #include "inspector/JsV8InspectorClient.h" #include "runtime/Console.h" -#include "runtime/RuntimeConfig.h" #include "runtime/Helpers.h" #include "runtime/Runtime.h" +#include "runtime/RuntimeConfig.h" #include "runtime/Tasks.h" using namespace v8; using namespace tns; +namespace tns { +// External flag from Runtime.mm to track JavaScript errors +extern bool jsErrorOccurred; +} + @implementation Config @synthesize BaseDir; @@ -23,99 +28,118 @@ @implementation NativeScript extern char defaultStartOfMetadataSection __asm("section$start$__DATA$__TNSMetadata"); -- (void)runScriptString: (NSString*) script runLoop: (BOOL) runLoop { - - std::string cppString = std::string([script UTF8String]); - runtime_->RunScript(cppString); - - if (runLoop) { - CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, true); - } +- (void)runScriptString:(NSString*)script runLoop:(BOOL)runLoop { + std::string cppString = std::string([script UTF8String]); + runtime_->RunScript(cppString); + if (runLoop) { + CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, true); + } - tns::Tasks::Drain(); - + tns::Tasks::Drain(); } std::unique_ptr runtime_; - (void)runMainApplication { - runtime_->RunMainScript(); - + runtime_->RunMainScript(); + + // In debug mode, if JavaScript errors occurred during boot, we've already handled them + // The ShowBootError function sends UIApplicationDidFinishLaunchingNotification + // which triggers NativeScript core to display the error UI + if (RuntimeConfig.IsDebug && jsErrorOccurred) { + NSLog(@"🔧 Debug mode - JavaScript boot error detected, but boot cycle completed successfully"); + NSLog(@"🔧 Debug mode - Error UI should be displayed via notification system"); + NSLog(@"🔧 Debug mode - App must stay alive indefinitely to prevent main() from returning"); + + // CRITICAL: We must NOT let main() return when there's a boot error + // The error UI is displayed, but if main() returns, the entire app process terminates + // This is different from a normal app where UIApplicationMain() never returns + while (true) { + @autoreleasepool { + CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1.0, true); + tns::Tasks::Drain(); + } + } + // This line is never reached - we stay in the loop to keep the app process alive + } else { + // Normal path - no boot errors CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, true); - tns::Tasks::Drain(); + } + tns::Tasks::Drain(); } - (bool)liveSync { - if (runtime_ == nullptr) { - return false; - } + if (runtime_ == nullptr) { + return false; + } - Isolate* isolate = runtime_->GetIsolate(); - return tns::LiveSync(isolate); + Isolate* isolate = runtime_->GetIsolate(); + return tns::LiveSync(isolate); } - (void)shutdownRuntime { - if (RuntimeConfig.IsDebug) { - Console::DetachInspectorClient(); - } - tns::Tasks::ClearTasks(); - if (runtime_ != nullptr) { - runtime_ = nullptr; - } + if (RuntimeConfig.IsDebug) { + Console::DetachInspectorClient(); + } + tns::Tasks::ClearTasks(); + if (runtime_ != nullptr) { + runtime_ = nullptr; + } } - (instancetype)initializeWithConfig:(Config*)config { - if (self = [super init]) { - RuntimeConfig.BaseDir = [config.BaseDir UTF8String]; - if (config.ApplicationPath != nil) { - RuntimeConfig.ApplicationPath = [[config.BaseDir stringByAppendingPathComponent:config.ApplicationPath] UTF8String]; - } else { - RuntimeConfig.ApplicationPath = [[config.BaseDir stringByAppendingPathComponent:@"app"] UTF8String]; - } - if (config.MetadataPtr != nil) { - RuntimeConfig.MetadataPtr = [config MetadataPtr]; - } else { - RuntimeConfig.MetadataPtr = &defaultStartOfMetadataSection; - } - RuntimeConfig.IsDebug = [config IsDebug]; - RuntimeConfig.LogToSystemConsole = [config LogToSystemConsole]; - - Runtime::Initialize(); - runtime_ = nullptr; - runtime_ = std::make_unique(); - - std::chrono::high_resolution_clock::time_point t1 = std::chrono::high_resolution_clock::now(); - Isolate* isolate = runtime_->CreateIsolate(); - v8::Locker l(isolate); - runtime_->Init(isolate); - std::chrono::high_resolution_clock::time_point t2 = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration_cast(t2 - t1).count(); - printf("Runtime initialization took %llims (version %s, V8 version %s)\n", duration, NATIVESCRIPT_VERSION, V8::GetVersion()); - - if (config.IsDebug) { - Isolate::Scope isolate_scope(isolate); - HandleScope handle_scope(isolate); - v8_inspector::JsV8InspectorClient* inspectorClient = new v8_inspector::JsV8InspectorClient(runtime_.get()); - inspectorClient->init(); - inspectorClient->registerModules(); - inspectorClient->connect([config ArgumentsCount], [config Arguments]); - Console::AttachInspectorClient(inspectorClient); - } + if (self = [super init]) { + RuntimeConfig.BaseDir = [config.BaseDir UTF8String]; + if (config.ApplicationPath != nil) { + RuntimeConfig.ApplicationPath = + [[config.BaseDir stringByAppendingPathComponent:config.ApplicationPath] UTF8String]; + } else { + RuntimeConfig.ApplicationPath = + [[config.BaseDir stringByAppendingPathComponent:@"app"] UTF8String]; } - return self; - + if (config.MetadataPtr != nil) { + RuntimeConfig.MetadataPtr = [config MetadataPtr]; + } else { + RuntimeConfig.MetadataPtr = &defaultStartOfMetadataSection; + } + RuntimeConfig.IsDebug = [config IsDebug]; + RuntimeConfig.LogToSystemConsole = [config LogToSystemConsole]; + + Runtime::Initialize(); + runtime_ = nullptr; + runtime_ = std::make_unique(); + + std::chrono::high_resolution_clock::time_point t1 = std::chrono::high_resolution_clock::now(); + Isolate* isolate = runtime_->CreateIsolate(); + v8::Locker l(isolate); + runtime_->Init(isolate); + std::chrono::high_resolution_clock::time_point t2 = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration_cast(t2 - t1).count(); + printf("Runtime initialization took %llims (version %s, V8 version %s)\n", duration, + NATIVESCRIPT_VERSION, V8::GetVersion()); + + if (config.IsDebug) { + Isolate::Scope isolate_scope(isolate); + HandleScope handle_scope(isolate); + v8_inspector::JsV8InspectorClient* inspectorClient = + new v8_inspector::JsV8InspectorClient(runtime_.get()); + inspectorClient->init(); + inspectorClient->registerModules(); + inspectorClient->connect([config ArgumentsCount], [config Arguments]); + Console::AttachInspectorClient(inspectorClient); + } + } + return self; } - (instancetype)initWithConfig:(Config*)config { - return [self initializeWithConfig:config]; + return [self initializeWithConfig:config]; } - (void)restartWithConfig:(Config*)config { - [self shutdownRuntime]; - [self initializeWithConfig:config]; + [self shutdownRuntime]; + [self initializeWithConfig:config]; } - - @end diff --git a/NativeScript/runtime/Console.cpp b/NativeScript/runtime/Console.cpp index c355587c..8377e5c3 100644 --- a/NativeScript/runtime/Console.cpp +++ b/NativeScript/runtime/Console.cpp @@ -1,8 +1,14 @@ #include "Console.h" -#include "Caches.h" + #include #include +#include +#include +#include + +#include "Caches.h" #include "Helpers.h" +#include "NativeScriptException.h" #include "RuntimeConfig.h" // #include "v8-log-agent-impl.h" #include @@ -11,384 +17,493 @@ using namespace v8; namespace tns { +// Flag to prevent duplicate error modals from OnUncaughtError +bool consoleModalShown = false; + void Console::Init(Local context) { - Isolate* isolate = context->GetIsolate(); - Context::Scope context_scope(context); - Local console = Object::New(isolate); - bool success = console->SetPrototype(context, Object::New(isolate)).FromMaybe(false); - tns::Assert(success, isolate); + Isolate* isolate = context->GetIsolate(); + Context::Scope context_scope(context); + Local console = Object::New(isolate); + bool success = + console->SetPrototype(context, Object::New(isolate)).FromMaybe(false); + tns::Assert(success, isolate); + + Console::AttachLogFunction(context, console, "log"); + Console::AttachLogFunction(context, console, "info"); + Console::AttachLogFunction(context, console, "error"); + Console::AttachLogFunction(context, console, "warn"); + Console::AttachLogFunction(context, console, "trace"); + Console::AttachLogFunction(context, console, "assert", AssertCallback); + Console::AttachLogFunction(context, console, "dir", DirCallback); + Console::AttachLogFunction(context, console, "time", TimeCallback); + Console::AttachLogFunction(context, console, "timeEnd", TimeEndCallback); + + Local global = context->Global(); + PropertyAttribute readOnlyFlags = static_cast( + PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly); + if (!global + ->DefineOwnProperty(context, tns::ToV8String(isolate, "console"), + console, readOnlyFlags) + .FromMaybe(false)) { + tns::Assert(false, isolate); + } +} - Console::AttachLogFunction(context, console, "log"); - Console::AttachLogFunction(context, console, "info"); - Console::AttachLogFunction(context, console, "error"); - Console::AttachLogFunction(context, console, "warn"); - Console::AttachLogFunction(context, console, "trace"); - Console::AttachLogFunction(context, console, "assert", AssertCallback); - Console::AttachLogFunction(context, console, "dir", DirCallback); - Console::AttachLogFunction(context, console, "time", TimeCallback); - Console::AttachLogFunction(context, console, "timeEnd", TimeEndCallback); - - Local global = context->Global(); - PropertyAttribute readOnlyFlags = static_cast(PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly); - if (!global->DefineOwnProperty(context, tns::ToV8String(isolate, "console"), console, readOnlyFlags).FromMaybe(false)) { - tns::Assert(false, isolate); - } +void Console::AttachInspectorClient( + v8_inspector::JsV8InspectorClient* aInspector) { + inspector = aInspector; } -void Console::AttachInspectorClient(v8_inspector::JsV8InspectorClient* aInspector) { - inspector = aInspector; +void Console::DetachInspectorClient() { inspector = nullptr; } + +bool isErrorMessage(const std::string& line) { + return line.find("Error") != std::string::npos; } -void Console::DetachInspectorClient() { - inspector = nullptr; +bool isStackFrame(const std::string& line) { + // e.g. " at foo (/path/to/file.ts:123:45)" + static const std::regex stackRe(R"(\s+at\s+\S+:\d+:\d+)"); + return std::regex_search(line, stackRe); } -void Console::LogCallback(const FunctionCallbackInfo& args) { - // TODO: implement 'forceLog' override option like android has, to force logs in prod if desired - if (!RuntimeConfig.LogToSystemConsole) { - return; +std::vector filterErrorLines( + const std::vector& allLines) { + std::vector result; + result.reserve(allLines.size()); + for (auto& line : allLines) { + if (isErrorMessage(line) || isStackFrame(line)) { + result.push_back(line); } + } + return result; +} - Isolate* isolate = args.GetIsolate(); - std::string stringResult = BuildStringFromArgs(args); - - Local data = args.Data().As(); - std::string verbosityLevel = tns::ToString(isolate, data); - std::string verbosityLevelUpper = verbosityLevel; - std::transform(verbosityLevelUpper.begin(), verbosityLevelUpper.end(), verbosityLevelUpper.begin(), ::toupper); - - std::stringstream ss; - ss << stringResult; - - if (verbosityLevel == "trace") { - std::string stacktrace = tns::GetStackTrace(isolate); - ss << std::endl << stacktrace << std::endl; +void Console::LogCallback(const FunctionCallbackInfo& args) { + // TODO: implement 'forceLog' override option like android has, to force logs + // in prod if desired + if (!RuntimeConfig.LogToSystemConsole) { + return; + } + + Isolate* isolate = args.GetIsolate(); + std::string stringResult = BuildStringFromArgs(args); + // Log("stringResult %s", stringResult.c_str()); + + Local data = args.Data().As(); + std::string verbosityLevel = tns::ToString(isolate, data); + + if (RuntimeConfig.IsDebug && + (verbosityLevel == "error" || verbosityLevel == "log")) { + // Simple universal error detection - any error with stack trace + bool hasStackTrace = isStackFrame(stringResult); + // bool hasStackTrace = (stringResult.find(" at ") != std::string::npos && + // stringResult.find("(file:") != std::string::npos && + // stringResult.length() > 200); + + if (hasStackTrace && !consoleModalShown) { + std::stringstream stackTraceLines; + stackTraceLines << stringResult; + + std::string stacktrace = tns::GetStackTrace(isolate); + stackTraceLines << std::endl << stacktrace << std::endl; + + std::string errorToDisplay = stackTraceLines.str(); + // Log("BEFORE mapped errorToDisplay %s", errorToDisplay.c_str()); + + // Extract error details + std::string errorTitle = "JavaScript Error"; + + // Use the new helper function to remap the stack trace + errorToDisplay = tns::RemapStackTrace(isolate, errorToDisplay); + + // Log("AFTER mapped errorToDisplay %s", errorToDisplay.c_str()); + + try { + NativeScriptException::ShowErrorModal(errorTitle, errorToDisplay, + errorToDisplay); + consoleModalShown = true; // Prevent duplicate modals + + } catch (const std::exception& e) { + Log("Console.cpp: Exception showing modal: %s", e.what()); + } catch (...) { + Log("Console.cpp: Unknown exception showing modal"); + } } - - std::string msgToLog = ss.str(); - - ConsoleAPIType method = VerbosityToInspectorMethod(verbosityLevel); - SendToDevToolsFrontEnd(method, args); - std::string msgWithVerbosity = "CONSOLE " + verbosityLevelUpper + ": " + msgToLog; - Log("%s", msgWithVerbosity.c_str()); + } + std::string verbosityLevelUpper = verbosityLevel; + std::transform(verbosityLevelUpper.begin(), verbosityLevelUpper.end(), + verbosityLevelUpper.begin(), ::toupper); + + std::stringstream ss; + ss << stringResult; + + if (verbosityLevel == "trace") { + std::string stacktrace = tns::GetStackTrace(isolate); + ss << std::endl << stacktrace << std::endl; + } + + std::string msgToLog = ss.str(); + + ConsoleAPIType method = VerbosityToInspectorMethod(verbosityLevel); + SendToDevToolsFrontEnd(method, args); + std::string msgWithVerbosity = + "CONSOLE " + verbosityLevelUpper + ": " + msgToLog; + Log("%s", msgWithVerbosity.c_str()); } void Console::AssertCallback(const FunctionCallbackInfo& args) { - if (!RuntimeConfig.LogToSystemConsole) { - return; - } + if (!RuntimeConfig.LogToSystemConsole) { + return; + } - Isolate* isolate = args.GetIsolate(); + Isolate* isolate = args.GetIsolate(); - int argsLength = args.Length(); - bool expressionPasses = argsLength > 0 && args[0]->BooleanValue(isolate); - if (!expressionPasses) { - std::stringstream ss; + int argsLength = args.Length(); + bool expressionPasses = argsLength > 0 && args[0]->BooleanValue(isolate); + if (!expressionPasses) { + std::stringstream ss; - ss << "Assertion failed: "; + ss << "Assertion failed: "; - if (argsLength > 1) { - ss << BuildStringFromArgs(args, 1); - } else { - ss << "console.assert"; - } - - std::string log = ss.str(); - - SendToDevToolsFrontEnd(ConsoleAPIType::kAssert, args); - Log("%s", log.c_str()); + if (argsLength > 1) { + ss << BuildStringFromArgs(args, 1); + } else { + ss << "console.assert"; } -} -void Console::DirCallback(const FunctionCallbackInfo& args) { - if (!RuntimeConfig.LogToSystemConsole) { - return; - } + std::string log = ss.str(); - int argsLen = args.Length(); - Isolate* isolate = args.GetIsolate(); - Local context = isolate->GetCurrentContext(); + SendToDevToolsFrontEnd(ConsoleAPIType::kAssert, args); + Log("%s", log.c_str()); + } +} - std::stringstream ss; - std::string scriptUrl = tns::GetCurrentScriptUrl(isolate); - ss << scriptUrl << ":"; +void Console::DirCallback(const FunctionCallbackInfo& args) { + if (!RuntimeConfig.LogToSystemConsole) { + return; + } + + int argsLen = args.Length(); + Isolate* isolate = args.GetIsolate(); + Local context = isolate->GetCurrentContext(); + + std::stringstream ss; + std::string scriptUrl = tns::GetCurrentScriptUrl(isolate); + ss << scriptUrl << ":"; + + if (argsLen > 0) { + if (!args[0]->IsObject()) { + std::string logString = BuildStringFromArgs(args); + ss << " " << logString; + } else { + ss << std::endl << "==== object dump start ====" << std::endl; + Local argObject = args[0].As(); + + Local propNames; + bool success = argObject->GetPropertyNames(context).ToLocal(&propNames); + tns::Assert(success, isolate); + uint32_t propertiesLength = propNames->Length(); + for (uint32_t i = 0; i < propertiesLength; i++) { + Local propertyName = propNames->Get(context, i).ToLocalChecked(); + Local propertyValue; + bool success = + argObject->Get(context, propertyName).ToLocal(&propertyValue); + if (!success || propertyValue.IsEmpty() || + propertyValue->IsUndefined()) { + continue; + } - if (argsLen > 0) { - if (!args[0]->IsObject()) { - std::string logString = BuildStringFromArgs(args); - ss << " " << logString; + bool propIsFunction = propertyValue->IsFunction(); + + ss << tns::ToString(isolate, + propertyName->ToString(context).ToLocalChecked()) + << ": "; + + if (propIsFunction) { + ss << "()"; + } else if (propertyValue->IsArray()) { + Local stringResult = + BuildStringFromArg(context, propertyValue); + std::string jsonStringifiedArray = + tns::ToString(isolate, stringResult); + ss << jsonStringifiedArray; + } else if (propertyValue->IsObject()) { + Local obj = propertyValue->ToObject(context).ToLocalChecked(); + Local objString = TransformJSObject(obj); + std::string jsonStringifiedObject = tns::ToString(isolate, objString); + // if object prints out as the error string for circular references, + // replace with #CR instead for brevity + if (jsonStringifiedObject.find("circular structure") != + std::string::npos) { + jsonStringifiedObject = "#CR"; + } + ss << jsonStringifiedObject; } else { - ss << std::endl << "==== object dump start ====" << std::endl; - Local argObject = args[0].As(); - - Local propNames; - bool success = argObject->GetPropertyNames(context).ToLocal(&propNames); - tns::Assert(success, isolate); - uint32_t propertiesLength = propNames->Length(); - for (uint32_t i = 0; i < propertiesLength; i++) { - Local propertyName = propNames->Get(context, i).ToLocalChecked(); - Local propertyValue; - bool success = argObject->Get(context, propertyName).ToLocal(&propertyValue); - if (!success || propertyValue.IsEmpty() || propertyValue->IsUndefined()) { - continue; - } - - bool propIsFunction = propertyValue->IsFunction(); - - ss << tns::ToString(isolate, propertyName->ToString(context).ToLocalChecked()) << ": "; - - if (propIsFunction) { - ss << "()"; - } else if (propertyValue->IsArray()) { - Local stringResult = BuildStringFromArg(context, propertyValue); - std::string jsonStringifiedArray = tns::ToString(isolate, stringResult); - ss << jsonStringifiedArray; - } else if (propertyValue->IsObject()) { - Local obj = propertyValue->ToObject(context).ToLocalChecked(); - Local objString = TransformJSObject(obj); - std::string jsonStringifiedObject = tns::ToString(isolate, objString); - // if object prints out as the error string for circular references, replace with #CR instead for brevity - if (jsonStringifiedObject.find("circular structure") != std::string::npos) { - jsonStringifiedObject = "#CR"; - } - ss << jsonStringifiedObject; - } else { - ss << "\"" << tns::ToString(isolate, propertyValue->ToDetailString(context).ToLocalChecked()) << "\""; - } - - ss << std::endl; - } - - ss << "==== object dump end ====" << std::endl; + ss << "\"" + << tns::ToString( + isolate, + propertyValue->ToDetailString(context).ToLocalChecked()) + << "\""; } - } else { - ss << ""; + + ss << std::endl; + } + + ss << "==== object dump end ====" << std::endl; } + } else { + ss << ""; + } - std::string msgToLog = ss.str(); - SendToDevToolsFrontEnd(ConsoleAPIType::kDir, args); - Log("%s", msgToLog.c_str()); + std::string msgToLog = ss.str(); + SendToDevToolsFrontEnd(ConsoleAPIType::kDir, args); + Log("%s", msgToLog.c_str()); } void Console::TimeCallback(const FunctionCallbackInfo& args) { - if (!RuntimeConfig.LogToSystemConsole) { - return; - } + if (!RuntimeConfig.LogToSystemConsole) { + return; + } - Isolate* isolate = args.GetIsolate(); - Local context = isolate->GetCurrentContext(); - std::string label = "default"; + Isolate* isolate = args.GetIsolate(); + Local context = isolate->GetCurrentContext(); + std::string label = "default"; - Local labelString; - if (args.Length() > 0 && args[0]->ToString(context).ToLocal(&labelString)) { - label = tns::ToString(isolate, labelString); - } + Local labelString; + if (args.Length() > 0 && args[0]->ToString(context).ToLocal(&labelString)) { + label = tns::ToString(isolate, labelString); + } - std::shared_ptr cache = Caches::Get(isolate); + std::shared_ptr cache = Caches::Get(isolate); - auto nano = std::chrono::time_point_cast(std::chrono::system_clock::now()); - double timeStamp = nano.time_since_epoch().count(); + auto nano = std::chrono::time_point_cast( + std::chrono::system_clock::now()); + double timeStamp = nano.time_since_epoch().count(); - cache->Timers.emplace(label, timeStamp); + cache->Timers.emplace(label, timeStamp); } void Console::TimeEndCallback(const FunctionCallbackInfo& args) { - if (!RuntimeConfig.LogToSystemConsole) { - return; - } - - Isolate* isolate = args.GetIsolate(); - Local context = isolate->GetCurrentContext(); - std::string label = "default"; - - Local labelString; - if (args.Length() > 0 && args[0]->ToString(context).ToLocal(&labelString)) { - label = tns::ToString(isolate, labelString); - } - - std::shared_ptr cache = Caches::Get(isolate); - auto itTimersMap = cache->Timers.find(label); - if (itTimersMap == cache->Timers.end()) { - std::string warning = std::string("No such label '" + label + "' for console.timeEnd()"); - Log("%s", warning.c_str()); - return; - } - - auto nano = std::chrono::time_point_cast(std::chrono::system_clock::now()); - double endTimeStamp = nano.time_since_epoch().count(); - double startTimeStamp = itTimersMap->second; - - cache->Timers.erase(label); - - double diffMicroseconds = endTimeStamp - startTimeStamp; - double diffMilliseconds = diffMicroseconds / 1000.0; - - std::stringstream ss; - ss << "CONSOLE INFO " << label << ": " << std::fixed << std::setprecision(3) << diffMilliseconds << "ms" ; - - std::string msgToLog = ss.str(); - SendToDevToolsFrontEnd(isolate, ConsoleAPIType::kTimeEnd, msgToLog); - Log("%s", msgToLog.c_str()); + if (!RuntimeConfig.LogToSystemConsole) { + return; + } + + Isolate* isolate = args.GetIsolate(); + Local context = isolate->GetCurrentContext(); + std::string label = "default"; + + Local labelString; + if (args.Length() > 0 && args[0]->ToString(context).ToLocal(&labelString)) { + label = tns::ToString(isolate, labelString); + } + + std::shared_ptr cache = Caches::Get(isolate); + auto itTimersMap = cache->Timers.find(label); + if (itTimersMap == cache->Timers.end()) { + std::string warning = + std::string("No such label '" + label + "' for console.timeEnd()"); + Log("%s", warning.c_str()); + return; + } + + auto nano = std::chrono::time_point_cast( + std::chrono::system_clock::now()); + double endTimeStamp = nano.time_since_epoch().count(); + double startTimeStamp = itTimersMap->second; + + cache->Timers.erase(label); + + double diffMicroseconds = endTimeStamp - startTimeStamp; + double diffMilliseconds = diffMicroseconds / 1000.0; + + std::stringstream ss; + ss << "CONSOLE INFO " << label << ": " << std::fixed << std::setprecision(3) + << diffMilliseconds << "ms"; + + std::string msgToLog = ss.str(); + SendToDevToolsFrontEnd(isolate, ConsoleAPIType::kTimeEnd, msgToLog); + Log("%s", msgToLog.c_str()); } -void Console::AttachLogFunction(Local context, Local console, const std::string name, v8::FunctionCallback callback) { - Isolate* isolate = context->GetIsolate(); - - Local func; - if (!Function::New(context, callback, tns::ToV8String(isolate, name), 0, ConstructorBehavior::kThrow).ToLocal(&func)) { - tns::Assert(false, isolate); - } - - Local logFuncName = tns::ToV8String(isolate, name); - func->SetName(logFuncName); - if (!console->CreateDataProperty(context, logFuncName, func).FromMaybe(false)) { - tns::Assert(false, isolate); - } +void Console::AttachLogFunction(Local context, Local console, + const std::string name, + v8::FunctionCallback callback) { + Isolate* isolate = context->GetIsolate(); + + Local func; + if (!Function::New(context, callback, tns::ToV8String(isolate, name), 0, + ConstructorBehavior::kThrow) + .ToLocal(&func)) { + tns::Assert(false, isolate); + } + + Local logFuncName = tns::ToV8String(isolate, name); + func->SetName(logFuncName); + if (!console->CreateDataProperty(context, logFuncName, func) + .FromMaybe(false)) { + tns::Assert(false, isolate); + } } -std::string Console::BuildStringFromArgs(const FunctionCallbackInfo& args, int startingIndex) { - Isolate* isolate = args.GetIsolate(); - Local context = isolate->GetCurrentContext(); - int argLen = args.Length(); - std::stringstream ss; +std::string Console::BuildStringFromArgs( + const FunctionCallbackInfo& args, int startingIndex) { + Isolate* isolate = args.GetIsolate(); + Local context = isolate->GetCurrentContext(); + int argLen = args.Length(); + std::stringstream ss; - if (argLen > 0) { - for (int i = startingIndex; i < argLen; i++) { - Local argString; + if (argLen > 0) { + for (int i = startingIndex; i < argLen; i++) { + Local argString; - argString = BuildStringFromArg(context, args[i]); + argString = BuildStringFromArg(context, args[i]); - // separate args with a space - if (i != startingIndex) { - ss << " "; - } + // separate args with a space + if (i != startingIndex) { + ss << " "; + } - ss << tns::ToString(isolate, argString); - } - } else { - ss << std::endl; + ss << tns::ToString(isolate, argString); } + } else { + ss << std::endl; + } - std::string stringResult = ss.str(); - return stringResult; + std::string stringResult = ss.str(); + return stringResult; } -const Local Console::BuildStringFromArg(Local context, const Local& val) { - Isolate* isolate = context->GetIsolate(); - Local argString; - if (val->IsFunction()) { - bool success = val->ToDetailString(context).ToLocal(&argString); - tns::Assert(success, isolate); - } else if (val->IsArray()) { - Local cachedSelf = val; - Local array = val->ToObject(context).ToLocalChecked(); - Local arrayEntryKeys = array->GetPropertyNames(context).ToLocalChecked(); +const Local Console::BuildStringFromArg(Local context, + const Local& val) { + Isolate* isolate = context->GetIsolate(); + Local argString; + if (val->IsFunction()) { + bool success = val->ToDetailString(context).ToLocal(&argString); + tns::Assert(success, isolate); + } else if (val->IsArray()) { + Local cachedSelf = val; + Local array = val->ToObject(context).ToLocalChecked(); + Local arrayEntryKeys = + array->GetPropertyNames(context).ToLocalChecked(); - uint32_t arrayLength = arrayEntryKeys->Length(); + uint32_t arrayLength = arrayEntryKeys->Length(); - argString = tns::ToV8String(isolate, "["); + argString = tns::ToV8String(isolate, "["); - for (int i = 0; i < arrayLength; i++) { - Local propertyName = arrayEntryKeys->Get(context, i).ToLocalChecked(); + for (int i = 0; i < arrayLength; i++) { + Local propertyName = + arrayEntryKeys->Get(context, i).ToLocalChecked(); - Local propertyValue = array->Get(context, propertyName).ToLocalChecked(); + Local propertyValue = + array->Get(context, propertyName).ToLocalChecked(); - // avoid bottomless recursion with cyclic reference to the same array - if (propertyValue->StrictEquals(cachedSelf)) { - argString = v8::String::Concat(isolate, argString, tns::ToV8String(isolate, "[Circular]")); - continue; - } + // avoid bottomless recursion with cyclic reference to the same array + if (propertyValue->StrictEquals(cachedSelf)) { + argString = v8::String::Concat(isolate, argString, + tns::ToV8String(isolate, "[Circular]")); + continue; + } - Local objectString = BuildStringFromArg(context, propertyValue); + Local objectString = + BuildStringFromArg(context, propertyValue); - argString = v8::String::Concat(isolate, argString, objectString); + argString = v8::String::Concat(isolate, argString, objectString); - if (i != arrayLength - 1) { - argString = v8::String::Concat(isolate, argString, tns::ToV8String(isolate, ", ")); - } - } + if (i != arrayLength - 1) { + argString = v8::String::Concat(isolate, argString, + tns::ToV8String(isolate, ", ")); + } + } - argString = v8::String::Concat(isolate, argString, tns::ToV8String(isolate, "]")); - } else if (val->IsObject()) { - Local obj = val.As(); + argString = + v8::String::Concat(isolate, argString, tns::ToV8String(isolate, "]")); + } else if (val->IsObject()) { + Local obj = val.As(); - argString = TransformJSObject(obj); - } else { - bool success = val->ToDetailString(isolate->GetCurrentContext()).ToLocal(&argString); - tns::Assert(success, isolate); - } + argString = TransformJSObject(obj); + } else { + bool success = + val->ToDetailString(isolate->GetCurrentContext()).ToLocal(&argString); + tns::Assert(success, isolate); + } - return argString; + return argString; } const Local Console::TransformJSObject(Local object) { - Local context; - bool success = object->GetCreationContext().ToLocal(&context); - tns::Assert(success); - Isolate* isolate = context->GetIsolate(); - Local value; - { - TryCatch tc(isolate); - bool success = object->ToString(context).ToLocal(&value); - if (!success) { - return tns::ToV8String(isolate, ""); - } + Local context; + bool success = object->GetCreationContext().ToLocal(&context); + tns::Assert(success); + Isolate* isolate = context->GetIsolate(); + Local value; + { + TryCatch tc(isolate); + bool success = object->ToString(context).ToLocal(&value); + if (!success) { + return tns::ToV8String(isolate, ""); } - Local objToString = value.As(); + } + Local objToString = value.As(); - Local resultString; - bool hasCustomToStringImplementation = tns::ToString(isolate, objToString).find("[object Object]") == std::string::npos; + Local resultString; + bool hasCustomToStringImplementation = + tns::ToString(isolate, objToString).find("[object Object]") == + std::string::npos; - if (hasCustomToStringImplementation) { - resultString = objToString; - } else { - resultString = tns::JsonStringifyObject(context, object); - } + if (hasCustomToStringImplementation) { + resultString = objToString; + } else { + resultString = tns::JsonStringifyObject(context, object); + } - return resultString; + return resultString; } -v8_inspector::ConsoleAPIType Console::VerbosityToInspectorMethod(const std::string level) { - if (level == "error") { - return ConsoleAPIType::kError; - } else if (level == "warn") { - return ConsoleAPIType::kWarning; - } else if (level == "info") { - return ConsoleAPIType::kInfo; - } else if (level == "trace") { - return ConsoleAPIType::kTrace; - } - - assert(level == "log"); - return ConsoleAPIType::kLog; +v8_inspector::ConsoleAPIType Console::VerbosityToInspectorMethod( + const std::string level) { + if (level == "error") { + return ConsoleAPIType::kError; + } else if (level == "warn") { + return ConsoleAPIType::kWarning; + } else if (level == "info") { + return ConsoleAPIType::kInfo; + } else if (level == "trace") { + return ConsoleAPIType::kTrace; + } + + assert(level == "log"); + return ConsoleAPIType::kLog; } -void Console::SendToDevToolsFrontEnd(ConsoleAPIType method, - const v8::FunctionCallbackInfo& args) { - if (!inspector) { - return; - } - - std::vector> arg_vector; - unsigned nargs = args.Length(); - arg_vector.reserve(nargs); - for (unsigned ix = 0; ix < nargs; ix++) - arg_vector.push_back(args[ix]); - - inspector->consoleLog(args.GetIsolate(), method, arg_vector); -} +void Console::SendToDevToolsFrontEnd( + ConsoleAPIType method, const v8::FunctionCallbackInfo& args) { + if (!inspector) { + return; + } -void Console::SendToDevToolsFrontEnd(v8::Isolate* isolate, ConsoleAPIType method, const std::string& msg) { - if (!inspector) { - return; - } + std::vector> arg_vector; + unsigned nargs = args.Length(); + arg_vector.reserve(nargs); + for (unsigned ix = 0; ix < nargs; ix++) arg_vector.push_back(args[ix]); + + inspector->consoleLog(args.GetIsolate(), method, arg_vector); +} - v8::Local v8str = v8::String::NewFromUtf8( - isolate, msg.c_str(), v8::NewStringType::kNormal, -1).ToLocalChecked(); - std::vector> args{v8str}; - inspector->consoleLog(isolate, method, args); +void Console::SendToDevToolsFrontEnd(v8::Isolate* isolate, + ConsoleAPIType method, + const std::string& msg) { + if (!inspector) { + return; + } + + v8::Local v8str = + v8::String::NewFromUtf8(isolate, msg.c_str(), v8::NewStringType::kNormal, + -1) + .ToLocalChecked(); + std::vector> args{v8str}; + inspector->consoleLog(isolate, method, args); } v8_inspector::JsV8InspectorClient* Console::inspector = nullptr; -} +} // namespace tns diff --git a/NativeScript/runtime/FFICall.h b/NativeScript/runtime/FFICall.h index c2f35549..46666405 100644 --- a/NativeScript/runtime/FFICall.h +++ b/NativeScript/runtime/FFICall.h @@ -2,117 +2,127 @@ #define FFICall_h #include + #include -#include "robin_hood.h" -#include "Metadata.h" + #include "DataWrapper.h" +#include "Metadata.h" #include "libffi.h" +#include "robin_hood.h" namespace tns { class BaseCall { -public: - BaseCall(uint8_t* buffer, size_t returnOffset = 0) - : buffer_(buffer), - returnOffset_(returnOffset) { - } + public: + BaseCall(uint8_t* buffer, size_t returnOffset = 0) + : buffer_(buffer), returnOffset_(returnOffset) {} - ~BaseCall() { - } + ~BaseCall() {} - inline void* ResultBuffer() { - return this->buffer_ + this->returnOffset_; - } + inline void* ResultBuffer() { return this->buffer_ + this->returnOffset_; } - template - inline T& GetResult() { - return *static_cast(this->ResultBuffer()); - } -protected: - uint8_t* buffer_; - size_t returnOffset_; + template + inline T& GetResult() { + return *static_cast(this->ResultBuffer()); + } + + protected: + uint8_t* buffer_; + size_t returnOffset_; }; class ParametrizedCall { -public: - ParametrizedCall(ffi_cif* cif) - : Cif(cif), - ReturnOffset(0), - StackSize(0) { - unsigned int argsCount = cif->nargs; - this->StackSize = 0; - - if (argsCount > 0) { - this->StackSize = malloc_good_size(sizeof(void* [argsCount])); - } - - this->ReturnOffset = this->StackSize; - - this->StackSize += malloc_good_size(std::max(cif->rtype->size, sizeof(ffi_arg))); - - this->ArgValueOffsets.reserve(argsCount); - for (size_t i = 0; i < argsCount; i++) { - this->ArgValueOffsets.push_back(this->StackSize); - ffi_type* argType = cif->arg_types[i]; - this->StackSize += malloc_good_size(std::max(argType->size, sizeof(ffi_arg))); - } + public: + ParametrizedCall(ffi_cif* cif) : Cif(cif), ReturnOffset(0), StackSize(0) { + unsigned int argsCount = cif->nargs; + this->StackSize = 0; + + if (argsCount > 0) { + // compute total bytes = number of pointers × size of a pointer + size_t needed = sizeof(void*) * static_cast(argsCount); + this->StackSize = malloc_good_size(needed); } - static ParametrizedCall* Get(const TypeEncoding* typeEncoding, const int initialParameterIndex, const int argsCount); + this->ReturnOffset = this->StackSize; - ffi_cif* Cif; - size_t ReturnOffset; - size_t StackSize; - std::vector ArgValueOffsets; -private: - static robin_hood::unordered_map callsCache_; -}; + this->StackSize += + malloc_good_size(std::max(cif->rtype->size, sizeof(ffi_arg))); -class FFICall: public BaseCall { -public: - FFICall(ParametrizedCall* parametrizedCall): BaseCall(nullptr) { - this->returnOffset_ = parametrizedCall->ReturnOffset; - this->useDynamicBuffer_ = parametrizedCall->StackSize > 512; - if(this->useDynamicBuffer_) { - this->buffer_ = reinterpret_cast(malloc(parametrizedCall->StackSize)); - } else { - this->buffer_ = reinterpret_cast(this->staticBuffer); - } - - this->argsArray_ = reinterpret_cast(this->buffer_); - for (size_t i = 0; i < parametrizedCall->Cif->nargs; i++) { - this->argsArray_[i] = this->buffer_ + parametrizedCall->ArgValueOffsets[i]; - } + this->ArgValueOffsets.reserve(argsCount); + for (size_t i = 0; i < argsCount; i++) { + this->ArgValueOffsets.push_back(this->StackSize); + ffi_type* argType = cif->arg_types[i]; + this->StackSize += + malloc_good_size(std::max(argType->size, sizeof(ffi_arg))); } + } - ~FFICall() { - if(this->useDynamicBuffer_) { - free(this->buffer_); - } - } + static ParametrizedCall* Get(const TypeEncoding* typeEncoding, + const int initialParameterIndex, + const int argsCount); - /** - When calling this, always make another call to DisposeFFIType with the same parameters - */ - static ffi_type* GetArgumentType(const TypeEncoding* typeEncoding, bool isStructMember = false); - static void DisposeFFIType(ffi_type* type, const TypeEncoding* typeEncoding); - static StructInfo GetStructInfo(const StructMeta* structMeta, std::string structName = ""); - static StructInfo GetStructInfo(size_t fieldsCount, const TypeEncoding* fieldEncoding, const String* fieldNames, std::string structName = ""); + ffi_cif* Cif; + size_t ReturnOffset; + size_t StackSize; + std::vector ArgValueOffsets; + + private: + static robin_hood::unordered_map + callsCache_; +}; + +class FFICall : public BaseCall { + public: + FFICall(ParametrizedCall* parametrizedCall) : BaseCall(nullptr) { + this->returnOffset_ = parametrizedCall->ReturnOffset; + this->useDynamicBuffer_ = parametrizedCall->StackSize > 512; + if (this->useDynamicBuffer_) { + this->buffer_ = + reinterpret_cast(malloc(parametrizedCall->StackSize)); + } else { + this->buffer_ = reinterpret_cast(this->staticBuffer); + } - inline void* ArgumentBuffer(unsigned index) { - return this->argsArray_[index]; + this->argsArray_ = reinterpret_cast(this->buffer_); + for (size_t i = 0; i < parametrizedCall->Cif->nargs; i++) { + this->argsArray_[i] = + this->buffer_ + parametrizedCall->ArgValueOffsets[i]; } + } - inline void** ArgsArray() { - return this->argsArray_; + ~FFICall() { + if (this->useDynamicBuffer_) { + free(this->buffer_); } -private: - static robin_hood::unordered_map structInfosCache_; - void** argsArray_; - bool useDynamicBuffer_; - uint8_t staticBuffer[512]; + } + + /** + When calling this, always make another call to DisposeFFIType with the same + parameters + */ + static ffi_type* GetArgumentType(const TypeEncoding* typeEncoding, + bool isStructMember = false); + static void DisposeFFIType(ffi_type* type, const TypeEncoding* typeEncoding); + static StructInfo GetStructInfo(const StructMeta* structMeta, + std::string structName = ""); + static StructInfo GetStructInfo(size_t fieldsCount, + const TypeEncoding* fieldEncoding, + const String* fieldNames, + std::string structName = ""); + + inline void* ArgumentBuffer(unsigned index) { + return this->argsArray_[index]; + } + + inline void** ArgsArray() { return this->argsArray_; } + + private: + static robin_hood::unordered_map structInfosCache_; + void** argsArray_; + bool useDynamicBuffer_; + uint8_t staticBuffer[512]; }; -} +} // namespace tns #endif /* FFICall_h */ diff --git a/NativeScript/runtime/Helpers.h b/NativeScript/runtime/Helpers.h index dc65a88d..0b6f29dd 100644 --- a/NativeScript/runtime/Helpers.h +++ b/NativeScript/runtime/Helpers.h @@ -3,9 +3,10 @@ #include #include + +#include "ArcMacro.h" #include "Common.h" #include "DataWrapper.h" -#include "ArcMacro.h" #ifdef __OBJC__ #include @@ -16,168 +17,199 @@ extern "C" void NSLog(CFStringRef format, ...); namespace tns { -inline v8::Local ToV8String(v8::Isolate* isolate,const std::string& value) { - return v8::String::NewFromUtf8(isolate, value.c_str(), v8::NewStringType::kNormal, (int)value.length()).ToLocalChecked(); +inline v8::Local ToV8String(v8::Isolate* isolate, + const std::string& value) { + return v8::String::NewFromUtf8(isolate, value.c_str(), + v8::NewStringType::kNormal, + (int)value.length()) + .ToLocalChecked(); } - -inline v8::Local ToV8String(v8::Isolate* isolate,const char* value, int length) { - return v8::String::NewFromUtf8(isolate, value, v8::NewStringType::kNormal, length).ToLocalChecked(); +inline v8::Local ToV8String(v8::Isolate* isolate, const char* value, + int length) { + return v8::String::NewFromUtf8(isolate, value, v8::NewStringType::kNormal, + length) + .ToLocalChecked(); } #ifdef __OBJC__ -inline v8::Local ToV8String(v8::Isolate* isolate,const NSString* value) { - /* - // TODO: profile if this is faster - // maybe have multiple conversion - if([value fastestEncoding] == NSUTF16StringEncoding) { - uint16_t static_buffer[256]; - uint16_t* targetBuffer = static_buffer; - bool isDynamic = false; - auto length = [value maximumLengthOfBytesUsingEncoding:NSUTF16StringEncoding]; - auto numberOfBytes = length * sizeof(uint16_t); - if (length > 256) { - targetBuffer = (uint16_t*)malloc(numberOfBytes); - isDynamic = true; - } - NSUInteger usedLength = 0; - NSRange range = NSMakeRange(0, [value length]); - [value getBytes:targetBuffer maxLength:numberOfBytes usedLength:&usedLength encoding:NSUTF16StringEncoding options:0 range:range remainingRange:NULL]; - - auto result = v8::String::NewFromTwoByte(isolate, targetBuffer, v8::NewStringType::kNormal, (int)[value length]).ToLocalChecked(); - if (isDynamic) { - free(targetBuffer); - } - return result; - } - */ - return v8::String::NewFromUtf8(isolate, [value UTF8String], v8::NewStringType::kNormal, (int)[value lengthOfBytesUsingEncoding:NSUTF8StringEncoding]).ToLocalChecked(); +inline v8::Local ToV8String(v8::Isolate* isolate, + const NSString* value) { + /* + // TODO: profile if this is faster + // maybe have multiple conversion + if([value fastestEncoding] == NSUTF16StringEncoding) { + uint16_t static_buffer[256]; + uint16_t* targetBuffer = static_buffer; + bool isDynamic = false; + auto length = [value + maximumLengthOfBytesUsingEncoding:NSUTF16StringEncoding]; auto numberOfBytes = + length * sizeof(uint16_t); if (length > 256) { targetBuffer = + (uint16_t*)malloc(numberOfBytes); isDynamic = true; + } + NSUInteger usedLength = 0; + NSRange range = NSMakeRange(0, [value length]); + [value getBytes:targetBuffer maxLength:numberOfBytes + usedLength:&usedLength encoding:NSUTF16StringEncoding options:0 range:range + remainingRange:NULL]; + + auto result = v8::String::NewFromTwoByte(isolate, targetBuffer, + v8::NewStringType::kNormal, (int)[value length]).ToLocalChecked(); if + (isDynamic) { free(targetBuffer); + } + return result; + } + */ + return v8::String::NewFromUtf8( + isolate, [value UTF8String], v8::NewStringType::kNormal, + (int)[value lengthOfBytesUsingEncoding:NSUTF8StringEncoding]) + .ToLocalChecked(); } #endif -inline std::string ToString(v8::Isolate* isolate, const v8::Local& value) { - if (value.IsEmpty()) { - return std::string(); - } +inline std::string ToString(v8::Isolate* isolate, + const v8::Local& value) { + if (value.IsEmpty()) { + return std::string(); + } - if (value->IsStringObject()) { - v8::Local obj = value.As()->ValueOf(); - return tns::ToString(isolate, obj); - } + if (value->IsStringObject()) { + v8::Local obj = value.As()->ValueOf(); + return tns::ToString(isolate, obj); + } - v8::String::Utf8Value result(isolate, value); + v8::String::Utf8Value result(isolate, value); - const char* val = *result; - if (val == nullptr) { - return std::string(); - } + const char* val = *result; + if (val == nullptr) { + return std::string(); + } - return std::string(*result, result.length()); + return std::string(*result, result.length()); } #ifdef __OBJC__ inline NSString* ToNSString(const std::string& v) { - return [[[NSString alloc] initWithBytes:v.c_str() length:v.length() encoding:NSUTF8StringEncoding] S_AUTORELEASE]; + return [[[NSString alloc] initWithBytes:v.c_str() + length:v.length() + encoding:NSUTF8StringEncoding] S_AUTORELEASE]; } -// this method is a copy of ToString to avoid needless std::string<->NSString conversions -inline NSString* ToNSString(v8::Isolate* isolate, const v8::Local& value) { - if (value.IsEmpty()) { - return @""; - } +// this method is a copy of ToString to avoid needless std::string<->NSString +// conversions +inline NSString* ToNSString(v8::Isolate* isolate, + const v8::Local& value) { + if (value.IsEmpty()) { + return @""; + } - if (value->IsStringObject()) { - v8::Local obj = value.As()->ValueOf(); - return ToNSString(isolate, obj); - } + if (value->IsStringObject()) { + v8::Local obj = value.As()->ValueOf(); + return ToNSString(isolate, obj); + } - v8::String::Utf8Value result(isolate, value); + v8::String::Utf8Value result(isolate, value); - const char* val = *result; - if (val == nullptr) { - return @""; - } + const char* val = *result; + if (val == nullptr) { + return @""; + } - return [[[NSString alloc] initWithBytes:*result length:result.length() encoding:NSUTF8StringEncoding] S_AUTORELEASE]; - + return [[[NSString alloc] initWithBytes:*result + length:result.length() + encoding:NSUTF8StringEncoding] S_AUTORELEASE]; } #endif -std::u16string ToUtf16String(v8::Isolate* isolate, const v8::Local& value); -inline double ToNumber(v8::Isolate* isolate, const v8::Local& value) { - double result = NAN; +std::u16string ToUtf16String(v8::Isolate* isolate, + const v8::Local& value); +inline double ToNumber(v8::Isolate* isolate, + const v8::Local& value) { + double result = NAN; - if (value.IsEmpty()) { - return result; - } + if (value.IsEmpty()) { + return result; + } - if (value->IsNumberObject()) { - result = value.As()->ValueOf(); - } else if (value->IsNumber()) { - result = value.As()->Value(); - } else { - v8::Local number; - v8::Local context = isolate->GetCurrentContext(); - bool success = value->ToNumber(context).ToLocal(&number); - if (success) { - result = number->Value(); - } + if (value->IsNumberObject()) { + result = value.As()->ValueOf(); + } else if (value->IsNumber()) { + result = value.As()->Value(); + } else { + v8::Local number; + v8::Local context = isolate->GetCurrentContext(); + bool success = value->ToNumber(context).ToLocal(&number); + if (success) { + result = number->Value(); } + } - return result; + return result; } inline bool ToBool(const v8::Local& value) { - bool result = false; + bool result = false; - if (value.IsEmpty()) { - return result; - } + if (value.IsEmpty()) { + return result; + } - if (value->IsBooleanObject()) { - result = value.As()->ValueOf(); - } else if (value->IsBoolean()) { - result = value.As()->Value(); - } + if (value->IsBooleanObject()) { + result = value.As()->ValueOf(); + } else if (value->IsBoolean()) { + result = value.As()->Value(); + } - return result; + return result; } std::vector ToVector(const std::string& value); bool Exists(const char* fullPath); -v8::Local ReadModule(v8::Isolate* isolate, const std::string &filePath); +v8::Local ReadModule(v8::Isolate* isolate, + const std::string& filePath); const char* ReadText(const std::string& filePath, long& length, bool& isNew); std::string ReadText(const std::string& file); uint8_t* ReadBinary(const std::string path, long& length, bool& isNew); bool WriteBinary(const std::string& path, const void* data, long length); -void SetPrivateValue(const v8::Local& obj, const v8::Local& propName, const v8::Local& value); -v8::Local GetPrivateValue(const v8::Local& obj, const v8::Local& propName); +void SetPrivateValue(const v8::Local& obj, + const v8::Local& propName, + const v8::Local& value); +v8::Local GetPrivateValue(const v8::Local& obj, + const v8::Local& propName); -void SetValue(v8::Isolate* isolate, const v8::Local& obj, BaseDataWrapper* value); -BaseDataWrapper* GetValue(v8::Isolate* isolate, const v8::Local& val); +void SetValue(v8::Isolate* isolate, const v8::Local& obj, + BaseDataWrapper* value); +BaseDataWrapper* GetValue(v8::Isolate* isolate, + const v8::Local& val); void DeleteValue(v8::Isolate* isolate, const v8::Local& val); -bool DeleteWrapperIfUnused(v8::Isolate* isolate, const v8::Local& obj, BaseDataWrapper* value); -std::vector> ArgsToVector(const v8::FunctionCallbackInfo& info); +bool DeleteWrapperIfUnused(v8::Isolate* isolate, + const v8::Local& obj, + BaseDataWrapper* value); +std::vector> ArgsToVector( + const v8::FunctionCallbackInfo& info); inline bool IsString(const v8::Local& value) { - return !value.IsEmpty() && (value->IsString() || value->IsStringObject()); + return !value.IsEmpty() && (value->IsString() || value->IsStringObject()); } inline bool IsNumber(const v8::Local& value) { - return !value.IsEmpty() && (value->IsNumber() || value->IsNumberObject()); + return !value.IsEmpty() && (value->IsNumber() || value->IsNumberObject()); } inline bool IsBigInt(const v8::Local& value) { - return !value.IsEmpty() && (value->IsBigInt() || value->IsBigIntObject()); + return !value.IsEmpty() && (value->IsBigInt() || value->IsBigIntObject()); } inline bool IsBool(const v8::Local& value) { - return !value.IsEmpty() && (value->IsBoolean() || value->IsBooleanObject()); + return !value.IsEmpty() && (value->IsBoolean() || value->IsBooleanObject()); } +bool IsArrayOrArrayLike(v8::Isolate* isolate, + const v8::Local& value); +void* TryGetBufferFromArrayBuffer(const v8::Local& value, + bool& isArrayBuffer); -bool IsArrayOrArrayLike(v8::Isolate* isolate, const v8::Local& value); -void* TryGetBufferFromArrayBuffer(const v8::Local& value, bool& isArrayBuffer); - -void ExecuteOnRunLoop(CFRunLoopRef queue, std::function func, bool async = true); -void ExecuteOnDispatchQueue(dispatch_queue_t queue, std::function func, bool async = true); -void ExecuteOnMainThread(std::function func, bool async = true); +void ExecuteOnRunLoop(CFRunLoopRef queue, std::function func, + bool async = true); +void ExecuteOnDispatchQueue(dispatch_queue_t queue, std::function func, + bool async = true); +void ExecuteOnMainThread(std::function func, bool async = true); void LogError(v8::Isolate* isolate, v8::TryCatch& tc); void LogBacktrace(int skip = 1); @@ -187,54 +219,57 @@ void LogBacktrace(int skip = 1); #define Log(...) NSLog(__VA_ARGS__) #endif -v8::Local JsonStringifyObject(v8::Local context, v8::Local value, bool handleCircularReferences = true); +v8::Local JsonStringifyObject(v8::Local context, + v8::Local value, + bool handleCircularReferences = true); v8::Local GetSmartJSONStringifyFunction(v8::Isolate* isolate); -std::string ReplaceAll(const std::string source, std::string find, std::string replacement); +std::string ReplaceAll(const std::string source, std::string find, + std::string replacement); -const std::string BuildStacktraceFrameLocationPart(v8::Isolate* isolate, v8::Local frame); -const std::string BuildStacktraceFrameMessage(v8::Isolate* isolate, v8::Local frame); +const std::string BuildStacktraceFrameLocationPart( + v8::Isolate* isolate, v8::Local frame); +const std::string BuildStacktraceFrameMessage(v8::Isolate* isolate, + v8::Local frame); const std::string GetStackTrace(v8::Isolate* isolate); const std::string GetCurrentScriptUrl(v8::Isolate* isolate); +std::string RemapStackTrace(v8::Isolate* isolate, + const std::string& stackTrace); bool LiveSync(v8::Isolate* isolate); -void Assert(bool condition, v8::Isolate* isolate = nullptr, std::string const &reason = std::string()); +void Assert(bool condition, v8::Isolate* isolate = nullptr, + std::string const& reason = std::string()); void StopExecutionAndLogStackTrace(v8::Isolate* isolate); - - - // Helpers from Node inline v8::Local OneByteString(v8::Isolate* isolate, - const char* data, - int length) { - return v8::String::NewFromOneByte(isolate, - reinterpret_cast(data), - v8::NewStringType::kNormal, - length).ToLocalChecked(); + const char* data, int length) { + return v8::String::NewFromOneByte(isolate, + reinterpret_cast(data), + v8::NewStringType::kNormal, length) + .ToLocalChecked(); } inline v8::Local OneByteString(v8::Isolate* isolate, const signed char* data, int length) { - return v8::String::NewFromOneByte(isolate, - reinterpret_cast(data), - v8::NewStringType::kNormal, - length).ToLocalChecked(); + return v8::String::NewFromOneByte(isolate, + reinterpret_cast(data), + v8::NewStringType::kNormal, length) + .ToLocalChecked(); } inline v8::Local OneByteString(v8::Isolate* isolate, const unsigned char* data, int length) { - return v8::String::NewFromOneByte( - isolate, data, v8::NewStringType::kNormal, length) - .ToLocalChecked(); + return v8::String::NewFromOneByte(isolate, data, v8::NewStringType::kNormal, + length) + .ToLocalChecked(); } // Convenience wrapper around v8::String::NewFromOneByte(). inline v8::Local OneByteString(v8::Isolate* isolate, - const char* data, - int length = -1); + const char* data, int length = -1); // For the people that compile with -funsigned-char. inline v8::Local OneByteString(v8::Isolate* isolate, const signed char* data, @@ -243,119 +278,92 @@ inline v8::Local OneByteString(v8::Isolate* isolate, const unsigned char* data, int length = -1); - - v8::Local NewFunctionTemplate( - v8::Isolate* isolate, - v8::FunctionCallback callback, - v8::Local data = v8::Local(), - v8::Local signature = v8::Local(), - v8::ConstructorBehavior behavior = v8::ConstructorBehavior::kAllow, - v8::SideEffectType side_effect = v8::SideEffectType::kHasSideEffect, - const v8::CFunction* c_function = nullptr); + v8::Isolate* isolate, v8::FunctionCallback callback, + v8::Local data = v8::Local(), + v8::Local signature = v8::Local(), + v8::ConstructorBehavior behavior = v8::ConstructorBehavior::kAllow, + v8::SideEffectType side_effect = v8::SideEffectType::kHasSideEffect, + const v8::CFunction* c_function = nullptr); // Convenience methods for NewFunctionTemplate(). -void SetMethod(v8::Local context, - v8::Local that, - const char* name, - v8::FunctionCallback callback, +void SetMethod(v8::Local context, v8::Local that, + const char* name, v8::FunctionCallback callback, v8::Local data = v8::Local()); // Similar to SetProtoMethod but without receiver signature checks. -void SetMethod(v8::Isolate* isolate, - v8::Local that, - const char* name, - v8::FunctionCallback callback, +void SetMethod(v8::Isolate* isolate, v8::Local that, + const char* name, v8::FunctionCallback callback, v8::Local data = v8::Local()); -void SetFastMethod(v8::Isolate* isolate, - v8::Local that, - const char* name, - v8::FunctionCallback slow_callback, +void SetFastMethod(v8::Isolate* isolate, v8::Local that, + const char* name, v8::FunctionCallback slow_callback, const v8::CFunction* c_function, v8::Local data = v8::Local()); -void SetFastMethod(v8::Local context, - v8::Local that, - const char* name, - v8::FunctionCallback slow_callback, +void SetFastMethod(v8::Local context, v8::Local that, + const char* name, v8::FunctionCallback slow_callback, const v8::CFunction* c_function, v8::Local data = v8::Local()); -void SetFastMethodNoSideEffect(v8::Isolate* isolate, - v8::Local that, - const char* name, - v8::FunctionCallback slow_callback, - const v8::CFunction* c_function, - v8::Local data = v8::Local()); -void SetFastMethodNoSideEffect(v8::Local context, - v8::Local that, - const char* name, - v8::FunctionCallback slow_callback, - const v8::CFunction* c_function, - v8::Local data = v8::Local()); -void SetProtoMethod(v8::Isolate* isolate, - v8::Local that, - const char* name, - v8::FunctionCallback callback, +void SetFastMethodNoSideEffect( + v8::Isolate* isolate, v8::Local that, const char* name, + v8::FunctionCallback slow_callback, const v8::CFunction* c_function, + v8::Local data = v8::Local()); +void SetFastMethodNoSideEffect( + v8::Local context, v8::Local that, + const char* name, v8::FunctionCallback slow_callback, + const v8::CFunction* c_function, + v8::Local data = v8::Local()); +void SetProtoMethod(v8::Isolate* isolate, v8::Local that, + const char* name, v8::FunctionCallback callback, v8::Local data = v8::Local()); void SetInstanceMethod(v8::Isolate* isolate, - v8::Local that, - const char* name, + v8::Local that, const char* name, v8::FunctionCallback callback, v8::Local data = v8::Local()); // Safe variants denote the function has no side effects. void SetMethodNoSideEffect(v8::Local context, - v8::Local that, - const char* name, + v8::Local that, const char* name, v8::FunctionCallback callback, v8::Local data = v8::Local()); -void SetProtoMethodNoSideEffect(v8::Isolate* isolate, - v8::Local that, - const char* name, - v8::FunctionCallback callback, - v8::Local data = v8::Local()); -void SetMethodNoSideEffect(v8::Isolate* isolate, - v8::Local that, - const char* name, - v8::FunctionCallback callback, +void SetProtoMethodNoSideEffect( + v8::Isolate* isolate, v8::Local that, + const char* name, v8::FunctionCallback callback, + v8::Local data = v8::Local()); +void SetMethodNoSideEffect(v8::Isolate* isolate, v8::Local that, + const char* name, v8::FunctionCallback callback, v8::Local data = v8::Local()); enum class SetConstructorFunctionFlag { - NONE, - SET_CLASS_NAME, + NONE, + SET_CLASS_NAME, }; void SetConstructorFunction(v8::Local context, - v8::Local that, - const char* name, + v8::Local that, const char* name, v8::Local tmpl, SetConstructorFunctionFlag flag = - SetConstructorFunctionFlag::SET_CLASS_NAME); + SetConstructorFunctionFlag::SET_CLASS_NAME); void SetConstructorFunction(v8::Local context, v8::Local that, v8::Local name, v8::Local tmpl, SetConstructorFunctionFlag flag = - SetConstructorFunctionFlag::SET_CLASS_NAME); -void SetConstructorFunction(v8::Isolate* isolate, - v8::Local that, + SetConstructorFunctionFlag::SET_CLASS_NAME); +void SetConstructorFunction(v8::Isolate* isolate, v8::Local that, const char* name, v8::Local tmpl, SetConstructorFunctionFlag flag = - SetConstructorFunctionFlag::SET_CLASS_NAME); -void SetConstructorFunction(v8::Isolate* isolate, - v8::Local that, + SetConstructorFunctionFlag::SET_CLASS_NAME); +void SetConstructorFunction(v8::Isolate* isolate, v8::Local that, v8::Local name, v8::Local tmpl, SetConstructorFunctionFlag flag = - SetConstructorFunctionFlag::SET_CLASS_NAME); - + SetConstructorFunctionFlag::SET_CLASS_NAME); template -inline v8::Local FIXED_ONE_BYTE_STRING( - v8::Isolate* isolate, - const char(&data)[N]) { +inline v8::Local FIXED_ONE_BYTE_STRING(v8::Isolate* isolate, + const char (&data)[N]) { return OneByteString(isolate, data, N - 1); } template inline v8::Local FIXED_ONE_BYTE_STRING( - v8::Isolate* isolate, - const std::array& arr) { + v8::Isolate* isolate, const std::array& arr) { return OneByteString(isolate, arr.data(), N - 1); } @@ -366,8 +374,7 @@ class PersistentToLocal { // reference to the object. template static inline v8::Local Default( - v8::Isolate* isolate, - const v8::PersistentBase& persistent) { + v8::Isolate* isolate, const v8::PersistentBase& persistent) { if (persistent.IsWeak()) { return PersistentToLocal::Weak(isolate, persistent); } else { @@ -383,19 +390,18 @@ class PersistentToLocal { template static inline v8::Local Strong( const v8::PersistentBase& persistent) { -// DCHECK(!persistent.IsWeak()); + // DCHECK(!persistent.IsWeak()); return *reinterpret_cast*>( const_cast*>(&persistent)); } template static inline v8::Local Weak( - v8::Isolate* isolate, - const v8::PersistentBase& persistent) { + v8::Isolate* isolate, const v8::PersistentBase& persistent) { return v8::Local::New(isolate, persistent); } }; -} +} // namespace tns #endif /* Helpers_h */ diff --git a/NativeScript/runtime/Helpers.mm b/NativeScript/runtime/Helpers.mm index db93fcb2..f3a6dd3f 100644 --- a/NativeScript/runtime/Helpers.mm +++ b/NativeScript/runtime/Helpers.mm @@ -1,961 +1,910 @@ +#include "Helpers.h" #include +#include #include -#include +#include #include -#include +#include +#include #include +#include #include -#include #include -#include -#include -#include "RuntimeConfig.h" -#include "Runtime.h" -#include "Helpers.h" #include "Caches.h" +#include "NativeScriptException.h" +#include "Runtime.h" +#include "RuntimeConfig.h" using namespace v8; namespace { - const int BUFFER_SIZE = 1024 * 1024; - char* Buffer = new char[BUFFER_SIZE]; - uint8_t* BinBuffer = new uint8_t[BUFFER_SIZE]; +const int BUFFER_SIZE = 1024 * 1024; +char* Buffer = new char[BUFFER_SIZE]; +uint8_t* BinBuffer = new uint8_t[BUFFER_SIZE]; } std::u16string tns::ToUtf16String(Isolate* isolate, const Local& value) { - std::string valueStr = tns::ToString(isolate, value); + std::string valueStr = tns::ToString(isolate, value); #pragma GCC diagnostic ignored "-Wdeprecated-declarations" - // FIXME: std::codecvt_utf8_utf16 is deprecated - std::wstring_convert, char16_t> convert; - std::u16string value16 = convert.from_bytes(valueStr); + // FIXME: std::codecvt_utf8_utf16 is deprecated + std::wstring_convert, char16_t> convert; + std::u16string value16 = convert.from_bytes(valueStr); - return value16; + return value16; } std::vector tns::ToVector(const std::string& value) { #pragma GCC diagnostic ignored "-Wdeprecated-declarations" - // FIXME: std::codecvt_utf8_utf16 is deprecated - std::wstring_convert, char16_t> convert; - std::u16string value16 = convert.from_bytes(value); + // FIXME: std::codecvt_utf8_utf16 is deprecated + std::wstring_convert, char16_t> convert; + std::u16string value16 = convert.from_bytes(value); - const uint16_t *begin = reinterpret_cast(value16.data()); - const uint16_t *end = reinterpret_cast(value16.data() + value16.size()); - std::vector vector(begin, end); - return vector; + const uint16_t* begin = reinterpret_cast(value16.data()); + const uint16_t* end = reinterpret_cast(value16.data() + value16.size()); + std::vector vector(begin, end); + return vector; } bool tns::Exists(const char* fullPath) { - struct stat statbuf; - mode_t mode = S_IFDIR | S_IFREG; - if (stat(fullPath, &statbuf) == 0) { - return (statbuf.st_mode & S_IFMT) & mode; - } + struct stat statbuf; + mode_t mode = S_IFDIR | S_IFREG; + if (stat(fullPath, &statbuf) == 0) { + return (statbuf.st_mode & S_IFMT) & mode; + } - return false; + return false; } -Local tns::ReadModule(Isolate* isolate, const std::string &filePath) { - struct stat finfo; +Local tns::ReadModule(Isolate* isolate, const std::string& filePath) { + struct stat finfo; - int file = open(filePath.c_str(), O_RDONLY); - if (file < 0) { - tns::Assert(false); - } + int file = open(filePath.c_str(), O_RDONLY); + if (file < 0) { + throw NativeScriptException(isolate, "Cannot read module " + filePath); + } - fstat(file, &finfo); - long length = finfo.st_size; + fstat(file, &finfo); + long length = finfo.st_size; - char* newBuffer = new char[length + 128]; - strcpy(newBuffer, "(function(module, exports, require, __filename, __dirname) { "); // 61 Characters - read(file, &newBuffer[61], length); - close(file); - length += 61; + char* newBuffer = new char[length + 128]; + strcpy(newBuffer, + "(function(module, exports, require, __filename, __dirname) { "); // 61 Characters + read(file, &newBuffer[61], length); + close(file); + length += 61; - // Add the closing "\n})" - newBuffer[length] = 10; - ++length; - newBuffer[length] = '}'; - ++length; - newBuffer[length] = ')'; - ++length; - newBuffer[length] = 0; + // Add the closing "\n})" + newBuffer[length] = 10; + ++length; + newBuffer[length] = '}'; + ++length; + newBuffer[length] = ')'; + ++length; + newBuffer[length] = 0; - Local str = v8::String::NewFromUtf8(isolate, newBuffer, NewStringType::kNormal, (int)length).ToLocalChecked(); - delete[] newBuffer; + Local str = + v8::String::NewFromUtf8(isolate, newBuffer, NewStringType::kNormal, (int)length) + .ToLocalChecked(); + delete[] newBuffer; - return str; + return str; } const char* tns::ReadText(const std::string& filePath, long& length, bool& isNew) { - FILE* file = fopen(filePath.c_str(), "rb"); - if (file == nullptr) { - tns::Assert(false); - } + FILE* file = fopen(filePath.c_str(), "rb"); + if (file == nullptr) { + tns::Assert(false); + } - fseek(file, 0, SEEK_END); + fseek(file, 0, SEEK_END); - length = ftell(file); - isNew = length > BUFFER_SIZE; + length = ftell(file); + isNew = length > BUFFER_SIZE; - rewind(file); + rewind(file); - if (isNew) { - char* newBuffer = new char[length]; - fread(newBuffer, 1, length, file); - fclose(file); + if (isNew) { + char* newBuffer = new char[length]; + fread(newBuffer, 1, length, file); + fclose(file); - return newBuffer; - } + return newBuffer; + } - fread(Buffer, 1, length, file); - fclose(file); + fread(Buffer, 1, length, file); + fclose(file); - return Buffer; + return Buffer; } std::string tns::ReadText(const std::string& file) { - long length; - bool isNew; - const char* content = tns::ReadText(file, length, isNew); + long length; + bool isNew; + const char* content = tns::ReadText(file, length, isNew); - std::string result(content, length); + std::string result(content, length); - if (isNew) { - delete[] content; - } + if (isNew) { + delete[] content; + } - return result; + return result; } uint8_t* tns::ReadBinary(const std::string path, long& length, bool& isNew) { - length = 0; - std::ifstream ifs(path); - if (ifs.fail()) { - return nullptr; - } - - FILE* file = fopen(path.c_str(), "rb"); - if (!file) { - return nullptr; - } - - fseek(file, 0, SEEK_END); - length = ftell(file); - rewind(file); - - isNew = length > BUFFER_SIZE; - - if (isNew) { - uint8_t* data = new uint8_t[length]; - fread(data, sizeof(uint8_t), length, file); - fclose(file); - return data; - } - - fread(BinBuffer, 1, length, file); + length = 0; + std::ifstream ifs(path); + if (ifs.fail()) { + return nullptr; + } + + FILE* file = fopen(path.c_str(), "rb"); + if (!file) { + return nullptr; + } + + fseek(file, 0, SEEK_END); + length = ftell(file); + rewind(file); + + isNew = length > BUFFER_SIZE; + + if (isNew) { + uint8_t* data = new uint8_t[length]; + fread(data, sizeof(uint8_t), length, file); fclose(file); + return data; + } + + fread(BinBuffer, 1, length, file); + fclose(file); - return BinBuffer; + return BinBuffer; } bool tns::WriteBinary(const std::string& path, const void* data, long length) { - FILE* file = fopen(path.c_str(), "wb"); - if (!file) { - return false; - } + FILE* file = fopen(path.c_str(), "wb"); + if (!file) { + return false; + } - size_t writtenBytes = fwrite(data, sizeof(uint8_t), length, file); - fclose(file); + size_t writtenBytes = fwrite(data, sizeof(uint8_t), length, file); + fclose(file); - return writtenBytes == length; + return writtenBytes == length; } -void tns::SetPrivateValue(const Local& obj, const Local& propName, const Local& value) { - Local context; - bool success = obj->GetCreationContext().ToLocal(&context); - tns::Assert(success); - Isolate* isolate = context->GetIsolate(); - Local privateKey = Private::ForApi(isolate, propName); +void tns::SetPrivateValue(const Local& obj, const Local& propName, + const Local& value) { + Local context; + bool success = obj->GetCreationContext().ToLocal(&context); + tns::Assert(success); + Isolate* isolate = context->GetIsolate(); + Local privateKey = Private::ForApi(isolate, propName); - if (!obj->SetPrivate(context, privateKey, value).To(&success) || !success) { - tns::Assert(false, isolate); - } + if (!obj->SetPrivate(context, privateKey, value).To(&success) || !success) { + tns::Assert(false, isolate); + } } Local tns::GetPrivateValue(const Local& obj, const Local& propName) { - Local context; - bool success = obj->GetCreationContext().ToLocal(&context); - tns::Assert(success); - Isolate* isolate = context->GetIsolate(); - Local privateKey = Private::ForApi(isolate, propName); + Local context; + bool success = obj->GetCreationContext().ToLocal(&context); + tns::Assert(success); + Isolate* isolate = context->GetIsolate(); + Local privateKey = Private::ForApi(isolate, propName); - Maybe hasPrivate = obj->HasPrivate(context, privateKey); + Maybe hasPrivate = obj->HasPrivate(context, privateKey); - tns::Assert(!hasPrivate.IsNothing(), isolate); + tns::Assert(!hasPrivate.IsNothing(), isolate); - if (!hasPrivate.FromMaybe(false)) { - return Local(); - } + if (!hasPrivate.FromMaybe(false)) { + return Local(); + } - v8::Locker locker(isolate); - Local result; - if (!obj->GetPrivate(context, privateKey).ToLocal(&result)) { - tns::Assert(false, isolate); - } + v8::Locker locker(isolate); + Local result; + if (!obj->GetPrivate(context, privateKey).ToLocal(&result)) { + tns::Assert(false, isolate); + } - return result; + return result; } bool tns::DeleteWrapperIfUnused(Isolate* isolate, const Local& obj, BaseDataWrapper* value) { - if (GetValue(isolate, obj) != value) { - delete value; - return true; - } - return false; + if (GetValue(isolate, obj) != value) { + delete value; + return true; + } + return false; } void tns::SetValue(Isolate* isolate, const Local& obj, BaseDataWrapper* value) { - if (obj.IsEmpty() || obj->IsNullOrUndefined()) { - return; - } + if (obj.IsEmpty() || obj->IsNullOrUndefined()) { + return; + } - Local ext = External::New(isolate, value); + Local ext = External::New(isolate, value); - if (obj->InternalFieldCount() > 0) { - obj->SetInternalField(0, ext); - } else { - tns::SetPrivateValue(obj, tns::ToV8String(isolate, "metadata"), ext); - } + if (obj->InternalFieldCount() > 0) { + obj->SetInternalField(0, ext); + } else { + tns::SetPrivateValue(obj, tns::ToV8String(isolate, "metadata"), ext); + } } tns::BaseDataWrapper* tns::GetValue(Isolate* isolate, const Local& val) { - if (val.IsEmpty() || val->IsNullOrUndefined() || !val->IsObject()) { - return nullptr; - } - - Local obj = val.As(); - if (obj->InternalFieldCount() > 0) { - Local field = obj->GetInternalField(0); - if (field.IsEmpty() || field->IsNullOrUndefined() || !field->IsExternal()) { - return nullptr; - } + if (val.IsEmpty() || val->IsNullOrUndefined() || !val->IsObject()) { + return nullptr; + } - return static_cast(field.As()->Value()); + Local obj = val.As(); + if (obj->InternalFieldCount() > 0) { + Local field = obj->GetInternalField(0); + if (field.IsEmpty() || field->IsNullOrUndefined() || !field->IsExternal()) { + return nullptr; } - Local metadataProp = tns::GetPrivateValue(obj, tns::ToV8String(isolate, "metadata")); - if (metadataProp.IsEmpty() || metadataProp->IsNullOrUndefined() || !metadataProp->IsExternal()) { - return nullptr; - } + return static_cast(field.As()->Value()); + } + + Local metadataProp = tns::GetPrivateValue(obj, tns::ToV8String(isolate, "metadata")); + if (metadataProp.IsEmpty() || metadataProp->IsNullOrUndefined() || !metadataProp->IsExternal()) { + return nullptr; + } - return static_cast(metadataProp.As()->Value()); + return static_cast(metadataProp.As()->Value()); } void tns::DeleteValue(Isolate* isolate, const Local& val) { - if (val.IsEmpty() || val->IsNullOrUndefined() || !val->IsObject()) { - return; - } + if (val.IsEmpty() || val->IsNullOrUndefined() || !val->IsObject()) { + return; + } - Local obj = val.As(); - if (obj->InternalFieldCount() > 0) { - obj->SetInternalField(0, v8::Undefined(isolate)); - return; - } + Local obj = val.As(); + if (obj->InternalFieldCount() > 0) { + obj->SetInternalField(0, v8::Undefined(isolate)); + return; + } - Local metadataKey = tns::ToV8String(isolate, "metadata"); - Local metadataProp = tns::GetPrivateValue(obj, metadataKey); - if (metadataProp.IsEmpty() || metadataProp->IsNullOrUndefined() || !metadataProp->IsExternal()) { - return; - } + Local metadataKey = tns::ToV8String(isolate, "metadata"); + Local metadataProp = tns::GetPrivateValue(obj, metadataKey); + if (metadataProp.IsEmpty() || metadataProp->IsNullOrUndefined() || !metadataProp->IsExternal()) { + return; + } - Local context; - bool success = obj->GetCreationContext().ToLocal(&context); - tns::Assert(success, isolate); - Local privateKey = Private::ForApi(isolate, metadataKey); + Local context; + bool success = obj->GetCreationContext().ToLocal(&context); + tns::Assert(success, isolate); + Local privateKey = Private::ForApi(isolate, metadataKey); - success = obj->DeletePrivate(context, privateKey).FromMaybe(false); - tns::Assert(success, isolate); + success = obj->DeletePrivate(context, privateKey).FromMaybe(false); + tns::Assert(success, isolate); } std::vector> tns::ArgsToVector(const FunctionCallbackInfo& info) { - std::vector> args; - args.reserve(info.Length()); - for (int i = 0; i < info.Length(); i++) { - args.push_back(info[i]); - } - return args; + std::vector> args; + args.reserve(info.Length()); + for (int i = 0; i < info.Length(); i++) { + args.push_back(info[i]); + } + return args; } bool tns::IsArrayOrArrayLike(Isolate* isolate, const Local& value) { - if (value->IsArray()) { - return true; - } + if (value->IsArray()) { + return true; + } - if (!value->IsObject()) { - return false; - } + if (!value->IsObject()) { + return false; + } - Local obj = value.As(); - Local context; - bool success = obj->GetCreationContext().ToLocal(&context); - tns::Assert(success, isolate); - return obj->Has(context, ToV8String(isolate, "length")).FromMaybe(false); + Local obj = value.As(); + Local context; + bool success = obj->GetCreationContext().ToLocal(&context); + tns::Assert(success, isolate); + return obj->Has(context, ToV8String(isolate, "length")).FromMaybe(false); } void* tns::TryGetBufferFromArrayBuffer(const v8::Local& value, bool& isArrayBuffer) { - isArrayBuffer = false; + isArrayBuffer = false; - if (value.IsEmpty() || (!value->IsArrayBuffer() && !value->IsArrayBufferView())) { - return nullptr; - } + if (value.IsEmpty() || (!value->IsArrayBuffer() && !value->IsArrayBufferView())) { + return nullptr; + } - Local buffer; - if (value->IsArrayBufferView()) { - isArrayBuffer = true; - buffer = value.As()->Buffer(); - } else if (value->IsArrayBuffer()) { - isArrayBuffer = true; - buffer = value.As(); - } + Local buffer; + if (value->IsArrayBufferView()) { + isArrayBuffer = true; + buffer = value.As()->Buffer(); + } else if (value->IsArrayBuffer()) { + isArrayBuffer = true; + buffer = value.As(); + } - if (buffer.IsEmpty()) { - return nullptr; - } + if (buffer.IsEmpty()) { + return nullptr; + } - void* data = buffer->GetBackingStore()->Data(); - return data; + void* data = buffer->GetBackingStore()->Data(); + return data; } struct LockAndCV { - std::mutex m; - std::condition_variable cv; + std::mutex m; + std::condition_variable cv; }; -void tns::ExecuteOnRunLoop(CFRunLoopRef queue, std::function func, bool async) { - if(!async) { - bool __block finished = false; - auto v = new LockAndCV; - std::unique_lock lock(v->m); - CFRunLoopPerformBlock(queue, kCFRunLoopCommonModes, ^(void) { - func(); - { - std::unique_lock lk(v->m); - finished = true; - } - v->cv.notify_all(); - }); - CFRunLoopWakeUp(queue); - while(!finished) { - v->cv.wait(lock); - } - delete v; - } else { - CFRunLoopPerformBlock(queue, kCFRunLoopCommonModes, ^(void) { - func(); - }); - CFRunLoopWakeUp(queue); - } - -} - -void tns::ExecuteOnDispatchQueue(dispatch_queue_t queue, std::function func, bool async) { - if (async) { - dispatch_async(queue, ^(void) { - func(); - }); - } else { - dispatch_sync(queue, ^(void) { - func(); - }); - } -} - -void tns::ExecuteOnMainThread(std::function func, bool async) { - if (async) { - dispatch_async(dispatch_get_main_queue(), ^(void) { - func(); - }); - } else { - dispatch_sync(dispatch_get_main_queue(), ^(void) { - func(); - }); - } +void tns::ExecuteOnRunLoop(CFRunLoopRef queue, std::function func, bool async) { + if (!async) { + bool __block finished = false; + auto v = new LockAndCV; + std::unique_lock lock(v->m); + CFRunLoopPerformBlock(queue, kCFRunLoopCommonModes, ^(void) { + func(); + { + std::unique_lock lk(v->m); + finished = true; + } + v->cv.notify_all(); + }); + CFRunLoopWakeUp(queue); + while (!finished) { + v->cv.wait(lock); + } + delete v; + } else { + CFRunLoopPerformBlock(queue, kCFRunLoopCommonModes, ^(void) { + func(); + }); + CFRunLoopWakeUp(queue); + } +} + +void tns::ExecuteOnDispatchQueue(dispatch_queue_t queue, std::function func, bool async) { + if (async) { + dispatch_async(queue, ^(void) { + func(); + }); + } else { + dispatch_sync(queue, ^(void) { + func(); + }); + } +} + +void tns::ExecuteOnMainThread(std::function func, bool async) { + if (async) { + dispatch_async(dispatch_get_main_queue(), ^(void) { + func(); + }); + } else { + dispatch_sync(dispatch_get_main_queue(), ^(void) { + func(); + }); + } } void tns::LogError(Isolate* isolate, TryCatch& tc) { - if (!tc.HasCaught()) { - return; - } - - Log(@"Native stack trace:"); - LogBacktrace(); - - Local stack; - Local context = isolate->GetCurrentContext(); - bool success = tc.StackTrace(context).ToLocal(&stack); - if (!success || stack.IsEmpty()) { - return; - } - - Local stackV8Str; - success = stack->ToDetailString(context).ToLocal(&stackV8Str); - if (!success || stackV8Str.IsEmpty()) { - return; - } - - std::string stackTraceStr = tns::ToString(isolate, stackV8Str); - stackTraceStr = ReplaceAll(stackTraceStr, RuntimeConfig.BaseDir, ""); - - Log(@"JavaScript error:"); - Log(@"%s", stackTraceStr.c_str()); -} - -Local tns::JsonStringifyObject(Local context, Local value, bool handleCircularReferences) { - Isolate* isolate = context->GetIsolate(); - if (value.IsEmpty()) { - return v8::String::Empty(isolate); - } - - if (handleCircularReferences) { - Local smartJSONStringifyFunction = tns::GetSmartJSONStringifyFunction(isolate); - - if (!smartJSONStringifyFunction.IsEmpty()) { - if (value->IsObject()) { - Local resultValue; - TryCatch tc(isolate); - - Local args[] = { - value->ToObject(context).ToLocalChecked() - }; - bool success = smartJSONStringifyFunction->Call(context, v8::Undefined(isolate), 1, args).ToLocal(&resultValue); - - if (success && !tc.HasCaught()) { - return resultValue->ToString(context).ToLocalChecked(); - } - } + if (!tc.HasCaught()) { + return; + } + + Log(@"Native stack trace:"); + LogBacktrace(); + + Local stack; + Local context = isolate->GetCurrentContext(); + bool success = tc.StackTrace(context).ToLocal(&stack); + if (!success || stack.IsEmpty()) { + return; + } + + Local stackV8Str; + success = stack->ToDetailString(context).ToLocal(&stackV8Str); + if (!success || stackV8Str.IsEmpty()) { + return; + } + + std::string stackTraceStr = tns::ToString(isolate, stackV8Str); + stackTraceStr = ReplaceAll(stackTraceStr, RuntimeConfig.BaseDir, ""); + + // Apply stack trace remapping if available + stackTraceStr = tns::RemapStackTrace(isolate, stackTraceStr); + + Log(@"🚨🚨🚨"); + Log(@"Boot Loading Error. Resolve to Continue."); + Log(@" "); + Log(@"%s", stackTraceStr.c_str()); + Log(@" "); +} + +Local tns::JsonStringifyObject(Local context, Local value, + bool handleCircularReferences) { + Isolate* isolate = context->GetIsolate(); + if (value.IsEmpty()) { + return v8::String::Empty(isolate); + } + + if (handleCircularReferences) { + Local smartJSONStringifyFunction = tns::GetSmartJSONStringifyFunction(isolate); + + if (!smartJSONStringifyFunction.IsEmpty()) { + if (value->IsObject()) { + Local resultValue; + TryCatch tc(isolate); + + Local args[] = {value->ToObject(context).ToLocalChecked()}; + bool success = smartJSONStringifyFunction->Call(context, v8::Undefined(isolate), 1, args) + .ToLocal(&resultValue); + + if (success && !tc.HasCaught()) { + return resultValue->ToString(context).ToLocalChecked(); } + } } + } - Local resultString; - TryCatch tc(isolate); - bool success = v8::JSON::Stringify(context, value->ToObject(context).ToLocalChecked()).ToLocal(&resultString); + Local resultString; + TryCatch tc(isolate); + bool success = v8::JSON::Stringify(context, value->ToObject(context).ToLocalChecked()) + .ToLocal(&resultString); - if (!success && tc.HasCaught()) { - tns::LogError(isolate, tc); - return Local(); - } + if (!success && tc.HasCaught()) { + tns::LogError(isolate, tc); + return Local(); + } - return resultString; + return resultString; } Local tns::GetSmartJSONStringifyFunction(Isolate* isolate) { - std::shared_ptr caches = Caches::Get(isolate); - if (caches->SmartJSONStringifyFunc != nullptr) { - return caches->SmartJSONStringifyFunc->Get(isolate); - } - - std::string smartStringifyFunctionScript = - "(function () {\n" - " function smartStringify(object) {\n" - " const seen = [];\n" - " var replacer = function (key, value) {\n" - " if (value != null && typeof value == \"object\") {\n" - " if (seen.indexOf(value) >= 0) {\n" - " if (key) {\n" - " return \"[Circular]\";\n" - " }\n" - " return;\n" - " }\n" - " seen.push(value);\n" - " }\n" - " return value;\n" - " };\n" - " return JSON.stringify(object, replacer, 2);\n" - " }\n" - " return smartStringify;\n" - "})();"; - - Local source = tns::ToV8String(isolate, smartStringifyFunctionScript); - Local context = isolate->GetCurrentContext(); - - Local