From 1bdcf285c2c7b24e85ad5f493568de87588e68c3 Mon Sep 17 00:00:00 2001 From: Martin Bektchiev Date: Wed, 18 Sep 2019 08:17:43 +0300 Subject: [PATCH 1/4] chore: Improve logging in libffi build script There are optimizations which skip `configure` and `autoreconf` steps, but it's good to leave some traces in the build output in case these lead to obscure failures. --- build/scripts/build-step-libffi.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/build/scripts/build-step-libffi.sh b/build/scripts/build-step-libffi.sh index f713230f1..9f2966ebe 100755 --- a/build/scripts/build-step-libffi.sh +++ b/build/scripts/build-step-libffi.sh @@ -21,8 +21,10 @@ function build { exit 1 fi - if [ ! -e ./configure ]; then + if [ ! -e ./configure ]; then autoreconf -i + else + echo "info: ./configure exists. Skipping autoreconf." fi mkdir -p "$BINARY_DIR" && pushd "$_" @@ -41,6 +43,8 @@ function build { if [ ! -e Makefile ]; then ./../configure --disable-shared --host="$TRIPLE" + else + echo "info: Makefile exists. Skipping configure." fi make From 22babd9c62d75ca6dab74e864a8d7771ae787467 Mon Sep 17 00:00:00 2001 From: Martin Bektchiev Date: Wed, 18 Sep 2019 08:39:27 +0300 Subject: [PATCH 2/4] fix(runtime): Crashes after destroying a worker runtime * Check for destroyed `TNSRuntime` and handle gracefully in `dealloc`. Sometimes native objects having JS counterparts could live longer than their respective `TNSRuntimes` (e.g. JS callbacks that have been subscribed for events from `NSNotificationCenter`) * Add `RELEASE_ASSERT`s for alive `TNSRuntime` in all places in the code where this is expected. * Allow creation of an `ObjCWrapperObject` without an alive `TNSRuntime`. This is needed when the native object needs to be accessed from another VM (e.g. accessing the handler to destroy it from the main (notifications producer) thread when the worker has already exited) * [webkit] Guard against destroyed and `nullptr` VM's in locking primitives --- .../Enumeration/TNSFastEnumerationAdapter.mm | 4 +++- .../ObjC/Inheritance/ObjCClassBuilder.mm | 18 +++++++++++------- src/NativeScript/ObjC/ObjCTypes.mm | 8 +++++--- src/NativeScript/ObjC/ObjCWrapperObject.mm | 15 +++++++++++---- src/NativeScript/ObjC/TNSArrayAdapter.mm | 4 +++- src/NativeScript/ObjC/TNSDataAdapter.mm | 4 +++- src/NativeScript/ObjC/TNSDictionaryAdapter.mm | 4 +++- src/webkit | 2 +- 8 files changed, 40 insertions(+), 19 deletions(-) diff --git a/src/NativeScript/ObjC/Enumeration/TNSFastEnumerationAdapter.mm b/src/NativeScript/ObjC/Enumeration/TNSFastEnumerationAdapter.mm index bfc4fcf0b..13d7f274e 100644 --- a/src/NativeScript/ObjC/Enumeration/TNSFastEnumerationAdapter.mm +++ b/src/NativeScript/ObjC/Enumeration/TNSFastEnumerationAdapter.mm @@ -25,7 +25,9 @@ NSUInteger TNSFastEnumerationAdapter(id self, NSFastEnumerationState* state, id if (state->state == State::Uninitialized) { ExecState* execState = globalObject->globalExec(); - JSObject* wrapper = [TNSRuntime runtimeForVM:&globalObject->vm()] -> _objectMap.get()->get(self); + auto runtime = [TNSRuntime runtimeForVM:&globalObject->vm()]; + RELEASE_ASSERT_WITH_MESSAGE(runtime, "The runtime is deallocated."); + JSObject* wrapper = runtime->_objectMap.get()->get(self); RELEASE_ASSERT(wrapper); JSC::VM& vm = execState->vm(); diff --git a/src/NativeScript/ObjC/Inheritance/ObjCClassBuilder.mm b/src/NativeScript/ObjC/Inheritance/ObjCClassBuilder.mm index 626f6d3f9..51b94dc79 100644 --- a/src/NativeScript/ObjC/Inheritance/ObjCClassBuilder.mm +++ b/src/NativeScript/ObjC/Inheritance/ObjCClassBuilder.mm @@ -83,10 +83,12 @@ static void attachDerivedMachinery(GlobalObject* globalObject, Class newKlass, J IMP retain = findNotOverridenMethod(newKlass, @selector(retain)); IMP newRetain = imp_implementationWithBlock(^(id self) { if ([self retainCount] == 1) { - if (JSObject* object = [TNSRuntime runtimeForVM:&globalObject->vm()] -> _objectMap.get()->get(self)) { - JSLockHolder lockHolder(globalObject->vm()); - /// TODO: This gcProtect() call might render the same call in the allocWithZone override unnecessary. Check if this is true. - gcProtect(object); + if (auto runtime = [TNSRuntime runtimeForVM:&globalObject->vm()]) { + if (JSObject* object = runtime->_objectMap.get()->get(self)) { + JSLockHolder lockHolder(globalObject->vm()); + /// TODO: This gcProtect() call might render the same call in the allocWithZone override unnecessary. Check if this is true. + gcProtect(object); + } } } @@ -97,9 +99,11 @@ static void attachDerivedMachinery(GlobalObject* globalObject, Class newKlass, J void (*release)(id, SEL) = (void (*)(id, SEL))findNotOverridenMethod(newKlass, @selector(release)); IMP newRelease = imp_implementationWithBlock(^(id self) { if ([self retainCount] == 2) { - if (JSObject* object = [TNSRuntime runtimeForVM:&globalObject->vm()] -> _objectMap.get()->get(self)) { - JSLockHolder lockHolder(globalObject->vm()); - gcUnprotect(object); + if (auto runtime = [TNSRuntime runtimeForVM:&globalObject->vm()]) { + if (JSObject* object = runtime->_objectMap.get()->get(self)) { + JSLockHolder lockHolder(globalObject->vm()); + gcUnprotect(object); + } } } diff --git a/src/NativeScript/ObjC/ObjCTypes.mm b/src/NativeScript/ObjC/ObjCTypes.mm index 00229079b..59d92f623 100644 --- a/src/NativeScript/ObjC/ObjCTypes.mm +++ b/src/NativeScript/ObjC/ObjCTypes.mm @@ -160,9 +160,11 @@ JSValue toValue(ExecState* execState, id object, Structure* (^structureResolver) UNUSED_PARAM(vm); #endif - if (JSObject* wrapper = [TNSRuntime runtimeForVM:&globalObject->vm()] -> _objectMap.get()->get(object)) { - ASSERT(wrapper->classInfo(vm) != ObjCWrapperObject::info() || jsCast(wrapper)->wrappedObject() == object); - return wrapper; + if (auto runtime = [TNSRuntime runtimeForVM:&globalObject->vm()]) { + if (JSObject* wrapper = runtime->_objectMap.get()->get(object)) { + ASSERT(wrapper->classInfo(vm) != ObjCWrapperObject::info() || jsCast(wrapper)->wrappedObject() == object); + return wrapper; + } } return ObjCWrapperObject::create(execState->vm(), structureResolver(), object, globalObject).get(); diff --git a/src/NativeScript/ObjC/ObjCWrapperObject.mm b/src/NativeScript/ObjC/ObjCWrapperObject.mm index 97ce2c4a7..120ae5fb1 100644 --- a/src/NativeScript/ObjC/ObjCWrapperObject.mm +++ b/src/NativeScript/ObjC/ObjCWrapperObject.mm @@ -19,7 +19,8 @@ void ObjCWrapperObject::finishCreation(VM& vm, id wrappedObject, GlobalObject* globalObject) { Base::finishCreation(vm); - this->_objectMap = [TNSRuntime runtimeForVM:&globalObject->vm()] -> _objectMap.get(); + auto runtime = [TNSRuntime runtimeForVM:&globalObject->vm()]; + this->_objectMap = runtime != nullptr ? runtime->_objectMap.get() : nullptr; this->setWrappedObject(wrappedObject); this->_canSetObjectAtIndexedSubscript = [wrappedObject respondsToSelector:@selector(setObject: atIndexedSubscript:)]; @@ -49,12 +50,16 @@ } void ObjCWrapperObject::removeFromCache() { - this->_objectMap->remove(this->_wrappedObject.get()); + if (this->_objectMap) { + this->_objectMap->remove(this->_wrappedObject.get()); + } } void ObjCWrapperObject::setWrappedObject(id wrappedObject) { if (this->_wrappedObject) { - this->_objectMap->remove(this->_wrappedObject.get()); + if (this->_objectMap) { + this->_objectMap->remove(this->_wrappedObject.get()); + } if ([this->_wrappedObject.get() conformsToProtocol:@protocol(TNSDerivedClass)] && [this->_wrappedObject retainCount] > 1) { // Derived classes have additional logic for protecting JS counterparts in their retain/release methods when the retention @@ -71,7 +76,9 @@ this->_wrappedObject = wrappedObject; if (wrappedObject) { - this->_objectMap->set(wrappedObject, this); + if (this->_objectMap) { + this->_objectMap->set(wrappedObject, this); + } if ([wrappedObject conformsToProtocol:@protocol(TNSDerivedClass)] && [wrappedObject retainCount] > 1) { // Derived classes have additional logic for protecting JS counterparts in their retain/release methods when the retention diff --git a/src/NativeScript/ObjC/TNSArrayAdapter.mm b/src/NativeScript/ObjC/TNSArrayAdapter.mm index c6235e1e6..fdc68582f 100644 --- a/src/NativeScript/ObjC/TNSArrayAdapter.mm +++ b/src/NativeScript/ObjC/TNSArrayAdapter.mm @@ -24,7 +24,9 @@ - (instancetype)initWithJSObject:(JSObject*)jsObject execState:(ExecState*)execS self->_object = Strong(execState->vm(), jsObject); self->_globalObject = execState->lexicalGlobalObject(); VM& vm = execState->vm(); - [TNSRuntime runtimeForVM:&vm] -> _objectMap.get()->set(self, jsObject); + auto runtime = [TNSRuntime runtimeForVM:&vm]; + RELEASE_ASSERT_WITH_MESSAGE(runtime, "The runtime is deallocated."); + runtime->_objectMap.get()->set(self, jsObject); } return self; diff --git a/src/NativeScript/ObjC/TNSDataAdapter.mm b/src/NativeScript/ObjC/TNSDataAdapter.mm index 43b2b7443..1b9b89658 100644 --- a/src/NativeScript/ObjC/TNSDataAdapter.mm +++ b/src/NativeScript/ObjC/TNSDataAdapter.mm @@ -25,7 +25,9 @@ - (instancetype)initWithJSObject:(JSObject*)jsObject execState:(ExecState*)execS VM& vm = execState->vm(); self->_object.set(vm, jsObject); self->_globalObject = execState->lexicalGlobalObject(); - [TNSRuntime runtimeForVM:&vm] -> _objectMap.get()->set(self, jsObject); + auto runtime = [TNSRuntime runtimeForVM:&vm]; + RELEASE_ASSERT_WITH_MESSAGE(runtime, "The runtime is deallocated."); + runtime->_objectMap.get()->set(self, jsObject); } return self; diff --git a/src/NativeScript/ObjC/TNSDictionaryAdapter.mm b/src/NativeScript/ObjC/TNSDictionaryAdapter.mm index 193601ad8..44302fcc8 100644 --- a/src/NativeScript/ObjC/TNSDictionaryAdapter.mm +++ b/src/NativeScript/ObjC/TNSDictionaryAdapter.mm @@ -114,7 +114,9 @@ - (instancetype)initWithJSObject:(JSObject*)jsObject execState:(ExecState*)execS self->_object = Strong(execState->vm(), jsObject); self->_globalObject = execState->lexicalGlobalObject(); self->_vm = &execState->vm(); - [TNSRuntime runtimeForVM:self->_vm] -> _objectMap.get()->set(self, jsObject); + auto runtime = [TNSRuntime runtimeForVM:self->_vm]; + RELEASE_ASSERT_WITH_MESSAGE(runtime, "The runtime is deallocated."); + runtime->_objectMap.get()->set(self, jsObject); } return self; diff --git a/src/webkit b/src/webkit index a483b704b..3701f58f1 160000 --- a/src/webkit +++ b/src/webkit @@ -1 +1 @@ -Subproject commit a483b704b7288588adc977ea5fdbb92c755f46d4 +Subproject commit 3701f58f1be6d6e8d2ea477e1c7fa6d822380d85 From ca5ea5ba1a7579b8f9335532bbbba9c2203a41f5 Mon Sep 17 00:00:00 2001 From: Martin Bektchiev Date: Wed, 16 Oct 2019 15:00:04 +0300 Subject: [PATCH 3/4] fix(runtime): Keep VM alive as long as there are objects referring it * Add strong references to VM from blocks and native adapters for JS objects * Invoke C++ destructor in `disposeBlock` instead of the explicit clearing its member(s) --- src/NativeScript/ObjC/Block/ObjCBlockType.mm | 4 ++- src/NativeScript/ObjC/TNSArrayAdapter.mm | 13 +++++----- src/NativeScript/ObjC/TNSDataAdapter.mm | 11 ++++---- src/NativeScript/ObjC/TNSDictionaryAdapter.mm | 26 +++++++++---------- 4 files changed, 29 insertions(+), 25 deletions(-) diff --git a/src/NativeScript/ObjC/Block/ObjCBlockType.mm b/src/NativeScript/ObjC/Block/ObjCBlockType.mm index cf83a63e6..e2655a270 100644 --- a/src/NativeScript/ObjC/Block/ObjCBlockType.mm +++ b/src/NativeScript/ObjC/Block/ObjCBlockType.mm @@ -37,6 +37,7 @@ int32_t reserved; const void* invoke; JSBlockDescriptor* descriptor; + Ref vm; Strong callback; @@ -55,6 +56,7 @@ static CFTypeRef createBlock(ExecState* execState, JSCell* function, ObjCBlockTy .reserved = 0, .invoke = blockCallback->functionPointer(), .descriptor = &kJSBlockDescriptor, + .vm = execState->vm(), }; blockPointer->callback.set(execState->vm(), blockCallback.get()); @@ -70,7 +72,7 @@ static void copyBlock(JSBlock* dst, const JSBlock* src) { static void disposeBlock(JSBlock* block) { JSLockHolder locker(block->callback->vm()); - block->callback.clear(); + block->~JSBlock(); } static JSC::JSCell* getJSFunction(id block) { diff --git a/src/NativeScript/ObjC/TNSArrayAdapter.mm b/src/NativeScript/ObjC/TNSArrayAdapter.mm index fdc68582f..5ef25be11 100644 --- a/src/NativeScript/ObjC/TNSArrayAdapter.mm +++ b/src/NativeScript/ObjC/TNSArrayAdapter.mm @@ -17,14 +17,15 @@ @implementation TNSArrayAdapter { Strong _object; JSGlobalObject* _globalObject; + RefPtr _vm; } - (instancetype)initWithJSObject:(JSObject*)jsObject execState:(ExecState*)execState { if (self) { self->_object = Strong(execState->vm(), jsObject); self->_globalObject = execState->lexicalGlobalObject(); - VM& vm = execState->vm(); - auto runtime = [TNSRuntime runtimeForVM:&vm]; + self->_vm = &execState->vm(); + auto runtime = [TNSRuntime runtimeForVM:self->_vm.get()]; RELEASE_ASSERT_WITH_MESSAGE(runtime, "The runtime is deallocated."); runtime->_objectMap.get()->set(self, jsObject); } @@ -33,7 +34,7 @@ - (instancetype)initWithJSObject:(JSObject*)jsObject execState:(ExecState*)execS } - (NSUInteger)count { - VM& vm = self->_globalObject->vm(); + VM& vm = *self->_vm.get(); RELEASE_ASSERT_WITH_MESSAGE([TNSRuntime runtimeForVM:&vm], "The runtime is deallocated."); JSLockHolder lock(vm); @@ -46,7 +47,7 @@ - (NSUInteger)count { } - (id)objectAtIndex:(NSUInteger)index { - VM& vm = self->_globalObject->vm(); + VM& vm = *self->_vm.get(); RELEASE_ASSERT_WITH_MESSAGE([TNSRuntime runtimeForVM:&vm], "The runtime is deallocated."); if (!(index < [self count])) { @throw [NSException exceptionWithName:NSRangeException reason:[NSString stringWithFormat:@"Index (%tu) out of bounds", index] userInfo:nil]; @@ -58,7 +59,7 @@ - (id)objectAtIndex:(NSUInteger)index { } - (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState*)state objects:(id[])buffer count:(NSUInteger)len { - VM& vm = self->_globalObject->vm(); + VM& vm = *self->_vm.get(); RELEASE_ASSERT_WITH_MESSAGE([TNSRuntime runtimeForVM:&vm], "The runtime is deallocated."); JSLockHolder lock(vm); @@ -89,7 +90,7 @@ - (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState*)state objects - (void)dealloc { { - VM& vm = self->_globalObject->vm(); + VM& vm = *self->_vm.get(); JSLockHolder lock(vm); if (TNSRuntime* runtime = [TNSRuntime runtimeForVM:&vm]) { runtime->_objectMap.get()->remove(self); diff --git a/src/NativeScript/ObjC/TNSDataAdapter.mm b/src/NativeScript/ObjC/TNSDataAdapter.mm index 1b9b89658..69f6e9f52 100644 --- a/src/NativeScript/ObjC/TNSDataAdapter.mm +++ b/src/NativeScript/ObjC/TNSDataAdapter.mm @@ -18,14 +18,15 @@ @implementation TNSDataAdapter { Strong _object; JSGlobalObject* _globalObject; + RefPtr _vm; } - (instancetype)initWithJSObject:(JSObject*)jsObject execState:(ExecState*)execState { if (self) { - VM& vm = execState->vm(); - self->_object.set(vm, jsObject); + self->_vm = &execState->vm(); + self->_object.set(*self->_vm.get(), jsObject); self->_globalObject = execState->lexicalGlobalObject(); - auto runtime = [TNSRuntime runtimeForVM:&vm]; + auto runtime = [TNSRuntime runtimeForVM:self->_vm.get()]; RELEASE_ASSERT_WITH_MESSAGE(runtime, "The runtime is deallocated."); runtime->_objectMap.get()->set(self, jsObject); } @@ -38,7 +39,7 @@ - (const void*)bytes { } - (void*)mutableBytes { - VM& vm = self->_globalObject->vm(); + VM& vm = *self->_vm.get(); RELEASE_ASSERT_WITH_MESSAGE([TNSRuntime runtimeForVM:&vm], "The runtime is deallocated."); JSLockHolder lock(vm); @@ -55,7 +56,7 @@ - (void*)mutableBytes { } - (NSUInteger)length { - VM& vm = self->_globalObject->vm(); + VM& vm = *self->_vm.get(); ExecState* execState = self->_globalObject->globalExec(); RELEASE_ASSERT_WITH_MESSAGE([TNSRuntime runtimeForVM:&vm], "The runtime is deallocated."); JSLockHolder lock(vm); diff --git a/src/NativeScript/ObjC/TNSDictionaryAdapter.mm b/src/NativeScript/ObjC/TNSDictionaryAdapter.mm index 44302fcc8..764160d1d 100644 --- a/src/NativeScript/ObjC/TNSDictionaryAdapter.mm +++ b/src/NativeScript/ObjC/TNSDictionaryAdapter.mm @@ -106,7 +106,7 @@ - (NSArray*)allObjects { @implementation TNSDictionaryAdapter { Strong _object; JSGlobalObject* _globalObject; - VM* _vm; + RefPtr _vm; } - (instancetype)initWithJSObject:(JSObject*)jsObject execState:(ExecState*)execState { @@ -114,7 +114,7 @@ - (instancetype)initWithJSObject:(JSObject*)jsObject execState:(ExecState*)execS self->_object = Strong(execState->vm(), jsObject); self->_globalObject = execState->lexicalGlobalObject(); self->_vm = &execState->vm(); - auto runtime = [TNSRuntime runtimeForVM:self->_vm]; + auto runtime = [TNSRuntime runtimeForVM:self->_vm.get()]; RELEASE_ASSERT_WITH_MESSAGE(runtime, "The runtime is deallocated."); runtime->_objectMap.get()->set(self, jsObject); } @@ -123,24 +123,24 @@ - (instancetype)initWithJSObject:(JSObject*)jsObject execState:(ExecState*)execS } - (NSUInteger)count { - JSLockHolder lock(self->_vm); + JSLockHolder lock(self->_vm.get()); JSObject* object = self->_object.get(); if (JSMap* map = jsDynamicCast(*self->_vm, object)) { return map->size(); } - PropertyNameArray properties(self->_vm, PropertyNameMode::Strings, PrivateSymbolMode::Include); + PropertyNameArray properties(self->_vm.get(), PropertyNameMode::Strings, PrivateSymbolMode::Include); object->methodTable(*self->_vm)->getOwnPropertyNames(object, self->_globalObject->globalExec(), properties, EnumerationMode()); return properties.size(); } - (id)objectForKey:(id)aKey { - RELEASE_ASSERT_WITH_MESSAGE([TNSRuntime runtimeForVM:self->_vm], "The runtime is deallocated."); - JSLockHolder lock(self->_vm); + RELEASE_ASSERT_WITH_MESSAGE([TNSRuntime runtimeForVM:self->_vm.get()], "The runtime is deallocated."); + JSLockHolder lock(self->_vm.get()); JSObject* object = self->_object.get(); - if (JSMap* map = jsDynamicCast(*self->_vm, object)) { + if (JSMap* map = jsDynamicCast(*self->_vm.get(), object)) { JSValue key = toValue(self->_globalObject->globalExec(), aKey); return toObject(self->_globalObject->globalExec(), map->get(self->_globalObject->globalExec(), key)); } else if ([aKey isKindOfClass:[NSString class]]) { @@ -155,23 +155,23 @@ - (id)objectForKey:(id)aKey { } - (NSEnumerator*)keyEnumerator { - RELEASE_ASSERT_WITH_MESSAGE([TNSRuntime runtimeForVM:self->_vm], "The runtime is deallocated."); + RELEASE_ASSERT_WITH_MESSAGE([TNSRuntime runtimeForVM:self->_vm.get()], "The runtime is deallocated."); JSLockHolder lock(self->_globalObject->globalExec()); JSObject* object = self->_object.get(); - if (JSMap* map = jsDynamicCast(*self->_vm, object)) { + if (JSMap* map = jsDynamicCast(*self->_vm.get(), object)) { return [[[TNSDictionaryAdapterMapKeysEnumerator alloc] initWithMap:map execState:self->_globalObject->globalExec()] autorelease]; } - PropertyNameArray properties(self->_vm, PropertyNameMode::Strings, PrivateSymbolMode::Include); - object->methodTable(*self->_vm)->getOwnPropertyNames(object, self->_globalObject->globalExec(), properties, EnumerationMode()); + PropertyNameArray properties(self->_vm.get(), PropertyNameMode::Strings, PrivateSymbolMode::Include); + object->methodTable(*self->_vm.get())->getOwnPropertyNames(object, self->_globalObject->globalExec(), properties, EnumerationMode()); return [[[TNSDictionaryAdapterObjectKeysEnumerator alloc] initWithProperties:properties.releaseData()] autorelease]; } - (void)dealloc { { - JSLockHolder lock(self->_vm); - if (TNSRuntime* runtime = [TNSRuntime runtimeForVM:self->_vm]) { + JSLockHolder lock(self->_vm.get()); + if (TNSRuntime* runtime = [TNSRuntime runtimeForVM:self->_vm.get()]) { runtime->_objectMap.get()->remove(self); } // Clear Strong reference inside the locked section. Otherwise it would be done when the From 2d41cb2fa53ed50b8cb03ad96726e91c8644d846 Mon Sep 17 00:00:00 2001 From: Martin Bektchiev Date: Thu, 17 Oct 2019 17:42:25 +0300 Subject: [PATCH 4/4] fix(runtime): More crashes during worker teardown * Keep worker thread alive as long as the VM is in use * Add tests for crashes and leaked runtimes --- src/NativeScript/CMakeLists.txt | 4 ++-- ...ionCallback.cpp => FFIFunctionCallback.mm} | 0 ...BlockCallback.cpp => ObjCBlockCallback.mm} | 0 src/NativeScript/TNSRuntime+Private.h | 6 +++++ src/NativeScript/TNSRuntime.mm | 4 ++++ .../Workers/WorkerMessagingProxy.mm | 24 +++++++++++++++---- src/NativeScript/module.modulemap | 5 ++++ tests/TestFixtures/Api/TNSApi.h | 2 ++ tests/TestRunner/app/shared | 2 +- 9 files changed, 40 insertions(+), 7 deletions(-) rename src/NativeScript/Calling/{FFIFunctionCallback.cpp => FFIFunctionCallback.mm} (100%) rename src/NativeScript/ObjC/Block/{ObjCBlockCallback.cpp => ObjCBlockCallback.mm} (100%) create mode 100644 src/NativeScript/module.modulemap diff --git a/src/NativeScript/CMakeLists.txt b/src/NativeScript/CMakeLists.txt index ca264f340..940b42981 100644 --- a/src/NativeScript/CMakeLists.txt +++ b/src/NativeScript/CMakeLists.txt @@ -121,7 +121,7 @@ set(SOURCE_FILES Calling/FFICall.cpp Calling/FFICallPrototype.cpp Calling/CFunctionWrapper.mm - Calling/FFIFunctionCallback.cpp + Calling/FFIFunctionCallback.mm Calling/FunctionWrapper.mm ConstructorKey.cpp GlobalObject.mm @@ -172,7 +172,7 @@ set(SOURCE_FILES Metadata/KnownUnknownClassPair.cpp ObjC/AllocatedPlaceholder.mm ObjC/Block/ObjCBlockCall.mm - ObjC/Block/ObjCBlockCallback.cpp + ObjC/Block/ObjCBlockCallback.mm ObjC/Block/ObjCBlockType.mm ObjC/Block/ObjCBlockTypeConstructor.cpp ObjC/Constructor/ObjCConstructorBase.mm diff --git a/src/NativeScript/Calling/FFIFunctionCallback.cpp b/src/NativeScript/Calling/FFIFunctionCallback.mm similarity index 100% rename from src/NativeScript/Calling/FFIFunctionCallback.cpp rename to src/NativeScript/Calling/FFIFunctionCallback.mm diff --git a/src/NativeScript/ObjC/Block/ObjCBlockCallback.cpp b/src/NativeScript/ObjC/Block/ObjCBlockCallback.mm similarity index 100% rename from src/NativeScript/ObjC/Block/ObjCBlockCallback.cpp rename to src/NativeScript/ObjC/Block/ObjCBlockCallback.mm diff --git a/src/NativeScript/TNSRuntime+Private.h b/src/NativeScript/TNSRuntime+Private.h index af13cd485..913e3cf09 100644 --- a/src/NativeScript/TNSRuntime+Private.h +++ b/src/NativeScript/TNSRuntime+Private.h @@ -10,12 +10,18 @@ @interface TNSRuntime () { @package +#ifdef __cplusplus WTF::RefPtr _vm; JSC::Strong _globalObject; std::unique_ptr> _objectMap; +#endif //__cplusplus + NSString* _applicationPath; } +#ifdef __cplusplus + (TNSRuntime*)runtimeForVM:(JSC::VM*)vm; +#endif //__cplusplus ++ (NSPointerArray*)runtimes; @end diff --git a/src/NativeScript/TNSRuntime.mm b/src/NativeScript/TNSRuntime.mm index ca0b5d804..cc071de2b 100644 --- a/src/NativeScript/TNSRuntime.mm +++ b/src/NativeScript/TNSRuntime.mm @@ -82,6 +82,10 @@ @implementation TNSRuntime static WTF::Lock _runtimesLock; static NSPointerArray* _runtimes; ++ (NSPointerArray*)runtimes { + return _runtimes; +} + + (TNSRuntime*)current { WTF::LockHolder lock(_runtimesLock); Thread* currentThread = &WTF::Thread::current(); diff --git a/src/NativeScript/Workers/WorkerMessagingProxy.mm b/src/NativeScript/Workers/WorkerMessagingProxy.mm index bd8ddd0f4..ef60ec63f 100644 --- a/src/NativeScript/Workers/WorkerMessagingProxy.mm +++ b/src/NativeScript/Workers/WorkerMessagingProxy.mm @@ -199,20 +199,36 @@ static void workerPerformWork(void* context) { }); } + RefPtr vm; @autoreleasepool { _workerData = std::make_unique(&WTF::Thread::current(), applicationPath, entryModuleId, referrer); _workerData->runtime = [[TNSWorkerRuntime alloc] initWithApplicationPath:_workerData->applicationPath]; _workerData->globalObject()->setWorkerMessagingProxy(messagingProxy); [_workerData->runtime scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; _workerData->onCloseIdentifier = Identifier::fromString(&_workerData->globalObject()->vm(), "onclose"); + vm = &_workerData->globalObject()->vm(); CFRunLoopRun(); - workerThreadExited(); + + [_workerData->runtime removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; + [_workerData->runtime release]; + Thread::current().detach(); } - [_workerData->runtime removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; - [_workerData->runtime release]; - Thread::current().detach(); + @autoreleasepool { + // Keep thread alive as long as the VM is in use. Otherwise, its `atomicStringTable` + // will be destroyed and sporadic crashes could occur + while (vm->refCount() > 1) { + sleep(1); + } + // Destroy VM by dropping the last reference + { + JSLockHolder lock(vm.get()); + vm = nullptr; + } + + workerThreadExited(); + } } void WorkerMessagingProxy::workerPostMessageToParent(WTF::String& message) { diff --git a/src/NativeScript/module.modulemap b/src/NativeScript/module.modulemap new file mode 100644 index 000000000..18282eb0c --- /dev/null +++ b/src/NativeScript/module.modulemap @@ -0,0 +1,5 @@ +module TNSRuntimeForTests { + umbrella header "TNSRuntime+Private.h" + export * + module * { export * } +} diff --git a/tests/TestFixtures/Api/TNSApi.h b/tests/TestFixtures/Api/TNSApi.h index 5d3b7f3fc..b82697598 100644 --- a/tests/TestFixtures/Api/TNSApi.h +++ b/tests/TestFixtures/Api/TNSApi.h @@ -6,6 +6,8 @@ // Copyright (c) 2014 Jason Zhekov. All rights reserved. // +#import "../../../src/NativeScript/TNSRuntime+Private.h" + typedef NS_ENUM(NSInteger, TNSEnums) { TNSEnum1 = -1, TNSEnum2, diff --git a/tests/TestRunner/app/shared b/tests/TestRunner/app/shared index 1844a35e2..9f94fbd60 160000 --- a/tests/TestRunner/app/shared +++ b/tests/TestRunner/app/shared @@ -1 +1 @@ -Subproject commit 1844a35e20ff91f2256af866afe6ed0c4c794089 +Subproject commit 9f94fbd603dbd677026f96f6a1cb317364c40376