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 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/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/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..5ef25be11 100644 --- a/src/NativeScript/ObjC/TNSArrayAdapter.mm +++ b/src/NativeScript/ObjC/TNSArrayAdapter.mm @@ -17,21 +17,24 @@ @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(); - [TNSRuntime runtimeForVM:&vm] -> _objectMap.get()->set(self, jsObject); + 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); } return self; } - (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); @@ -44,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]; @@ -56,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); @@ -87,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 43b2b7443..69f6e9f52 100644 --- a/src/NativeScript/ObjC/TNSDataAdapter.mm +++ b/src/NativeScript/ObjC/TNSDataAdapter.mm @@ -18,14 +18,17 @@ @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(); - [TNSRuntime runtimeForVM:&vm] -> _objectMap.get()->set(self, jsObject); + auto runtime = [TNSRuntime runtimeForVM:self->_vm.get()]; + RELEASE_ASSERT_WITH_MESSAGE(runtime, "The runtime is deallocated."); + runtime->_objectMap.get()->set(self, jsObject); } return self; @@ -36,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); @@ -53,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 193601ad8..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,31 +114,33 @@ - (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.get()]; + RELEASE_ASSERT_WITH_MESSAGE(runtime, "The runtime is deallocated."); + runtime->_objectMap.get()->set(self, jsObject); } return self; } - (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]]) { @@ -153,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 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/src/webkit b/src/webkit index 39af903f8..460b24262 160000 --- a/src/webkit +++ b/src/webkit @@ -1 +1 @@ -Subproject commit 39af903f847f31f24d3eb8d48ef8e30929930357 +Subproject commit 460b2426223ec624620b50053b978020be120c07 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