diff --git a/NativeScript/runtime/ClassBuilder.cpp b/NativeScript/runtime/ClassBuilder.cpp index 10a820b9..a748e27f 100644 --- a/NativeScript/runtime/ClassBuilder.cpp +++ b/NativeScript/runtime/ClassBuilder.cpp @@ -2,34 +2,27 @@ namespace tns { -// Moved this method in a separate .cpp file because ARC destroys the class -// created with objc_allocateClassPair when the control leaves this method scope +// Moved this method in a separate .cpp file because ARC destroys the class created with objc_allocateClassPair +// when the control leaves this method scope -Class ClassBuilder::GetExtendedClass(std::string baseClassName, - std::string staticClassName) { - Class baseClass = objc_getClass(baseClassName.c_str()); - std::string name = - !staticClassName.empty() - ? staticClassName - : baseClassName + "_" + - std::to_string(++ClassBuilder::classNameCounter_); - // here we could either call objc_getClass with the name to see if the class - // already exists or we can just try allocating it, which will return nil if - // the class already exists so we try allocating it every time to avoid race - // conditions in case this method is being executed by multiple threads - Class clazz = objc_allocateClassPair(baseClass, name.c_str(), 0); +Class ClassBuilder::GetExtendedClass(std::string baseClassName, std::string staticClassName) { + Class baseClass = objc_getClass(baseClassName.c_str()); + std::string name = !staticClassName.empty() ? staticClassName : baseClassName + "_" + std::to_string(++ClassBuilder::classNameCounter_); + Class clazz = objc_getClass(name.c_str()); - if (clazz == nil) { - int i = 1; - std::string initialName = name; - while (clazz == nil) { - name = initialName + std::to_string(i++); - clazz = objc_allocateClassPair(baseClass, name.c_str(), 0); + if (clazz != nil) { + int i = 1; + std::string initialName = name; + while (clazz != nil) { + name = initialName + std::to_string(i++); + clazz = objc_getClass(name.c_str()); + } } - } - objc_registerClassPair(clazz); - return clazz; + clazz = objc_allocateClassPair(baseClass, name.c_str(), 0); + + objc_registerClassPair(clazz); + return clazz; } -} // namespace tns +} diff --git a/NativeScript/runtime/ClassBuilder.mm b/NativeScript/runtime/ClassBuilder.mm index 308b9430..53ec9f33 100644 --- a/NativeScript/runtime/ClassBuilder.mm +++ b/NativeScript/runtime/ClassBuilder.mm @@ -1,172 +1,192 @@ +#include "ClassBuilder.h" #include #include #include -#include "ClassBuilder.h" -#include "TNSDerivedClass.h" -#include "NativeScriptException.h" -#include "FastEnumerationAdapter.h" #include "ArgConverter.h" -#include "ObjectManager.h" -#include "Helpers.h" #include "Caches.h" +#include "FastEnumerationAdapter.h" +#include "Helpers.h" #include "Interop.h" +#include "NativeScriptException.h" +#include "ObjectManager.h" #include "Runtime.h" +#include "TNSDerivedClass.h" using namespace v8; namespace tns { -Local ClassBuilder::GetExtendFunction(Isolate* isolate, const InterfaceMeta* interfaceMeta) { - CacheItem* item = new CacheItem(interfaceMeta, nullptr); - Caches::Get(isolate)->registerCacheBoundObject(item); - Local ext = External::New(isolate, item); - return FunctionTemplate::New(isolate, ClassBuilder::ExtendCallback, ext); +Local ClassBuilder::GetExtendFunction(Isolate* isolate, + const InterfaceMeta* interfaceMeta) { + CacheItem* item = new CacheItem(interfaceMeta, nullptr); + Caches::Get(isolate)->registerCacheBoundObject(item); + Local ext = External::New(isolate, item); + return FunctionTemplate::New(isolate, ClassBuilder::ExtendCallback, ext); } void ClassBuilder::ExtendCallback(const FunctionCallbackInfo& info) { - Isolate* isolate = info.GetIsolate(); - - tns::Assert(info.Length() > 0 && info[0]->IsObject() && info.This()->IsFunction(), isolate); - - try { - Local context = isolate->GetCurrentContext(); - CacheItem* item = static_cast(info.Data().As()->Value()); - - Local implementationObject = info[0].As(); - Local baseFunc = info.This().As(); - std::string baseClassName = tns::ToString(isolate, baseFunc->GetName()); - - BaseDataWrapper* baseWrapper = tns::GetValue(isolate, baseFunc); - if (baseWrapper != nullptr && baseWrapper->Type() == WrapperType::ObjCClass) { - ObjCClassWrapper* classWrapper = static_cast(baseWrapper); - if (classWrapper->ExtendedClass()) { - throw NativeScriptException("Cannot extend an already extended class"); - } - } - - Local nativeSignature; - std::string staticClassName; - if (info.Length() > 1 && info[1]->IsObject()) { - nativeSignature = info[1].As(); - Local explicitClassName; - tns::Assert(nativeSignature->Get(context, tns::ToV8String(isolate, "name")).ToLocal(&explicitClassName), isolate); - if (!explicitClassName.IsEmpty() && !explicitClassName->IsNullOrUndefined()) { - staticClassName = tns::ToString(isolate, explicitClassName); - } - } - - Class extendedClass = ClassBuilder::GetExtendedClass(baseClassName, staticClassName); - class_addProtocol(extendedClass, @protocol(TNSDerivedClass)); - class_addProtocol(object_getClass(extendedClass), @protocol(TNSDerivedClass)); - - if (!nativeSignature.IsEmpty()) { - ClassBuilder::ExposeDynamicMembers(context, extendedClass, implementationObject, nativeSignature); - } else { - ClassBuilder::ExposeDynamicMethods(context, extendedClass, Local(), Local(), implementationObject); - } + Isolate* isolate = info.GetIsolate(); - auto cache = Caches::Get(isolate); - Local baseCtorFunc = cache->CtorFuncs.find(item->meta_->name())->second->Get(isolate); + tns::Assert(info.Length() > 0 && info[0]->IsObject() && info.This()->IsFunction(), isolate); - CacheItem* cacheItem = new CacheItem(nullptr, extendedClass); - Caches::Get(isolate)->registerCacheBoundObject(cacheItem); - Local ext = External::New(isolate, cacheItem); - Local extendedClassCtorFuncTemplate = FunctionTemplate::New(isolate, ExtendedClassConstructorCallback, ext); - extendedClassCtorFuncTemplate->InstanceTemplate()->SetInternalFieldCount(1); + try { + Local context = isolate->GetCurrentContext(); + CacheItem* item = static_cast(info.Data().As()->Value()); + + Local implementationObject = info[0].As(); + Local baseFunc = info.This().As(); + std::string baseClassName = tns::ToString(isolate, baseFunc->GetName()); + + BaseDataWrapper* baseWrapper = tns::GetValue(isolate, baseFunc); + if (baseWrapper != nullptr && baseWrapper->Type() == WrapperType::ObjCClass) { + ObjCClassWrapper* classWrapper = static_cast(baseWrapper); + if (classWrapper->ExtendedClass()) { + throw NativeScriptException("Cannot extend an already extended class"); + } + } - Local extendClassCtorFunc; - if (!extendedClassCtorFuncTemplate->GetFunction(context).ToLocal(&extendClassCtorFunc)) { - tns::Assert(false, isolate); - } + Local nativeSignature; + std::string staticClassName; + if (info.Length() > 1 && info[1]->IsObject()) { + nativeSignature = info[1].As(); + Local explicitClassName; + tns::Assert(nativeSignature->Get(context, tns::ToV8String(isolate, "name")) + .ToLocal(&explicitClassName), + isolate); + if (!explicitClassName.IsEmpty() && !explicitClassName->IsNullOrUndefined()) { + staticClassName = tns::ToString(isolate, explicitClassName); + } + } - Local baseProto; - bool success = baseCtorFunc->Get(context, tns::ToV8String(isolate, "prototype")).ToLocal(&baseProto); - tns::Assert(success, isolate); + Class extendedClass = ClassBuilder::GetExtendedClass(baseClassName, staticClassName); + class_addProtocol(extendedClass, @protocol(TNSDerivedClass)); + class_addProtocol(object_getClass(extendedClass), @protocol(TNSDerivedClass)); - if (!implementationObject->SetPrototype(context, baseProto).To(&success) || !success) { - tns::Assert(false, isolate); - } - if (!implementationObject->SetAccessor(context, tns::ToV8String(isolate, "super"), SuperAccessorGetterCallback, nullptr, ext).To(&success) || !success) { - tns::Assert(false, isolate); - } + if (!nativeSignature.IsEmpty()) { + ClassBuilder::ExposeDynamicMembers(context, extendedClass, implementationObject, + nativeSignature); + } else { + ClassBuilder::ExposeDynamicMethods(context, extendedClass, Local(), Local(), + implementationObject); + } - extendClassCtorFunc->SetName(tns::ToV8String(isolate, class_getName(extendedClass))); - Local extendFuncPrototypeValue; - success = extendClassCtorFunc->Get(context, tns::ToV8String(isolate, "prototype")).ToLocal(&extendFuncPrototypeValue); - tns::Assert(success && extendFuncPrototypeValue->IsObject(), isolate); - Local extendFuncPrototype = extendFuncPrototypeValue.As(); - if (!extendFuncPrototype->SetPrototype(context, implementationObject).To(&success) || !success) { - tns::Assert(false, isolate); - } + auto cache = Caches::Get(isolate); + Local baseCtorFunc = + cache->CtorFuncs.find(item->meta_->name())->second->Get(isolate); + + CacheItem* cacheItem = new CacheItem(nullptr, extendedClass); + Caches::Get(isolate)->registerCacheBoundObject(cacheItem); + Local ext = External::New(isolate, cacheItem); + Local extendedClassCtorFuncTemplate = + FunctionTemplate::New(isolate, ExtendedClassConstructorCallback, ext); + extendedClassCtorFuncTemplate->InstanceTemplate()->SetInternalFieldCount(1); + + Local extendClassCtorFunc; + if (!extendedClassCtorFuncTemplate->GetFunction(context).ToLocal(&extendClassCtorFunc)) { + tns::Assert(false, isolate); + } - if (!extendClassCtorFunc->SetPrototype(context, baseCtorFunc).To(&success) || !success) { - tns::Assert(false, isolate); - } + Local baseProto; + bool success = + baseCtorFunc->Get(context, tns::ToV8String(isolate, "prototype")).ToLocal(&baseProto); + tns::Assert(success, isolate); - std::string extendedClassName = class_getName(extendedClass); - ObjCClassWrapper* wrapper = new ObjCClassWrapper(extendedClass, true); - tns::SetValue(isolate, extendClassCtorFunc, wrapper); + if (!implementationObject->SetPrototype(context, baseProto).To(&success) || !success) { + tns::Assert(false, isolate); + } + if (!implementationObject + ->SetAccessor(context, tns::ToV8String(isolate, "super"), SuperAccessorGetterCallback, + nullptr, ext) + .To(&success) || + !success) { + tns::Assert(false, isolate); + } - auto extendedPersistent = std::make_unique>(isolate, extendClassCtorFunc); - extendedPersistent->SetWrapperClassId(Constants::ClassTypes::DataWrapper); - cache->CtorFuncs.emplace(extendedClassName, std::move(extendedPersistent)); - cache->ClassPrototypes.emplace(extendedClassName, std::make_unique>(isolate, extendFuncPrototype)); + extendClassCtorFunc->SetName(tns::ToV8String(isolate, class_getName(extendedClass))); + Local extendFuncPrototypeValue; + success = extendClassCtorFunc->Get(context, tns::ToV8String(isolate, "prototype")) + .ToLocal(&extendFuncPrototypeValue); + tns::Assert(success && extendFuncPrototypeValue->IsObject(), isolate); + Local extendFuncPrototype = extendFuncPrototypeValue.As(); + if (!extendFuncPrototype->SetPrototype(context, implementationObject).To(&success) || + !success) { + tns::Assert(false, isolate); + } - info.GetReturnValue().Set(extendClassCtorFunc); - } catch (NativeScriptException& ex) { - ex.ReThrowToV8(isolate); + if (!extendClassCtorFunc->SetPrototype(context, baseCtorFunc).To(&success) || !success) { + tns::Assert(false, isolate); } + + std::string extendedClassName = class_getName(extendedClass); + ObjCClassWrapper* wrapper = new ObjCClassWrapper(extendedClass, true); + tns::SetValue(isolate, extendClassCtorFunc, wrapper); + + auto extendedPersistent = + std::make_unique>(isolate, extendClassCtorFunc); + extendedPersistent->SetWrapperClassId(Constants::ClassTypes::DataWrapper); + cache->CtorFuncs.emplace(extendedClassName, std::move(extendedPersistent)); + cache->ClassPrototypes.emplace( + extendedClassName, std::make_unique>(isolate, extendFuncPrototype)); + + info.GetReturnValue().Set(extendClassCtorFunc); + } catch (NativeScriptException& ex) { + ex.ReThrowToV8(isolate); + } } void ClassBuilder::ExtendedClassConstructorCallback(const FunctionCallbackInfo& info) { - Isolate* isolate = info.GetIsolate(); - Local context = isolate->GetCurrentContext(); + Isolate* isolate = info.GetIsolate(); + Local context = isolate->GetCurrentContext(); - try { - CacheItem* item = static_cast(info.Data().As()->Value()); - Class klass = item->data_; + try { + CacheItem* item = static_cast(info.Data().As()->Value()); + Class klass = item->data_; - ArgConverter::ConstructObject(context, info, klass); - } catch (NativeScriptException& ex) { - ex.ReThrowToV8(isolate); - } + ArgConverter::ConstructObject(context, info, klass); + } catch (NativeScriptException& ex) { + ex.ReThrowToV8(isolate); + } } void ClassBuilder::RegisterBaseTypeScriptExtendsFunction(Local context) { - Isolate* isolate = context->GetIsolate(); - auto cache = Caches::Get(isolate); - if (cache->OriginalExtendsFunc.get() != nullptr) { - return; - } - - std::string extendsFuncScript = - "(function() { " - " function __extends(d, b) { " - " for (var p in b) {" - " if (b.hasOwnProperty(p)) {" - " d[p] = b[p];" - " }" - " }" - " function __() { this.constructor = d; }" - " d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());" - " } " - " return __extends;" - "})()"; - - Local