From 863a7902c238de47a81eb0fb637c87a3b3b8d882 Mon Sep 17 00:00:00 2001 From: Eduardo Speroni Date: Tue, 17 Dec 2024 15:47:07 -0600 Subject: [PATCH 1/3] chore: format Runtime files --- NativeScript/runtime/Runtime.h | 140 ++++----- NativeScript/runtime/Runtime.mm | 513 +++++++++++++++++--------------- 2 files changed, 341 insertions(+), 312 deletions(-) diff --git a/NativeScript/runtime/Runtime.h b/NativeScript/runtime/Runtime.h index f52215d9..a55080c4 100644 --- a/NativeScript/runtime/Runtime.h +++ b/NativeScript/runtime/Runtime.h @@ -1,91 +1,93 @@ #ifndef Runtime_h #define Runtime_h -#include "libplatform/libplatform.h" +#include "Caches.h" #include "Common.h" -#include "ModuleInternal.h" #include "MetadataBuilder.h" +#include "ModuleInternal.h" #include "SpinLock.h" -#include "Caches.h" +#include "libplatform/libplatform.h" namespace tns { class Runtime { -public: - Runtime(); - ~Runtime(); - v8::Isolate* CreateIsolate(); - void Init(v8::Isolate* isolate, bool isWorker = false); - void RunMainScript(); - v8::Isolate* GetIsolate(); - - const int WorkerId(); - - void SetWorkerId(int workerId); - inline bool IsRuntimeWorker() { - return workerId_ > 0; - } - - inline CFRunLoopRef RuntimeLoop() { - return runtimeLoop_; - } + public: + Runtime(); + ~Runtime(); + v8::Isolate* CreateIsolate(); + void Init(v8::Isolate* isolate, bool isWorker = false); + void RunMainScript(); + v8::Isolate* GetIsolate(); - void RunModule(const std::string moduleName); - - void RunScript(const std::string script); + const int WorkerId(); - static void Initialize(); + void SetWorkerId(int workerId); + inline bool IsRuntimeWorker() { return workerId_ > 0; } - static Runtime* GetCurrentRuntime() { - return currentRuntime_; - } - - static Runtime* GetRuntime(v8::Isolate* isolate); + inline CFRunLoopRef RuntimeLoop() { return runtimeLoop_; } - static bool IsWorker() { - if (currentRuntime_ == nullptr) { - return false; - } + void RunModule(const std::string moduleName); - return currentRuntime_->IsRuntimeWorker(); - } + void RunScript(const std::string script); + + static void Initialize(); + + static Runtime* GetCurrentRuntime() { return currentRuntime_; } + + static Runtime* GetRuntime(v8::Isolate* isolate); - static std::shared_ptr GetPlatform() { - return platform_; + static bool IsWorker() { + if (currentRuntime_ == nullptr) { + return false; } - static id GetAppConfigValue(std::string key); - - static bool IsAlive(const v8::Isolate* isolate); -private: - static thread_local Runtime* currentRuntime_; - static std::shared_ptr platform_; - static std::vector isolates_; - static SpinMutex isolatesMutex_; - static bool v8Initialized_; - static std::atomic nextIsolateId; - - void DefineGlobalObject(v8::Local context, bool isWorker); - void DefineCollectFunction(v8::Local context); - void DefineNativeScriptVersion(v8::Isolate* isolate, v8::Local globalTemplate); - void DefinePerformanceObject(v8::Isolate* isolate, v8::Local globalTemplate); - void DefineTimeMethod(v8::Isolate* isolate, v8::Local globalTemplate); - void DefineDrainMicrotaskMethod(v8::Isolate* isolate, v8::Local globalTemplate); - void DefineDateTimeConfigurationChangeNotificationMethod(v8::Isolate* isolate, v8::Local globalTemplate); - - static void PerformanceNowCallback(const v8::FunctionCallbackInfo& args); - v8::Isolate* isolate_; - std::unique_ptr moduleInternal_; - int workerId_; - CFRunLoopRef runtimeLoop_; - // TODO: refactor this. This is only needed because, during program termination (UIApplicationMain not called) - // the Cache::Workers is released (static initialization order fiasco https://en.cppreference.com/w/cpp/language/siof) - // so it released the Cache::Workers shared_ptr and then releases the Runtime unique_ptr - // eventually we just need to refactor so that Runtime::Initialize is responsible for its initalization - // and lifecycle - std::shared_ptr>> workerCache_; + return currentRuntime_->IsRuntimeWorker(); + } + + static std::shared_ptr GetPlatform() { return platform_; } + + static id GetAppConfigValue(std::string key); + + static bool IsAlive(const v8::Isolate* isolate); + + private: + static thread_local Runtime* currentRuntime_; + static std::shared_ptr platform_; + static std::vector isolates_; + static SpinMutex isolatesMutex_; + static bool v8Initialized_; + static std::atomic nextIsolateId; + + void DefineGlobalObject(v8::Local context, bool isWorker); + void DefineCollectFunction(v8::Local context); + void DefineNativeScriptVersion(v8::Isolate* isolate, + v8::Local globalTemplate); + void DefinePerformanceObject(v8::Isolate* isolate, + v8::Local globalTemplate); + void DefineTimeMethod(v8::Isolate* isolate, + v8::Local globalTemplate); + void DefineDrainMicrotaskMethod(v8::Isolate* isolate, + v8::Local globalTemplate); + void DefineDateTimeConfigurationChangeNotificationMethod( + v8::Isolate* isolate, v8::Local globalTemplate); + + static void PerformanceNowCallback( + const v8::FunctionCallbackInfo& args); + v8::Isolate* isolate_; + std::unique_ptr moduleInternal_; + int workerId_; + CFRunLoopRef runtimeLoop_; + // TODO: refactor this. This is only needed because, during program + // termination (UIApplicationMain not called) the Cache::Workers is released + // (static initialization order fiasco + // https://en.cppreference.com/w/cpp/language/siof) so it released the + // Cache::Workers shared_ptr and then releases the Runtime unique_ptr + // eventually we just need to refactor so that Runtime::Initialize is + // responsible for its initalization and lifecycle + std::shared_ptr>> + workerCache_; }; -} +} // namespace tns #endif /* Runtime_h */ diff --git a/NativeScript/runtime/Runtime.mm b/NativeScript/runtime/Runtime.mm index 43e0c029..d34f9359 100644 --- a/NativeScript/runtime/Runtime.mm +++ b/NativeScript/runtime/Runtime.mm @@ -1,26 +1,26 @@ -#include -#include #include "Runtime.h" +#include +#include +#include "ArgConverter.h" #include "Caches.h" #include "Console.h" -#include "ArgConverter.h" +#include "Constants.h" +#include "Helpers.h" +#include "InlineFunctions.h" #include "Interop.h" #include "NativeScriptException.h" -#include "InlineFunctions.h" -#include "SimpleAllocator.h" #include "ObjectManager.h" -#include "RuntimeConfig.h" #include "PromiseProxy.h" -#include "Helpers.h" +#include "RuntimeConfig.h" +#include "SimpleAllocator.h" +#include "SpinLock.h" #include "TSHelpers.h" #include "WeakRef.h" #include "Worker.h" -#include "Constants.h" -#include "SpinLock.h" // #include "SetTimeout.h" -#include "IsolateWrapper.h" #include "DisposerPHV.h" +#include "IsolateWrapper.h" #include "ModuleBinding.hpp" #include "URLImpl.h" @@ -38,8 +38,9 @@ SimpleAllocator allocator_; NSDictionary* AppPackageJson = nil; -// TODO: consider listening to timezone changes and automatically reseting the DateTime. Probably makes more sense to move it to its own file -//void UpdateTimezoneNotificationCallback(CFNotificationCenterRef center, +// TODO: consider listening to timezone changes and automatically reseting the DateTime. Probably +// makes more sense to move it to its own file +// void UpdateTimezoneNotificationCallback(CFNotificationCenterRef center, // void *observer, // CFStringRef name, // const void *object, @@ -53,153 +54,158 @@ // }); //} // add this to register (most likely on setting up isolate -//CFNotificationCenterAddObserver(CFNotificationCenterGetLocalCenter(), this, &UpdateTimezoneNotificationCallback, kCFTimeZoneSystemTimeZoneDidChangeNotification, nullptr, CFNotificationSuspensionBehaviorDeliverImmediately); +// CFNotificationCenterAddObserver(CFNotificationCenterGetLocalCenter(), this, +// &UpdateTimezoneNotificationCallback, kCFTimeZoneSystemTimeZoneDidChangeNotification, nullptr, +// CFNotificationSuspensionBehaviorDeliverImmediately); // add this to remove the observer -//CFNotificationCenterRemoveObserver(CFNotificationCenterGetLocalCenter(), this, kCFTimeZoneSystemTimeZoneDidChangeNotification, NULL); +// CFNotificationCenterRemoveObserver(CFNotificationCenterGetLocalCenter(), this, +// kCFTimeZoneSystemTimeZoneDidChangeNotification, NULL); void DisposeIsolateWhenPossible(Isolate* isolate) { - // most of the time, this will never delay disposal - // occasionally this can happen when the runtime is destroyed by actions of its own isolate - // as an example: isolate calls exit(0), which in turn destroys the Runtime unique_ptr - // another scenario is when embedding nativescript, if the embedder deletes the runtime as a result of a callback from JS - // in the case of exit(0), the app will die before actually disposing the isolate, which isn't a problem - if (isolate->IsInUse()) { - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10.0 * NSEC_PER_MSEC)), - dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{ - DisposeIsolateWhenPossible(isolate); - }); - } else { - isolate->Dispose(); - } + // most of the time, this will never delay disposal + // occasionally this can happen when the runtime is destroyed by actions of its own isolate + // as an example: isolate calls exit(0), which in turn destroys the Runtime unique_ptr + // another scenario is when embedding nativescript, if the embedder deletes the runtime as a + // result of a callback from JS in the case of exit(0), the app will die before actually disposing + // the isolate, which isn't a problem + if (isolate->IsInUse()) { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10.0 * NSEC_PER_MSEC)), + dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{ + DisposeIsolateWhenPossible(isolate); + }); + } else { + isolate->Dispose(); + } } -void Runtime::Initialize() { - MetaFile::setInstance(RuntimeConfig.MetadataPtr); -} +void Runtime::Initialize() { MetaFile::setInstance(RuntimeConfig.MetadataPtr); } Runtime::Runtime() { - currentRuntime_ = this; - workerId_ = -1; - workerCache_ = Caches::Workers; + currentRuntime_ = this; + workerId_ = -1; + workerCache_ = Caches::Workers; } Runtime::~Runtime() { - auto currentIsolate = this->isolate_; - { - // make sure we remove the isolate from the list of active isolates first - // this will make sure isAlive(isolate) will return false and prevent locking of the v8 isolate after - // it terminates execution - SpinLock lock(isolatesMutex_); - Runtime::isolates_.erase(std::remove(Runtime::isolates_.begin(), Runtime::isolates_.end(), this->isolate_), Runtime::isolates_.end()); - Caches::Get(isolate_)->InvalidateIsolate(); + auto currentIsolate = this->isolate_; + { + // make sure we remove the isolate from the list of active isolates first + // this will make sure isAlive(isolate) will return false and prevent locking of the v8 isolate + // after it terminates execution + SpinLock lock(isolatesMutex_); + Runtime::isolates_.erase( + std::remove(Runtime::isolates_.begin(), Runtime::isolates_.end(), this->isolate_), + Runtime::isolates_.end()); + Caches::Get(isolate_)->InvalidateIsolate(); + } + this->isolate_->TerminateExecution(); + + // TODO: fix race condition on workers where a queue can leak (maybe calling Terminate before + // Initialize?) + Caches::Workers->ForEach([currentIsolate](int& key, std::shared_ptr& value) { + auto childWorkerWrapper = static_cast(value->UserData()); + if (childWorkerWrapper->GetMainIsolate() == currentIsolate) { + childWorkerWrapper->Terminate(); } - this->isolate_->TerminateExecution(); - - // TODO: fix race condition on workers where a queue can leak (maybe calling Terminate before Initialize?) - Caches::Workers->ForEach([currentIsolate](int& key, std::shared_ptr& value) { - auto childWorkerWrapper = static_cast(value->UserData()); - if (childWorkerWrapper->GetMainIsolate() == currentIsolate) { - childWorkerWrapper->Terminate(); - } - return false; - }); + return false; + }); + + { + v8::Locker lock(isolate_); + DisposerPHV phv(isolate_); + isolate_->VisitHandlesWithClassIds(&phv); + + if (IsRuntimeWorker()) { + auto currentWorker = + static_cast(Caches::Workers->Get(this->workerId_)->UserData()); + Caches::Workers->Remove(this->workerId_); + // if the parent isolate is dead then deleting the wrapper is our responsibility + if (currentWorker->IsWeak()) { + delete currentWorker; + } + } + Caches::Remove(this->isolate_); - { - v8::Locker lock(isolate_); - DisposerPHV phv(isolate_); - isolate_->VisitHandlesWithClassIds( &phv ); - - if (IsRuntimeWorker()) { - auto currentWorker = static_cast(Caches::Workers->Get(this->workerId_)->UserData()); - Caches::Workers->Remove(this->workerId_); - // if the parent isolate is dead then deleting the wrapper is our responsibility - if (currentWorker->IsWeak()) { - delete currentWorker; - } - } - Caches::Remove(this->isolate_); + this->isolate_->SetData(Constants::RUNTIME_SLOT, nullptr); + } - this->isolate_->SetData(Constants::RUNTIME_SLOT, nullptr); - } + DisposeIsolateWhenPossible(this->isolate_); - DisposeIsolateWhenPossible(this->isolate_); - - currentRuntime_ = nullptr; + currentRuntime_ = nullptr; } Runtime* Runtime::GetRuntime(v8::Isolate* isolate) { - return static_cast(isolate->GetData(Constants::RUNTIME_SLOT)); + return static_cast(isolate->GetData(Constants::RUNTIME_SLOT)); } Isolate* Runtime::CreateIsolate() { - if (!v8Initialized_) { - // Runtime::platform_ = RuntimeConfig.IsDebug + if (!v8Initialized_) { + // Runtime::platform_ = RuntimeConfig.IsDebug // ? v8_inspector::V8InspectorPlatform::CreateDefaultPlatform() // : platform::NewDefaultPlatform(); - Runtime::platform_ = platform::NewDefaultPlatform(); + Runtime::platform_ = platform::NewDefaultPlatform(); - V8::InitializePlatform(Runtime::platform_.get()); - V8::Initialize(); - std::string flags = RuntimeConfig.IsDebug - ? "--expose_gc --jitless" - : "--expose_gc --jitless --no-lazy"; - V8::SetFlagsFromString(flags.c_str(), flags.size()); - v8Initialized_ = true; - } - - // auto version = v8::V8::GetVersion(); - - Isolate::CreateParams create_params; - create_params.array_buffer_allocator = &allocator_; - Isolate* isolate = Isolate::New(create_params); - runtimeLoop_ = CFRunLoopGetCurrent(); - isolate->SetData(Constants::RUNTIME_SLOT, this); - - { - SpinLock lock(isolatesMutex_); - Runtime::isolates_.emplace_back(isolate); - } + V8::InitializePlatform(Runtime::platform_.get()); + V8::Initialize(); + std::string flags = + RuntimeConfig.IsDebug ? "--expose_gc --jitless" : "--expose_gc --jitless --no-lazy"; + V8::SetFlagsFromString(flags.c_str(), flags.size()); + v8Initialized_ = true; + } + + // auto version = v8::V8::GetVersion(); + + Isolate::CreateParams create_params; + create_params.array_buffer_allocator = &allocator_; + Isolate* isolate = Isolate::New(create_params); + runtimeLoop_ = CFRunLoopGetCurrent(); + isolate->SetData(Constants::RUNTIME_SLOT, this); + + { + SpinLock lock(isolatesMutex_); + Runtime::isolates_.emplace_back(isolate); + } - return isolate; + return isolate; } void Runtime::Init(Isolate* isolate, bool isWorker) { - std::shared_ptr cache = Caches::Init(isolate, nextIsolateId.fetch_add(1, std::memory_order_relaxed)); - cache->isWorker = isWorker; - cache->ObjectCtorInitializer = MetadataBuilder::GetOrCreateConstructorFunctionTemplate; - cache->StructCtorInitializer = MetadataBuilder::GetOrCreateStructCtorFunction; - - Isolate::Scope isolate_scope(isolate); - HandleScope handle_scope(isolate); - Local globalTemplateFunction = FunctionTemplate::New(isolate); - globalTemplateFunction->SetClassName(tns::ToV8String(isolate, "NativeScriptGlobalObject")); - tns::binding::CreateInternalBindingTemplates(isolate, globalTemplateFunction); - Local globalTemplate = ObjectTemplate::New(isolate, globalTemplateFunction); - DefineNativeScriptVersion(isolate, globalTemplate); - - //Worker::Init(isolate, globalTemplate, isWorker); - DefinePerformanceObject(isolate, globalTemplate); - DefineTimeMethod(isolate, globalTemplate); - DefineDrainMicrotaskMethod(isolate, globalTemplate); - ObjectManager::Init(isolate, globalTemplate); -// SetTimeout::Init(isolate, globalTemplate); - MetadataBuilder::RegisterConstantsOnGlobalObject(isolate, globalTemplate, isWorker); - - isolate->SetCaptureStackTraceForUncaughtExceptions(true, 100, StackTrace::kOverview); - isolate->AddMessageListener(NativeScriptException::OnUncaughtError); - - Local context = Context::New(isolate, nullptr, globalTemplate); - context->Enter(); - - DefineGlobalObject(context, isWorker); - DefineCollectFunction(context); - PromiseProxy::Init(context); - Console::Init(context); - WeakRef::Init(context); - - - auto blob_methods = R"js( + std::shared_ptr cache = + Caches::Init(isolate, nextIsolateId.fetch_add(1, std::memory_order_relaxed)); + cache->isWorker = isWorker; + cache->ObjectCtorInitializer = MetadataBuilder::GetOrCreateConstructorFunctionTemplate; + cache->StructCtorInitializer = MetadataBuilder::GetOrCreateStructCtorFunction; + + Isolate::Scope isolate_scope(isolate); + HandleScope handle_scope(isolate); + Local globalTemplateFunction = FunctionTemplate::New(isolate); + globalTemplateFunction->SetClassName(tns::ToV8String(isolate, "NativeScriptGlobalObject")); + tns::binding::CreateInternalBindingTemplates(isolate, globalTemplateFunction); + Local globalTemplate = ObjectTemplate::New(isolate, globalTemplateFunction); + DefineNativeScriptVersion(isolate, globalTemplate); + + // Worker::Init(isolate, globalTemplate, isWorker); + DefinePerformanceObject(isolate, globalTemplate); + DefineTimeMethod(isolate, globalTemplate); + DefineDrainMicrotaskMethod(isolate, globalTemplate); + ObjectManager::Init(isolate, globalTemplate); + // SetTimeout::Init(isolate, globalTemplate); + MetadataBuilder::RegisterConstantsOnGlobalObject(isolate, globalTemplate, isWorker); + + isolate->SetCaptureStackTraceForUncaughtExceptions(true, 100, StackTrace::kOverview); + isolate->AddMessageListener(NativeScriptException::OnUncaughtError); + + Local context = Context::New(isolate, nullptr, globalTemplate); + context->Enter(); + + DefineGlobalObject(context, isWorker); + DefineCollectFunction(context); + PromiseProxy::Init(context); + Console::Init(context); + WeakRef::Init(context); + + auto blob_methods = R"js( const BLOB_STORE = new Map(); URL.createObjectURL = function (object, options = null) { try { @@ -260,170 +266,191 @@ void DisposeIsolateWhenPossible(Isolate* isolate) { }, }); )js"; - - - v8::Local script; - auto done = v8::Script::Compile(context, ToV8String(isolate, blob_methods)).ToLocal(&script); - - v8::Local outVal; - if(done){ - done = script->Run(context).ToLocal(&outVal); - } - - - this->moduleInternal_ = std::make_unique(context); + v8::Local script; + auto done = v8::Script::Compile(context, ToV8String(isolate, blob_methods)).ToLocal(&script); + + v8::Local outVal; + if (done) { + done = script->Run(context).ToLocal(&outVal); + } + + this->moduleInternal_ = std::make_unique(context); - ArgConverter::Init(context, MetadataBuilder::StructPropertyGetterCallback, MetadataBuilder::StructPropertySetterCallback); - Interop::RegisterInteropTypes(context); + ArgConverter::Init(context, MetadataBuilder::StructPropertyGetterCallback, + MetadataBuilder::StructPropertySetterCallback); + Interop::RegisterInteropTypes(context); - ClassBuilder::RegisterBaseTypeScriptExtendsFunction(context); // Register the __extends function to the global object - ClassBuilder::RegisterNativeTypeScriptExtendsFunction(context); // Override the __extends function for native objects - TSHelpers::Init(context); + ClassBuilder::RegisterBaseTypeScriptExtendsFunction( + context); // Register the __extends function to the global object + ClassBuilder::RegisterNativeTypeScriptExtendsFunction( + context); // Override the __extends function for native objects + TSHelpers::Init(context); - InlineFunctions::Init(context); + InlineFunctions::Init(context); - cache->SetContext(context); + cache->SetContext(context); - this->isolate_ = isolate; + this->isolate_ = isolate; } void Runtime::RunMainScript() { - Isolate* isolate = this->GetIsolate(); - v8::Locker locker(isolate); - Isolate::Scope isolate_scope(isolate); - HandleScope handle_scope(isolate); - this->moduleInternal_->RunModule(isolate, "./"); + Isolate* isolate = this->GetIsolate(); + v8::Locker locker(isolate); + Isolate::Scope isolate_scope(isolate); + HandleScope handle_scope(isolate); + this->moduleInternal_->RunModule(isolate, "./"); } void Runtime::RunModule(const std::string moduleName) { - Isolate* isolate = this->GetIsolate(); - Isolate::Scope isolate_scope(isolate); - HandleScope handle_scope(isolate); - this->moduleInternal_->RunModule(isolate, moduleName); + Isolate* isolate = this->GetIsolate(); + Isolate::Scope isolate_scope(isolate); + HandleScope handle_scope(isolate); + this->moduleInternal_->RunModule(isolate, moduleName); } void Runtime::RunScript(const std::string script) { - Isolate* isolate = this->GetIsolate(); - v8::Locker locker(isolate); - Isolate::Scope isolate_scope(isolate); - HandleScope handle_scope(isolate); - this->moduleInternal_->RunScript(isolate, script); + Isolate* isolate = this->GetIsolate(); + v8::Locker locker(isolate); + Isolate::Scope isolate_scope(isolate); + HandleScope handle_scope(isolate); + this->moduleInternal_->RunScript(isolate, script); } +Isolate* Runtime::GetIsolate() { return this->isolate_; } -Isolate* Runtime::GetIsolate() { - return this->isolate_; -} +const int Runtime::WorkerId() { return this->workerId_; } -const int Runtime::WorkerId() { - return this->workerId_; -} - -void Runtime::SetWorkerId(int workerId) { - this->workerId_ = workerId; -} +void Runtime::SetWorkerId(int workerId) { this->workerId_ = workerId; } id Runtime::GetAppConfigValue(std::string key) { - if (AppPackageJson == nil) { - NSString* packageJsonPath = [[NSString stringWithUTF8String:RuntimeConfig.ApplicationPath.c_str()] stringByAppendingPathComponent:@"package.json"]; - NSData* data = [NSData dataWithContentsOfFile:packageJsonPath]; - if (data) { - NSError* error = nil; - NSDictionary* dict = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error]; - AppPackageJson = [[NSDictionary alloc] initWithDictionary:dict]; - } + if (AppPackageJson == nil) { + NSString* packageJsonPath = + [[NSString stringWithUTF8String:RuntimeConfig.ApplicationPath.c_str()] + stringByAppendingPathComponent:@"package.json"]; + NSData* data = [NSData dataWithContentsOfFile:packageJsonPath]; + if (data) { + NSError* error = nil; + NSDictionary* dict = [NSJSONSerialization JSONObjectWithData:data + options:kNilOptions + error:&error]; + AppPackageJson = [[NSDictionary alloc] initWithDictionary:dict]; } + } - id result = AppPackageJson[[NSString stringWithUTF8String:key.c_str()]]; - return result; + id result = AppPackageJson[[NSString stringWithUTF8String:key.c_str()]]; + return result; } void Runtime::DefineGlobalObject(Local context, bool isWorker) { - Isolate* isolate = context->GetIsolate(); - Local global = context->Global(); - const PropertyAttribute readOnlyFlags = static_cast(PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly); - if (!global->DefineOwnProperty(context, ToV8String(context->GetIsolate(), "global"), global, readOnlyFlags).FromMaybe(false)) { - tns::Assert(false, isolate); - } - - if (isWorker && !global->DefineOwnProperty(context, ToV8String(context->GetIsolate(), "self"), global, readOnlyFlags).FromMaybe(false)) { - tns::Assert(false, isolate); - } + Isolate* isolate = context->GetIsolate(); + Local global = context->Global(); + const PropertyAttribute readOnlyFlags = + static_cast(PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly); + if (!global + ->DefineOwnProperty(context, ToV8String(context->GetIsolate(), "global"), global, + readOnlyFlags) + .FromMaybe(false)) { + tns::Assert(false, isolate); + } + + if (isWorker && !global + ->DefineOwnProperty(context, ToV8String(context->GetIsolate(), "self"), + global, readOnlyFlags) + .FromMaybe(false)) { + tns::Assert(false, isolate); + } } void Runtime::DefineCollectFunction(Local context) { - Isolate* isolate = context->GetIsolate(); - Local global = context->Global(); - Local value; - bool success = global->Get(context, tns::ToV8String(isolate, "gc")).ToLocal(&value); - tns::Assert(success, isolate); - - if (value.IsEmpty() || !value->IsFunction()) { - return; - } - - Local gcFunc = value.As(); - const PropertyAttribute readOnlyFlags = static_cast(PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly); - success = global->DefineOwnProperty(context, tns::ToV8String(isolate, "__collect"), gcFunc, readOnlyFlags).FromMaybe(false); - tns::Assert(success, isolate); + Isolate* isolate = context->GetIsolate(); + Local global = context->Global(); + Local value; + bool success = global->Get(context, tns::ToV8String(isolate, "gc")).ToLocal(&value); + tns::Assert(success, isolate); + + if (value.IsEmpty() || !value->IsFunction()) { + return; + } + + Local gcFunc = value.As(); + const PropertyAttribute readOnlyFlags = + static_cast(PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly); + success = + global + ->DefineOwnProperty(context, tns::ToV8String(isolate, "__collect"), gcFunc, readOnlyFlags) + .FromMaybe(false); + tns::Assert(success, isolate); } void Runtime::DefinePerformanceObject(Isolate* isolate, Local globalTemplate) { - Local performanceTemplate = ObjectTemplate::New(isolate); + Local performanceTemplate = ObjectTemplate::New(isolate); - Local nowFuncTemplate = FunctionTemplate::New(isolate, PerformanceNowCallback); - performanceTemplate->Set(tns::ToV8String(isolate, "now"), nowFuncTemplate); + Local nowFuncTemplate = FunctionTemplate::New(isolate, PerformanceNowCallback); + performanceTemplate->Set(tns::ToV8String(isolate, "now"), nowFuncTemplate); - Local performancePropertyName = ToV8String(isolate, "performance"); - globalTemplate->Set(performancePropertyName, performanceTemplate); + Local performancePropertyName = ToV8String(isolate, "performance"); + globalTemplate->Set(performancePropertyName, performanceTemplate); } void Runtime::PerformanceNowCallback(const FunctionCallbackInfo& args) { - std::chrono::system_clock::time_point now = std::chrono::system_clock::now(); - std::chrono::milliseconds timestampMs = std::chrono::duration_cast(now.time_since_epoch()); - double result = timestampMs.count(); - args.GetReturnValue().Set(result); + std::chrono::system_clock::time_point now = std::chrono::system_clock::now(); + std::chrono::milliseconds timestampMs = + std::chrono::duration_cast(now.time_since_epoch()); + double result = timestampMs.count(); + args.GetReturnValue().Set(result); } void Runtime::DefineNativeScriptVersion(Isolate* isolate, Local globalTemplate) { - const PropertyAttribute readOnlyFlags = static_cast(PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly); - globalTemplate->Set(ToV8String(isolate, "__runtimeVersion"), ToV8String(isolate, STRINGIZE_VALUE_OF(NATIVESCRIPT_VERSION)), readOnlyFlags); + const PropertyAttribute readOnlyFlags = + static_cast(PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly); + globalTemplate->Set(ToV8String(isolate, "__runtimeVersion"), + ToV8String(isolate, STRINGIZE_VALUE_OF(NATIVESCRIPT_VERSION)), readOnlyFlags); } void Runtime::DefineTimeMethod(v8::Isolate* isolate, v8::Local globalTemplate) { - Local timeFunctionTemplate = FunctionTemplate::New(isolate, [](const FunctionCallbackInfo& info) { - auto nano = std::chrono::time_point_cast(std::chrono::steady_clock::now()); + Local timeFunctionTemplate = + FunctionTemplate::New(isolate, [](const FunctionCallbackInfo& info) { + auto nano = std::chrono::time_point_cast( + std::chrono::steady_clock::now()); double duration = nano.time_since_epoch().count() / 1000000.0; info.GetReturnValue().Set(duration); - }); - globalTemplate->Set(ToV8String(isolate, "__time"), timeFunctionTemplate); + }); + globalTemplate->Set(ToV8String(isolate, "__time"), timeFunctionTemplate); } -void Runtime::DefineDrainMicrotaskMethod(v8::Isolate* isolate, v8::Local globalTemplate) { - Local drainMicrotaskTemplate = FunctionTemplate::New(isolate, [](const FunctionCallbackInfo& info) { +void Runtime::DefineDrainMicrotaskMethod(v8::Isolate* isolate, + v8::Local globalTemplate) { + Local drainMicrotaskTemplate = + FunctionTemplate::New(isolate, [](const FunctionCallbackInfo& info) { info.GetIsolate()->PerformMicrotaskCheckpoint(); - }); - globalTemplate->Set(ToV8String(isolate, "__drainMicrotaskQueue"), drainMicrotaskTemplate); + }); + globalTemplate->Set(ToV8String(isolate, "__drainMicrotaskQueue"), drainMicrotaskTemplate); } -void Runtime::DefineDateTimeConfigurationChangeNotificationMethod(v8::Isolate* isolate, v8::Local globalTemplate) { - Local drainMicrotaskTemplate = FunctionTemplate::New(isolate, [](const FunctionCallbackInfo& info) { - info.GetIsolate()->DateTimeConfigurationChangeNotification(Isolate::TimeZoneDetection::kRedetect); - }); - globalTemplate->Set(ToV8String(isolate, "__dateTimeConfigurationChangeNotification"), drainMicrotaskTemplate); +void Runtime::DefineDateTimeConfigurationChangeNotificationMethod( + v8::Isolate* isolate, v8::Local globalTemplate) { + Local drainMicrotaskTemplate = + FunctionTemplate::New(isolate, [](const FunctionCallbackInfo& info) { + info.GetIsolate()->DateTimeConfigurationChangeNotification( + Isolate::TimeZoneDetection::kRedetect); + }); + globalTemplate->Set(ToV8String(isolate, "__dateTimeConfigurationChangeNotification"), + drainMicrotaskTemplate); } bool Runtime::IsAlive(const Isolate* isolate) { - // speedup lookup by avoiding locking if thread locals match - // note: this can be a problem when the Runtime is deleted in a different thread that it was created - // which could happen under some specific embedding scenarios - if ((Isolate::TryGetCurrent() == isolate || (currentRuntime_ != nullptr && currentRuntime_->GetIsolate() == isolate)) - && Caches::Get((Isolate*)isolate)->IsValid()) { - return true; - } - SpinLock lock(isolatesMutex_); - return std::find(Runtime::isolates_.begin(), Runtime::isolates_.end(), isolate) != Runtime::isolates_.end(); + // speedup lookup by avoiding locking if thread locals match + // note: this can be a problem when the Runtime is deleted in a different thread that it was + // created which could happen under some specific embedding scenarios + if ((Isolate::TryGetCurrent() == isolate || + (currentRuntime_ != nullptr && currentRuntime_->GetIsolate() == isolate)) && + Caches::Get((Isolate*)isolate)->IsValid()) { + return true; + } + SpinLock lock(isolatesMutex_); + return std::find(Runtime::isolates_.begin(), Runtime::isolates_.end(), isolate) != + Runtime::isolates_.end(); } std::shared_ptr Runtime::platform_; @@ -432,4 +459,4 @@ void DisposeIsolateWhenPossible(Isolate* isolate) { thread_local Runtime* Runtime::currentRuntime_ = nullptr; SpinMutex Runtime::isolatesMutex_; -} +} // namespace tns From 3f712c462c6eeda2f82ceda36bdd2e11c7eade2f Mon Sep 17 00:00:00 2001 From: Eduardo Speroni Date: Tue, 17 Dec 2024 15:48:48 -0600 Subject: [PATCH 2/3] feat: use monotonic time for performance object --- NativeScript/runtime/Runtime.h | 2 ++ NativeScript/runtime/Runtime.mm | 14 +++++++++----- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/NativeScript/runtime/Runtime.h b/NativeScript/runtime/Runtime.h index a55080c4..5a7de0e1 100644 --- a/NativeScript/runtime/Runtime.h +++ b/NativeScript/runtime/Runtime.h @@ -77,6 +77,8 @@ class Runtime { std::unique_ptr moduleInternal_; int workerId_; CFRunLoopRef runtimeLoop_; + double startTime; + double realtimeOrigin; // TODO: refactor this. This is only needed because, during program // termination (UIApplicationMain not called) the Cache::Workers is released // (static initialization order fiasco diff --git a/NativeScript/runtime/Runtime.mm b/NativeScript/runtime/Runtime.mm index d34f9359..e9f1a0d1 100644 --- a/NativeScript/runtime/Runtime.mm +++ b/NativeScript/runtime/Runtime.mm @@ -154,6 +154,9 @@ void DisposeIsolateWhenPossible(Isolate* isolate) { v8Initialized_ = true; } + startTime = platform_->MonotonicallyIncreasingTime(); + realtimeOrigin = platform_->CurrentClockTimeMillis(); + // auto version = v8::V8::GetVersion(); Isolate::CreateParams create_params; @@ -389,16 +392,17 @@ void DisposeIsolateWhenPossible(Isolate* isolate) { Local nowFuncTemplate = FunctionTemplate::New(isolate, PerformanceNowCallback); performanceTemplate->Set(tns::ToV8String(isolate, "now"), nowFuncTemplate); + performanceTemplate->Set(tns::ToV8String(isolate, "timeOrigin"), + v8::Number::New(isolate, realtimeOrigin)); + Local performancePropertyName = ToV8String(isolate, "performance"); globalTemplate->Set(performancePropertyName, performanceTemplate); } void Runtime::PerformanceNowCallback(const FunctionCallbackInfo& args) { - std::chrono::system_clock::time_point now = std::chrono::system_clock::now(); - std::chrono::milliseconds timestampMs = - std::chrono::duration_cast(now.time_since_epoch()); - double result = timestampMs.count(); - args.GetReturnValue().Set(result); + auto runtime = Runtime::GetRuntime(args.GetIsolate()); + args.GetReturnValue().Set( + (runtime->platform_->MonotonicallyIncreasingTime() - runtime->startTime) * 1000.0); } void Runtime::DefineNativeScriptVersion(Isolate* isolate, Local globalTemplate) { From 4f9fa8c4610cf210f4c3a3aeb0c5e92f8acfde16 Mon Sep 17 00:00:00 2001 From: Eduardo Speroni Date: Tue, 17 Dec 2024 15:59:12 -0600 Subject: [PATCH 3/3] test: add performance object test --- .../app/tests/RuntimeImplementedAPIs.js | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/TestRunner/app/tests/RuntimeImplementedAPIs.js b/TestRunner/app/tests/RuntimeImplementedAPIs.js index f9b1b6ba..fe3bf575 100644 --- a/TestRunner/app/tests/RuntimeImplementedAPIs.js +++ b/TestRunner/app/tests/RuntimeImplementedAPIs.js @@ -16,3 +16,31 @@ describe("Runtime exposes", function () { expect(Math.abs(dateDelta - timeDelta)).toBeLessThan(dateDelta * 0.25); }); }); + +describe("Performance object", () => { + it("should be available", () => { + expect(performance).toBeDefined(); + }); + it("should have a now function", () => { + expect(performance.now).toBeDefined(); + }); + it("should have a now function that returns a number", () => { + expect(typeof performance.now()).toBe("number"); + }); + it("should have timeOrigin", () => { + expect(performance.timeOrigin).toBeDefined(); + }); + it("should have timeOrigin that is a number", () => { + expect(typeof performance.timeOrigin).toBe("number"); + }); + it("should have timeOrigin that is greater than 0", () => { + expect(performance.timeOrigin).toBeGreaterThan(0); + }); + it("should be close to the current time", () => { + const dateNow = Date.now(); + const performanceNow = performance.now(); + const timeOrigin = performance.timeOrigin; + const performanceAccurateNow = timeOrigin + performanceNow; + expect(Math.abs(dateNow - performanceAccurateNow)).toBeLessThan(10); + }); +});