From f26d72c769c936b3ead647933f5da1af6c5c4434 Mon Sep 17 00:00:00 2001 From: Nathan Walker Date: Tue, 4 Mar 2025 12:52:18 -0800 Subject: [PATCH 1/4] revert: "feat: visionOS unit tests" --- README.md | 3 -- TestFixtures/Api/TNSVersions.h | 51 +++++++------------ TestFixtures/exported-symbols.txt | 9 ---- .../app/tests/Infrastructure/simulator.js | 6 --- TestRunner/app/tests/MetadataTests.js | 8 +-- TestRunner/app/tests/VersionDiffTests.js | 16 +----- v8ios.xcodeproj/project.pbxproj | 26 ++-------- 7 files changed, 26 insertions(+), 93 deletions(-) diff --git a/README.md b/README.md index 79d1303a..60a25cd4 100644 --- a/README.md +++ b/README.md @@ -5,9 +5,6 @@ To start diving into the v8 iOS runtime make sure you have Xcode and [Homebrew]( # Install CMake brew install cmake -# Install jq (for scripting json replacements) -brew install jq - # (Optional) Install clang-format to format the code brew install clang-format diff --git a/TestFixtures/Api/TNSVersions.h b/TestFixtures/Api/TNSVersions.h index 04c2ce2a..fba2328d 100644 --- a/TestFixtures/Api/TNSVersions.h +++ b/TestFixtures/Api/TNSVersions.h @@ -1,28 +1,21 @@ -#define generateVersionDeclarations(V1, V2) \ - __attribute__((availability(ios, introduced = V1))) \ - __attribute__((availability(visionos, introduced = V1))) @interface TNSInterface \ - ##V2##Plus : NSObject @end \ - \ - @interface TNSInterfaceMembers \ - ##V2 : NSObject @property int property __attribute__((availability(ios, introduced = V1))) \ - __attribute__((availability(visionos, introduced = V1))); \ - \ - +(void)staticMethod __attribute__((availability(ios, introduced = V1))) \ - __attribute__((availability(visionos, introduced = V1))); \ - \ - -(void)instanceMethod __attribute__((availability(ios, introduced = V1))) \ - __attribute__((availability(visionos, introduced = V1))); \ - @end \ - \ - __attribute__((availability(ios, introduced = V1))) \ - __attribute__((availability(visionos, introduced = V1))) void TNSFunction##V2##Plus(); \ - \ - __attribute__((availability(ios, introduced = V1))) __attribute__( \ - (availability(visionos, introduced = V1))) extern const int TNSConstant##V2##Plus; \ - \ - enum TNSEnum##V2##Plus { TNSEnum##V2##Member } \ - __attribute__((availability(ios, introduced = V1))) \ - __attribute__((availability(visionos, introduced = V1))) +#define generateVersionDeclarations(V1, V2) \ + __attribute__((availability(ios, introduced = V1))) @interface TNSInterface \ + ##V2##Plus : NSObject @end \ + \ + @interface TNSInterfaceMembers \ + ##V2 : NSObject @property int property __attribute__((availability(ios, introduced = V1))); \ + \ + +(void)staticMethod __attribute__((availability(ios, introduced = V1))); \ + \ + -(void)instanceMethod __attribute__((availability(ios, introduced = V1))); \ + @end \ + \ + __attribute__((availability(ios, introduced = V1))) void TNSFunction##V2##Plus(); \ + \ + __attribute__((availability(ios, introduced = V1))) extern const int TNSConstant##V2##Plus; \ + \ + enum TNSEnum##V2##Plus { TNSEnum##V2##Member } \ + __attribute__((availability(ios, introduced = V1))) #ifndef generateVersionImpl #define generateVersion(V1, V2) generateVersionDeclarations(V1, V2) @@ -52,8 +45,6 @@ generateVersion(MAJOR##.4, MAJOR##_4); \ generateVersion(MAJOR##.5, MAJOR##_5); -generateMinors(1); -generateMinors(2); generateMinors(9); generateMinors(10); generateMinors(11); @@ -67,7 +58,6 @@ generateMinors(15); #define MAX_AVAILABILITY 31.7 __attribute__((availability(ios, introduced = MAX_AVAILABILITY))) -__attribute__((availability(visionos, introduced = MAX_AVAILABILITY))) @protocol TNSProtocolNeverAvailable @property(class, readonly) int staticPropertyFromProtocolNeverAvailable; @@ -84,9 +74,7 @@ __attribute__((availability(visionos, introduced = MAX_AVAILABILITY))) @end -__attribute__((availability(ios, introduced = 1.0))) -__attribute__((availability(visionos, introduced = 1.0))) -@protocol TNSProtocolAlwaysAvailable +__attribute__((availability(ios, introduced = 1.0))) @protocol TNSProtocolAlwaysAvailable @property(class, readonly) int staticPropertyFromProtocolAlwaysAvailable; @@ -103,7 +91,6 @@ __attribute__((availability(visionos, introduced = 1.0))) @end __attribute__((availability(ios, introduced = MAX_AVAILABILITY))) -__attribute__((availability(visionos, introduced = MAX_AVAILABILITY))) @interface TNSInterfaceNeverAvailable : TNSInterfaceAlwaysAvailable @end diff --git a/TestFixtures/exported-symbols.txt b/TestFixtures/exported-symbols.txt index c506cba1..fb47d646 100644 --- a/TestFixtures/exported-symbols.txt +++ b/TestFixtures/exported-symbols.txt @@ -58,9 +58,6 @@ _functionWithUShortPtr _TNSIsConfigurationDebug _TNSClearOutput _TNSConstant -_TNSConstant1_0Plus -_TNSConstant1_1Plus -_TNSConstant1_2Plus _TNSConstant10_0Plus _TNSConstant10_1Plus _TNSConstant10_2Plus @@ -97,18 +94,12 @@ _TNSConstant15_2Plus _TNSConstant15_3Plus _TNSConstant15_4Plus _TNSConstant15_5Plus -_TNSConstant1_0Plus -_TNSConstant1_1Plus -_TNSConstant1_2Plus _TNSConstant9_0Plus _TNSConstant9_1Plus _TNSConstant9_2Plus _TNSConstant9_3Plus _TNSConstant9_4Plus _TNSConstant9_5Plus -_TNSFunction1_0Plus -_TNSFunction1_1Plus -_TNSFunction1_2Plus _TNSFunction9_0Plus _TNSFunction9_1Plus _TNSFunction9_2Plus diff --git a/TestRunner/app/tests/Infrastructure/simulator.js b/TestRunner/app/tests/Infrastructure/simulator.js index bf3f297c..061877f8 100644 --- a/TestRunner/app/tests/Infrastructure/simulator.js +++ b/TestRunner/app/tests/Infrastructure/simulator.js @@ -7,9 +7,3 @@ function isSimulator() { } global.isSimulator = isSimulator(); - -function isVision() { - return UIDevice.currentDevice.model.toLowerCase().includes('vision'); -} - -global.isVision = isVision(); diff --git a/TestRunner/app/tests/MetadataTests.js b/TestRunner/app/tests/MetadataTests.js index 7d61eb12..d49c937e 100644 --- a/TestRunner/app/tests/MetadataTests.js +++ b/TestRunner/app/tests/MetadataTests.js @@ -10,13 +10,7 @@ describe("Metadata", function () { const swiftLikeObj = TNSSwiftLikeFactory.create(); expect(swiftLikeObj.constructor).toBe(global.TNSSwiftLike); expect(swiftLikeObj.constructor.name).toBe("_TtC17NativeScriptTests12TNSSwiftLike"); - let majorVersion = 13; - let minorVersion = 4; - if (isVision) { - majorVersion = 1; - minorVersion = 0; - } - var expectedName = NSProcessInfo.processInfo.isOperatingSystemAtLeastVersion({ majorVersion: majorVersion, minorVersion: minorVersion, patchVersion: 0 }) + var expectedName = NSProcessInfo.processInfo.isOperatingSystemAtLeastVersion({ majorVersion: 13, minorVersion: 4, patchVersion: 0 }) ? "_TtC17NativeScriptTests12TNSSwiftLike" : "NativeScriptTests.TNSSwiftLike"; expect(NSString.stringWithUTF8String(class_getName(swiftLikeObj.constructor)).toString()).toBe(expectedName); diff --git a/TestRunner/app/tests/VersionDiffTests.js b/TestRunner/app/tests/VersionDiffTests.js index 2bb0dd0d..7ef9aec1 100644 --- a/TestRunner/app/tests/VersionDiffTests.js +++ b/TestRunner/app/tests/VersionDiffTests.js @@ -17,16 +17,8 @@ describe(module.id, function() { } function forEachVersion(action) { - let majorMinVersion = 9; - let majorMaxVersion = 15; - let minorMaxVersion = 5; - if (isVision) { - majorMinVersion = 1; - majorMaxVersion = 1; - minorMaxVersion = 2; - } - for (var major = majorMinVersion; major <= majorMaxVersion; major++) { - for (var minor = 0; minor <= minorMaxVersion; minor++) { + for (var major = 9; major <= 15; major++) { + for (var minor = 0; minor <= 5; minor++) { action(major, minor); } } @@ -82,10 +74,6 @@ describe(module.id, function() { }); it("Base class which is unavailable should be skipped", function() { - if (isVision) { - pending(); - return; - } // Test case inspired from MTLArrayType(8.0) : MTLType(11.0) : NSObject // TNSInterfaceNeverAvailableDescendant : TNSInterfaceNeverAvailable(API31.7 - skipped) : TNSInterfaceAlwaysAvailable expect(Object.getPrototypeOf(TNSInterfaceNeverAvailableDescendant).toString()).toBe(TNSInterfaceAlwaysAvailable.toString(), "TNSInterfaceNeverAvailable base class should be skipped as it is unavailable"); diff --git a/v8ios.xcodeproj/project.pbxproj b/v8ios.xcodeproj/project.pbxproj index 584c17bc..54f685c8 100644 --- a/v8ios.xcodeproj/project.pbxproj +++ b/v8ios.xcodeproj/project.pbxproj @@ -7,7 +7,9 @@ objects = { /* Begin PBXBuildFile section */ + 2B5088A62BBEB92300F6EB68 /* metadata-arm64.bin in Resources */ = {isa = PBXBuildFile; fileRef = 2BFE21ED2AC1AC3100307752 /* metadata-arm64.bin */; }; 2B5088A72BBEC1BC00F6EB68 /* TNSWidgets.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = C20525A72577D86600C12A5C /* TNSWidgets.xcframework */; }; + 2B5088A82BBEC1BC00F6EB68 /* TNSWidgets.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = C20525A72577D86600C12A5C /* TNSWidgets.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 2B7EA6AF2353477000E5184E /* NativeScriptException.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2B7EA6AD2353476F00E5184E /* NativeScriptException.mm */; }; 2B7EA6B02353477000E5184E /* NativeScriptException.h in Headers */ = {isa = PBXBuildFile; fileRef = 2B7EA6AE2353477000E5184E /* NativeScriptException.h */; }; 2BFE22062AC1C93100307752 /* metadata-arm64.bin in Resources */ = {isa = PBXBuildFile; fileRef = 2BFE22052AC1C93100307752 /* metadata-arm64.bin */; }; @@ -1848,6 +1850,7 @@ files = ( C27E5DB522F3206B00498ED0 /* app in Resources */, C27E5DBA22F324C200498ED0 /* Default-568h@2x.png in Resources */, + 2B5088A62BBEB92300F6EB68 /* metadata-arm64.bin in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2287,6 +2290,7 @@ }; C293752B229FC4740075CB16 /* PBXTargetDependency */ = { isa = PBXTargetDependency; + platformFilter = ios; target = C29374E1229FC0F60075CB16 /* TestFixtures */; targetProxy = C293752A229FC4740075CB16 /* PBXContainerItemProxy */; }; @@ -2380,7 +2384,6 @@ C23992CE236C2D6E00D2F720 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - ALLOW_TARGET_PLATFORM_SPECIALIZATION = YES; ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; CLANG_ENABLE_MODULES = NO; CODE_SIGN_STYLE = Automatic; @@ -2396,8 +2399,6 @@ ); PRODUCT_BUNDLE_IDENTIFIER = org.nativescript.TestRunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "xrsimulator xros iphonesimulator iphoneos"; - SUPPORTS_MACCATALYST = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; @@ -2409,7 +2410,6 @@ C23992CF236C2D6E00D2F720 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - ALLOW_TARGET_PLATFORM_SPECIALIZATION = YES; ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; CLANG_ENABLE_MODULES = NO; CODE_SIGN_STYLE = Automatic; @@ -2425,8 +2425,6 @@ ); PRODUCT_BUNDLE_IDENTIFIER = org.nativescript.TestRunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "xrsimulator xros iphonesimulator iphoneos"; - SUPPORTS_MACCATALYST = YES; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; @@ -2559,7 +2557,6 @@ C27E5D9F22F31CCC00498ED0 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - ALLOW_TARGET_PLATFORM_SPECIALIZATION = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_CXX_LANGUAGE_STANDARD = "compiler-default"; CLANG_CXX_LIBRARY = "compiler-default"; @@ -2591,8 +2588,6 @@ ); PRODUCT_BUNDLE_IDENTIFIER = org.nativescript.AppWithModules; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "xrsimulator xros iphonesimulator iphoneos"; - SUPPORTS_MACCATALYST = YES; TARGETED_DEVICE_FAMILY = 1; }; name = Debug; @@ -2600,7 +2595,6 @@ C27E5DA022F31CCC00498ED0 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - ALLOW_TARGET_PLATFORM_SPECIALIZATION = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_CXX_LANGUAGE_STANDARD = "compiler-default"; CLANG_CXX_LIBRARY = "compiler-default"; @@ -2632,8 +2626,6 @@ ); PRODUCT_BUNDLE_IDENTIFIER = org.nativescript.AppWithModules; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "xrsimulator xros iphonesimulator iphoneos"; - SUPPORTS_MACCATALYST = YES; TARGETED_DEVICE_FAMILY = 1; }; name = Release; @@ -2641,7 +2633,6 @@ C29374E9229FC0F60075CB16 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - ALLOW_TARGET_PLATFORM_SPECIALIZATION = YES; CLANG_ENABLE_MODULES = NO; CLANG_WARN_STRICT_PROTOTYPES = NO; DEFINES_MODULE = YES; @@ -2650,10 +2641,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 13.0; OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = iphoneos; SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "xrsimulator xros iphonesimulator iphoneos"; - SUPPORTS_MACCATALYST = YES; SYMROOT = build; TARGETED_DEVICE_FAMILY = "1,2"; }; @@ -2662,7 +2650,6 @@ C29374EA229FC0F60075CB16 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - ALLOW_TARGET_PLATFORM_SPECIALIZATION = YES; CLANG_ENABLE_MODULES = NO; CLANG_WARN_STRICT_PROTOTYPES = NO; DEFINES_MODULE = YES; @@ -2672,10 +2659,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 13.0; OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = iphoneos; SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "xrsimulator xros iphonesimulator iphoneos"; - SUPPORTS_MACCATALYST = YES; SYMROOT = build; TARGETED_DEVICE_FAMILY = "1,2"; }; @@ -2863,7 +2847,6 @@ __DATA, __TNSMetadata, "\"$(SRCROOT)/NativeScript/metadata-arm64.bin\"", - "-w", ); PRODUCT_BUNDLE_IDENTIFIER = org.nativescript.NativeScript; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; @@ -2956,7 +2939,6 @@ __DATA, __TNSMetadata, "\"$(SRCROOT)/NativeScript/metadata-arm64.bin\"", - "-w", ); PRODUCT_BUNDLE_IDENTIFIER = org.nativescript.NativeScript; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; From 0adeabf24aab579bdc10900495a46e9b8287b5d9 Mon Sep 17 00:00:00 2001 From: Nathan Walker Date: Tue, 11 Mar 2025 15:55:02 -0700 Subject: [PATCH 2/4] fix: Intel simulators (#272) --- CHANGELOG.md | 4 + NativeScript/runtime/ClassBuilder.cpp | 43 +- NativeScript/runtime/ClassBuilder.mm | 1488 +++++++++++++------------ package.json | 2 +- 4 files changed, 802 insertions(+), 735 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ed5847f6..cac74800 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## [8.9.2-dev.1](https://github.com/NativeScript/ios/compare/v8.9.2-dev.0...v8.9.2-dev.1) (2025-03-11) + + + # [8.9.0](https://github.com/NativeScript/ios/compare/v8.8.2...v8.9.0) (2025-02-24) 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