diff --git a/NativeScript/runtime/AOTCalls.h b/NativeScript/runtime/AOTCalls.h new file mode 100644 index 000000000..336c2d4bf --- /dev/null +++ b/NativeScript/runtime/AOTCalls.h @@ -0,0 +1,60 @@ +#ifndef AOTCalls_h +#define AOTCalls_h + +#include + +#include +#include + +#include "Common.h" +#include "NativeScriptAOT.h" + +// Binding of app-compiled AOT call stubs (see NativeScriptAOT.h) to the +// prototype templates built by MetadataBuilder. +namespace tns::aot { + +struct ClassStubs; + +struct Binding { + // Installed as the FunctionTemplate callback in place of the generic one. + v8::FunctionCallback callback = nullptr; + // Stored in the site's CacheItem::userData_, where the callback reads it. + void* handler = nullptr; +}; + +// Registers the stubs exported by the app binary, if any. Every runtime calls +// it before building its first prototype template; later calls are no-ops. +void DiscoverExternalStubs(); + +// The function handed to the app's __ns_register_aot_calls. Registrations +// outside that call are ignored. +void Register(const char* className, const char* selector, bool isStatic, + NSAOTCallHandler handler); + +// The stubs that apply to a class's template: those registered under its own +// name, then under each ObjC superclass's name, nearest first. A class's +// template can re-register a member it inherits (protocol members are added +// to every adopting class), and the own member shadows the ancestor's, so a +// stub registered for the ancestor must bind there too; it messages the same +// selector, so it is valid for the descendant. +struct StubScope { + std::vector classes; + bool empty() const { return classes.empty(); } +}; + +// Callers resolve it once per Register* pass and look members up with it. +// Empty, without touching the ObjC runtime, when no stubs are registered. +StubScope FindStubScope(const char* className); + +bool FindInstanceMethod(const StubScope& scope, SEL selector, Binding& out); +bool FindStaticMethod(const StubScope& scope, SEL selector, Binding& out); +bool FindInstanceGetter(const StubScope& scope, SEL getter, Binding& out); +bool FindStaticGetter(const StubScope& scope, SEL getter, Binding& out); + +// Counted only while MethodCallProfiler is enabled. +uint64_t ServedCallCount(); +uint64_t DeclinedCallCount(); + +} // namespace tns::aot + +#endif /* AOTCalls_h */ diff --git a/NativeScript/runtime/Interop.h b/NativeScript/runtime/Interop.h index 85a556f15..daeca55a9 100644 --- a/NativeScript/runtime/Interop.h +++ b/NativeScript/runtime/Interop.h @@ -96,6 +96,29 @@ class Interop { std::shared_ptr> parentStruct = nullptr, bool isStructMember = false, bool ownsReturnedObject = false, bool returnsUnmanaged = false, bool isInitializer = false); + // Converts an object returned by native code to its JS value. typeEncoding + // is the declared return type and may be null; without it NSMutableString + // returns are marshalled like NSString, NSProxy results get no metadata + // from the declared type, and id conformances are not attached. + static v8::Local ObjectToJsValue(v8::Local context, + id value, + const TypeEncoding* typeEncoding, + bool marshalToPrimitive, + bool ownsReturnedObject, + bool isInitializer); + static v8::Local ClassToJsValue(v8::Local context, + Class value); + // Builds the JS Error for an NSException raised by a native call: name and + // reason copied over, the exception itself attached as `nativeException`. + // Returns an empty handle if no Error could be created. `message` receives + // the text the error was created with. + static v8::Local NSExceptionToJsError( + v8::Local context, id exception, std::string& message); + static v8::Local StructToValue( + v8::Local context, void* result, const StructInfo& structInfo, + std::shared_ptr> parentStruct); + static SEL GetSwizzledMethodSelector(SEL selector); + static bool IsNumbericType(BinaryTypeEncodingType type); static void SetStructPropertyValue(v8::Local context, StructWrapper* wrapper, const StructField& field, @@ -160,20 +183,14 @@ class Interop { static bool isRefTypeEqual(const TypeEncoding* typeEncoding, const char* clazz); static v8::Local ToArray(v8::Local object); - static v8::Local StructToValue( - v8::Local context, void* result, - const StructInfo& structInfo, - std::shared_ptr> parentStruct); static const TypeEncoding* CreateEncoding(BinaryTypeEncodingType type); static v8::Local HandleOf(v8::Local context, v8::Local value); static v8::Local CallFunctionInternal(MethodCall& methodCall); - static bool IsNumbericType(BinaryTypeEncodingType type); static v8::Local GetInteropType(v8::Local context, BinaryTypeEncodingType type); static std::vector GetAdditionalProtocols( const TypeEncoding* typeEncoding); - static SEL GetSwizzledMethodSelector(SEL selector); template static inline void SetValue(void* dest, T value) { diff --git a/NativeScript/runtime/Interop.mm b/NativeScript/runtime/Interop.mm index d35a05b27..6892449c8 100644 --- a/NativeScript/runtime/Interop.mm +++ b/NativeScript/runtime/Interop.mm @@ -1059,37 +1059,10 @@ inline bool isBool() { } if (typeEncoding->type == BinaryTypeEncodingType::ClassEncoding) { - Class result = call->GetResult(); - if (result == nil) { - return Null(isolate); - } - - std::shared_ptr cache = Caches::Get(isolate); - while (true) { - const char* name = class_getName(result); - - const Meta* meta = ArgConverter::GetMeta(name); - if (meta != nullptr && - (meta->type() == MetaType::Interface || meta->type() == MetaType::ProtocolType)) { - const BaseClassMeta* baseMeta = static_cast(meta); - Class knownClass = meta->type() == MetaType::Interface ? objc_getClass(meta->name()) : nil; - KnownUnknownClassPair pair(knownClass); - std::vector emptyProtocols; - cache->ObjectCtorInitializer(context, baseMeta, pair, emptyProtocols); - } - - auto it = cache->CtorFuncs.find(name); - if (it != cache->CtorFuncs.end()) { - return it->second->Get(isolate); - } - - result = class_getSuperclass(result); - if (!result) { - break; - } + Local klass = Interop::ClassToJsValue(context, call->GetResult()); + if (!klass.IsEmpty()) { + return klass; } - - tns::Assert(false, isolate); } if (typeEncoding->type == BinaryTypeEncodingType::BlockEncoding) { @@ -1240,88 +1213,167 @@ inline bool isBool() { if (typeEncoding->type == BinaryTypeEncodingType::InterfaceDeclarationReference || typeEncoding->type == BinaryTypeEncodingType::IdEncoding || typeEncoding->type == BinaryTypeEncodingType::InstanceTypeEncoding) { - id result = call->GetResult(); + return Interop::ObjectToJsValue(context, call->GetResult(), typeEncoding, + marshalToPrimitive, ownsReturnedObject, isInitializer); + } - if (result == nil) { - return Null(isolate); - } + return Interop::GetPrimitiveReturnType(context, typeEncoding->type, call); +} - if (marshalToPrimitive && result == [NSNull null]) { - return Null(isolate); - } +Local Interop::ObjectToJsValue(Local context, id result, + const TypeEncoding* typeEncoding, bool marshalToPrimitive, + bool ownsReturnedObject, bool isInitializer) { + Isolate* isolate = v8::Isolate::GetCurrent(); - if ([result isKindOfClass:[@YES class]]) { - return v8::Boolean::New(isolate, [result boolValue]); - } + if (result == nil) { + return Null(isolate); + } - if (marshalToPrimitive && [result isKindOfClass:[NSDate class]]) { - double time = [result timeIntervalSince1970] * 1000.0; - Local date; - if (Date::New(context, time).ToLocal(&date)) { - return date; - } + if (marshalToPrimitive && result == [NSNull null]) { + return Null(isolate); + } - std::ostringstream errorStream; - errorStream << "Unable to convert " << [result description] << " to a Date object"; - std::string errorMessage = errorStream.str(); - Local error = Exception::Error(tns::ToV8String(isolate, errorMessage)); - isolate->ThrowException(error); - return Local(); + if ([result isKindOfClass:[@YES class]]) { + return v8::Boolean::New(isolate, [result boolValue]); + } + + if (marshalToPrimitive && [result isKindOfClass:[NSDate class]]) { + double time = [result timeIntervalSince1970] * 1000.0; + Local date; + if (Date::New(context, time).ToLocal(&date)) { + return date; } - if (marshalToPrimitive && [result isKindOfClass:[NSString class]]) { - if (typeEncoding->type == BinaryTypeEncodingType::InterfaceDeclarationReference) { - const char* returnClassName = typeEncoding->details.declarationReference.name.valuePtr(); - Class returnClass = objc_getClass(returnClassName); - if (returnClass != nil && returnClass == [NSMutableString class]) { - marshalToPrimitive = false; - } - } + std::ostringstream errorStream; + errorStream << "Unable to convert " << [result description] << " to a Date object"; + std::string errorMessage = errorStream.str(); + Local error = Exception::Error(tns::ToV8String(isolate, errorMessage)); + isolate->ThrowException(error); + return Local(); + } - if (marshalToPrimitive) { - // Convert NSString instances to javascript strings for all instance method calls - return tns::ToV8String(isolate, result); + if (marshalToPrimitive && [result isKindOfClass:[NSString class]]) { + if (typeEncoding != nullptr && + typeEncoding->type == BinaryTypeEncodingType::InterfaceDeclarationReference) { + const char* returnClassName = typeEncoding->details.declarationReference.name.valuePtr(); + Class returnClass = objc_getClass(returnClassName); + if (returnClass != nil && returnClass == [NSMutableString class]) { + marshalToPrimitive = false; } } - if (marshalToPrimitive && [result isKindOfClass:[NSNumber class]] && - ![result isKindOfClass:[NSDecimalNumber class]]) { - // Convert NSNumber instances to javascript numbers for all instance method calls - double value = [result doubleValue]; - return Number::New(isolate, value); + if (marshalToPrimitive) { + // Convert NSString instances to javascript strings for all instance method calls + return tns::ToV8String(isolate, result); } + } - auto cache = Caches::Get(isolate); - auto poInstance = ArgConverter::FindCachedInstance(isolate, cache, result); - if (poInstance != nullptr) { - return poInstance->Get(isolate); - } + if (marshalToPrimitive && [result isKindOfClass:[NSNumber class]] && + ![result isKindOfClass:[NSDecimalNumber class]]) { + // Convert NSNumber instances to javascript numbers for all instance method calls + double value = [result doubleValue]; + return Number::New(isolate, value); + } + + auto cache = Caches::Get(isolate); + auto poInstance = ArgConverter::FindCachedInstance(isolate, cache, result); + if (poInstance != nullptr) { + return poInstance->Get(isolate); + } - // For NSProxy we will try to read the metadata from - // typeEncoding->details.interfaceDeclarationReference.name because class_getSuperclass will - // directly return NSProxy and thus missing to attach all instance members - const TypeEncoding* te = [result isProxy] ? typeEncoding : nullptr; + // For NSProxy we will try to read the metadata from + // typeEncoding->details.interfaceDeclarationReference.name because class_getSuperclass will + // directly return NSProxy and thus missing to attach all instance members + const TypeEncoding* te = [result isProxy] ? typeEncoding : nullptr; - ObjCDataWrapper* wrapper = new ObjCDataWrapper(result, te); - std::vector additionalProtocols = Interop::GetAdditionalProtocols(typeEncoding); - Local jsResult = - ArgConverter::ConvertArgument(context, wrapper, false, additionalProtocols); + ObjCDataWrapper* wrapper = new ObjCDataWrapper(result, te); + std::vector additionalProtocols = typeEncoding != nullptr + ? Interop::GetAdditionalProtocols(typeEncoding) + : std::vector(); + Local jsResult = + ArgConverter::ConvertArgument(context, wrapper, false, additionalProtocols); - if (ownsReturnedObject || isInitializer) { - [result release]; + if (ownsReturnedObject || isInitializer) { + [result release]; + } + + if ([result isKindOfClass:[NSArray class]]) { + // attach Symbol.iterator to the instance + SymbolIterator::Set(context, jsResult); + } + + tns::DeleteWrapperIfUnused(isolate, jsResult, wrapper); + + return jsResult; +} + +Local Interop::ClassToJsValue(Local context, Class result) { + Isolate* isolate = v8::Isolate::GetCurrent(); + if (result == nil) { + return Null(isolate); + } + + std::shared_ptr cache = Caches::Get(isolate); + while (true) { + const char* name = class_getName(result); + + const Meta* meta = ArgConverter::GetMeta(name); + if (meta != nullptr && + (meta->type() == MetaType::Interface || meta->type() == MetaType::ProtocolType)) { + const BaseClassMeta* baseMeta = static_cast(meta); + Class knownClass = meta->type() == MetaType::Interface ? objc_getClass(meta->name()) : nil; + KnownUnknownClassPair pair(knownClass); + std::vector emptyProtocols; + cache->ObjectCtorInitializer(context, baseMeta, pair, emptyProtocols); + } + + auto it = cache->CtorFuncs.find(name); + if (it != cache->CtorFuncs.end()) { + return it->second->Get(isolate); } - if ([result isKindOfClass:[NSArray class]]) { - // attach Symbol.iterator to the instance - SymbolIterator::Set(context, jsResult); + result = class_getSuperclass(result); + if (!result) { + break; } + } - tns::DeleteWrapperIfUnused(isolate, jsResult, wrapper); + tns::Assert(false, isolate); + return Local(); +} - return jsResult; +Local Interop::NSExceptionToJsError(Local context, id exception, + std::string& message) { + Isolate* isolate = v8::Isolate::GetCurrent(); + NSException* e = (NSException*)exception; + + NSString* nsName = [e name]; + NSString* nsReason = [e reason]; + Local messageV8 = tns::ToV8String(isolate, nsReason ?: (nsName ?: @"NSException")); + message = tns::ToString(isolate, messageV8); + + Local jsErrVal = Exception::Error(messageV8); + if (jsErrVal.IsEmpty() || !jsErrVal->IsObject()) { + return Local(); } - return Interop::GetPrimitiveReturnType(context, typeEncoding->type, call); + Local jsErrObj = jsErrVal.As(); + if (nsName != nil) { + jsErrObj->Set(context, tns::ToV8String(isolate, "name"), tns::ToV8String(isolate, nsName)) + .FromMaybe(false); + } + if (nsReason != nil) { + jsErrObj->Set(context, tns::ToV8String(isolate, "message"), tns::ToV8String(isolate, nsReason)) + .FromMaybe(false); + } + + ObjCDataWrapper* wrapper = new ObjCDataWrapper((id)e); + Local nativeWrapper = + ArgConverter::CreateJsWrapper(context, wrapper, Local(), true); + jsErrObj->Set(context, tns::ToV8String(isolate, "nativeException"), nativeWrapper) + .FromMaybe(false); + + return jsErrObj; } Local Interop::GetPrimitiveReturnType(Local context, BinaryTypeEncodingType type, @@ -1714,35 +1766,14 @@ inline bool isBool() { Isolate* isolate = v8::Isolate::GetCurrent(); Local context = isolate->GetCurrentContext(); - NSString* nsName = [e name]; - NSString* nsReason = [e reason]; - Local messageV8 = tns::ToV8String(isolate, nsReason ?: (nsName ?: @"NSException")); - std::string message = tns::ToString(isolate, messageV8); - - Local jsErrVal = Exception::Error(messageV8); - if (jsErrVal.IsEmpty() || !jsErrVal->IsObject()) { + std::string message; + Local jsError = Interop::NSExceptionToJsError(context, e, message); + if (jsError.IsEmpty()) { // Fallback: keep the description-only behavior if Error creation fails. throw NativeScriptException(tns::ToString(isolate, [e description])); } - Local jsErrObj = jsErrVal.As(); - if (nsName != nil) { - jsErrObj->Set(context, tns::ToV8String(isolate, "name"), tns::ToV8String(isolate, nsName)) - .FromMaybe(false); - } - if (nsReason != nil) { - jsErrObj - ->Set(context, tns::ToV8String(isolate, "message"), tns::ToV8String(isolate, nsReason)) - .FromMaybe(false); - } - - ObjCDataWrapper* wrapper = new ObjCDataWrapper((id)e); - Local nativeWrapper = - ArgConverter::CreateJsWrapper(context, wrapper, Local(), true); - jsErrObj->Set(context, tns::ToV8String(isolate, "nativeException"), nativeWrapper) - .FromMaybe(false); - - throw NativeScriptException(isolate, jsErrObj.As(), message); + throw NativeScriptException(isolate, jsError, message); } if (errorRef != nullptr) { diff --git a/NativeScript/runtime/LazyGlobals.cpp b/NativeScript/runtime/LazyGlobals.cpp index 81ac7dd94..4ed315fe1 100644 --- a/NativeScript/runtime/LazyGlobals.cpp +++ b/NativeScript/runtime/LazyGlobals.cpp @@ -4,6 +4,7 @@ #include "BuiltinLoader.h" #include "Helpers.h" #include "Messaging.h" +#include "MethodCallProfiler.h" #include "StructuredSerialization.h" #include "TextEncoding.h" @@ -45,6 +46,7 @@ constexpr LazyGlobalEntry kLazyGlobals[] = { {"MessageChannel", "MessageChannel", messaging::GetMessageChannelExports}, {"BroadcastChannel", "BroadcastChannel", messaging::GetBroadcastChannelExports}, + {"__native_call_profiler", "profiler", MethodCallProfiler::GetExports}, }; void LazyGlobalGetter(Local property, diff --git a/NativeScript/runtime/MetadataBuilder.h b/NativeScript/runtime/MetadataBuilder.h index e8b3ae82c..7b13817f5 100644 --- a/NativeScript/runtime/MetadataBuilder.h +++ b/NativeScript/runtime/MetadataBuilder.h @@ -12,7 +12,13 @@ namespace tns { +namespace aot { +struct Trampolines; +} + class MetadataBuilder { + friend struct aot::Trampolines; + public: static void RegisterConstantsOnGlobalObject( v8::Isolate* isolate, v8::Local globalTemplate, @@ -80,7 +86,8 @@ class MetadataBuilder { static void RegisterInstanceMethods( v8::Local context, v8::Local ctorFuncTemplate, - const BaseClassMeta* meta, KnownUnknownClassPair pair, + const BaseClassMeta* meta, const char* className, + KnownUnknownClassPair pair, robin_hood::unordered_map& names); static void RegisterInstanceProperties( v8::Local context, @@ -102,7 +109,8 @@ class MetadataBuilder { robin_hood::unordered_map& names); static void RegisterStaticMethods( v8::Local context, v8::Local ctorFunc, - const BaseClassMeta* meta, KnownUnknownClassPair pair, + const BaseClassMeta* meta, const char* className, + KnownUnknownClassPair pair, robin_hood::unordered_map& names); static void RegisterStaticProperties( v8::Local context, v8::Local ctorFunc, diff --git a/NativeScript/runtime/MetadataBuilder.mm b/NativeScript/runtime/MetadataBuilder.mm index fd4728560..950816b41 100644 --- a/NativeScript/runtime/MetadataBuilder.mm +++ b/NativeScript/runtime/MetadataBuilder.mm @@ -1,5 +1,6 @@ #include "MetadataBuilder.h" #include +#include "AOTCalls.h" #include "ArgConverter.h" #include "Caches.h" #include "Constants.h" @@ -7,6 +8,7 @@ #include "InlineFunctions.h" #include "Interop.h" #include "LazyGlobals.h" +#include "MethodCallProfiler.h" #include "NativeScriptException.h" #include "ObjectManager.h" #include "Runtime.h" @@ -456,7 +458,8 @@ throw NativeScriptException( MetadataBuilder::RegisterInstanceProperties(context, ctorFuncTemplate, meta, meta->name(), pair, instanceMembers); - MetadataBuilder::RegisterInstanceMethods(context, ctorFuncTemplate, meta, pair, instanceMembers); + MetadataBuilder::RegisterInstanceMethods(context, ctorFuncTemplate, meta, meta->name(), pair, + instanceMembers); MetadataBuilder::RegisterInstanceProtocols(context, ctorFuncTemplate, meta, meta->name(), pair, instanceMembers); MetadataBuilder::RegisterAdditionalProtocols(context, ctorFuncTemplate, pair, additionalProtocols, @@ -515,7 +518,8 @@ NamedPropertyHandlerConfiguration config(nullptr, MetadataBuilder::SwizzledInsta } } - MetadataBuilder::RegisterStaticMethods(context, ctorFunc, meta, pair, staticMembers); + MetadataBuilder::RegisterStaticMethods(context, ctorFunc, meta, meta->name(), pair, + staticMembers); MetadataBuilder::RegisterStaticProperties(context, ctorFunc, meta, meta->name(), pair, staticMembers); MetadataBuilder::RegisterStaticProtocols(context, ctorFunc, meta, meta->name(), pair, @@ -566,9 +570,11 @@ NamedPropertyHandlerConfiguration config(nullptr, MetadataBuilder::SwizzledInsta void MetadataBuilder::RegisterInstanceMethods( Local context, Local ctorFuncTemplate, const BaseClassMeta* meta, - KnownUnknownClassPair pair, robin_hood::unordered_map& names) { + const char* className, KnownUnknownClassPair pair, + robin_hood::unordered_map& names) { Isolate* isolate = v8::Isolate::GetCurrent(); Local proto = ctorFuncTemplate->PrototypeTemplate(); + const aot::StubScope stubs = aot::FindStubScope(className); for (auto it = meta->instanceMethods->begin(); it != meta->instanceMethods->end(); it++) { const MethodMeta* methodMeta = (*it).valuePtr(); @@ -582,8 +588,14 @@ NamedPropertyHandlerConfiguration config(nullptr, MetadataBuilder::SwizzledInsta CacheItem* item = new CacheItem(methodMeta, meta->name()); Caches::Get(isolate)->registerCacheBoundObject(item); Local ext = External::New(isolate, item, v8::kExternalPointerTypeTagDefault); + FunctionCallback callback = MethodCallback; + aot::Binding binding; + if (!stubs.empty() && aot::FindInstanceMethod(stubs, methodMeta->selector(), binding)) { + callback = binding.callback; + item->userData_ = binding.handler; + } Local instanceMethodTemplate = - FunctionTemplate::New(isolate, MethodCallback, ext); + FunctionTemplate::New(isolate, callback, ext); proto->Set(tns::ToV8String(isolate, methodMeta->jsName()), instanceMethodTemplate); names.emplace(methodName, 0); } @@ -596,6 +608,7 @@ NamedPropertyHandlerConfiguration config(nullptr, MetadataBuilder::SwizzledInsta robin_hood::unordered_map& names) { Isolate* isolate = v8::Isolate::GetCurrent(); Local proto = ctorFuncTemplate->PrototypeTemplate(); + const aot::StubScope stubs = aot::FindStubScope(className.c_str()); for (auto it = meta->instanceProps->begin(); it != meta->instanceProps->end(); it++) { const PropertyMeta* propMeta = (*it).valuePtr(); @@ -617,7 +630,14 @@ NamedPropertyHandlerConfiguration config(nullptr, MetadataBuilder::SwizzledInsta CacheItem* item = new CacheItem(propMeta, className); Caches::Get(isolate)->registerCacheBoundObject(item); Local ext = External::New(isolate, item, v8::kExternalPointerTypeTagDefault); - Local getter = FunctionTemplate::New(isolate, PropertyGetterCallback, ext); + FunctionCallback getterCallback = PropertyGetterCallback; + aot::Binding binding; + if (!stubs.empty() && propMeta->hasGetter() && + aot::FindInstanceGetter(stubs, propMeta->getter()->selector(), binding)) { + getterCallback = binding.callback; + item->userData_ = binding.handler; + } + Local getter = FunctionTemplate::New(isolate, getterCallback, ext); Local setter = FunctionTemplate::New(isolate, PropertySetterCallback, ext); proto->SetAccessorProperty(tns::ToV8String(isolate, propMeta->jsName()), getter, setter, PropertyAttribute::None); @@ -631,7 +651,8 @@ NamedPropertyHandlerConfiguration config(nullptr, MetadataBuilder::SwizzledInsta const std::string className, KnownUnknownClassPair pair, robin_hood::unordered_map& names) { if (meta->type() == MetaType::ProtocolType) { - MetadataBuilder::RegisterInstanceMethods(context, ctorFuncTemplate, meta, pair, names); + MetadataBuilder::RegisterInstanceMethods(context, ctorFuncTemplate, meta, className.c_str(), + pair, names); MetadataBuilder::RegisterInstanceProperties(context, ctorFuncTemplate, meta, className, pair, names); } @@ -655,7 +676,8 @@ NamedPropertyHandlerConfiguration config(nullptr, MetadataBuilder::SwizzledInsta const Meta* meta = ArgConverter::GetMeta(protocolName.c_str()); if (meta != nullptr) { const BaseClassMeta* baseMeta = static_cast(meta); - MetadataBuilder::RegisterInstanceMethods(context, ctorFuncTemplate, baseMeta, pair, names); + MetadataBuilder::RegisterInstanceMethods(context, ctorFuncTemplate, baseMeta, + baseMeta->name(), pair, names); MetadataBuilder::RegisterInstanceProperties(context, ctorFuncTemplate, baseMeta, baseMeta->name(), pair, names); @@ -673,8 +695,10 @@ NamedPropertyHandlerConfiguration config(nullptr, MetadataBuilder::SwizzledInsta void MetadataBuilder::RegisterStaticMethods( Local context, Local ctorFunc, const BaseClassMeta* meta, - KnownUnknownClassPair pair, robin_hood::unordered_map& names) { + const char* className, KnownUnknownClassPair pair, + robin_hood::unordered_map& names) { Isolate* isolate = v8::Isolate::GetCurrent(); + const aot::StubScope stubs = aot::FindStubScope(className); for (auto it = meta->staticMethods->begin(); it != meta->staticMethods->end(); it++) { const MethodMeta* methodMeta = (*it).valuePtr(); if (!methodMeta->isAvailableInClasses(pair, true)) { @@ -687,8 +711,13 @@ NamedPropertyHandlerConfiguration config(nullptr, MetadataBuilder::SwizzledInsta CacheItem* item = new CacheItem(methodMeta, meta->name()); Caches::Get(isolate)->registerCacheBoundObject(item); Local ext = External::New(isolate, item, v8::kExternalPointerTypeTagDefault); - Local staticMethodTemplate = - FunctionTemplate::New(isolate, MethodCallback, ext); + FunctionCallback callback = MethodCallback; + aot::Binding binding; + if (!stubs.empty() && aot::FindStaticMethod(stubs, methodMeta->selector(), binding)) { + callback = binding.callback; + item->userData_ = binding.handler; + } + Local staticMethodTemplate = FunctionTemplate::New(isolate, callback, ext); Local staticMethod; if (!staticMethodTemplate->GetFunction(context).ToLocal(&staticMethod)) { tns::Assert(false, isolate); @@ -713,6 +742,7 @@ NamedPropertyHandlerConfiguration config(nullptr, MetadataBuilder::SwizzledInsta const std::string className, KnownUnknownClassPair pair, robin_hood::unordered_map& names) { Isolate* isolate = v8::Isolate::GetCurrent(); + const aot::StubScope stubs = aot::FindStubScope(className.c_str()); for (auto it = meta->staticProps->begin(); it != meta->staticProps->end(); it++) { const PropertyMeta* propMeta = (*it).valuePtr(); @@ -736,6 +766,14 @@ NamedPropertyHandlerConfiguration config(nullptr, MetadataBuilder::SwizzledInsta Caches::Get(isolate)->registerCacheBoundObject(item); Local ext = External::New(isolate, item, v8::kExternalPointerTypeTagDefault); + FunctionCallback getterCallback = PropertyNameGetterCallback; + aot::Binding binding; + if (!stubs.empty() && propMeta->hasGetter() && + aot::FindStaticGetter(stubs, propMeta->getter()->selector(), binding)) { + getterCallback = binding.callback; + item->userData_ = binding.handler; + } + Local propName = tns::ToV8String(isolate, propMeta->jsName()); // A real accessor pair, not SetNativeDataProperty: a derived class // inherits this from the base constructor, and assigning through the @@ -743,7 +781,7 @@ NamedPropertyHandlerConfiguration config(nullptr, MetadataBuilder::SwizzledInsta // data property. Local propGetter; Local propSetter; - if (!FunctionTemplate::New(isolate, PropertyNameGetterCallback, ext) + if (!FunctionTemplate::New(isolate, getterCallback, ext) ->GetFunction(context) .ToLocal(&propGetter) || !FunctionTemplate::New(isolate, PropertyNameSetterCallback, ext) @@ -763,7 +801,7 @@ NamedPropertyHandlerConfiguration config(nullptr, MetadataBuilder::SwizzledInsta const std::string className, KnownUnknownClassPair pair, robin_hood::unordered_map& names) { if (meta->type() == MetaType::ProtocolType) { - MetadataBuilder::RegisterStaticMethods(context, ctorFunc, meta, pair, names); + MetadataBuilder::RegisterStaticMethods(context, ctorFunc, meta, className.c_str(), pair, names); MetadataBuilder::RegisterStaticProperties(context, ctorFunc, meta, className, pair, names); } @@ -843,6 +881,11 @@ NamedPropertyHandlerConfiguration config(nullptr, MetadataBuilder::SwizzledInsta } } + if (MethodCallProfiler::IsEnabled()) { + MethodCallProfiler::RecordCall(klass ? std::string(class_getName(klass)) : item->className_, + item->meta_, !instanceMethod); + } + Local context = isolate->GetCurrentContext(); Local result = instanceMethod @@ -870,6 +913,10 @@ NamedPropertyHandlerConfiguration config(nullptr, MetadataBuilder::SwizzledInsta return; } + if (MethodCallProfiler::IsEnabled()) { + MethodCallProfiler::RecordCall(item->className_, item->meta_->getter()); + } + V8EmptyValueArgs args; Local context = isolate->GetCurrentContext(); Local result = MetadataBuilder::InvokeMethod(context, item->meta_->getter(), receiver, @@ -908,6 +955,10 @@ NamedPropertyHandlerConfiguration config(nullptr, MetadataBuilder::SwizzledInsta return; } + if (MethodCallProfiler::IsEnabled()) { + MethodCallProfiler::RecordCall(item->className_, item->meta_->getter(), true); + } + V8EmptyValueArgs args; Local context = isolate->GetCurrentContext(); Local result = MetadataBuilder::InvokeMethod( diff --git a/NativeScript/runtime/MethodCallProfiler.h b/NativeScript/runtime/MethodCallProfiler.h new file mode 100644 index 000000000..bc7abd368 --- /dev/null +++ b/NativeScript/runtime/MethodCallProfiler.h @@ -0,0 +1,45 @@ +#ifndef MethodCallProfiler_h +#define MethodCallProfiler_h + +#include +#include + +#include "Common.h" +#include "Metadata.h" + +namespace tns { + +// Counts native method and property-getter calls that take the generic +// (libffi) path. Exposed to JS as the lazy global `__native_call_profiler`: +// start() / stop() / reset() +// report(topN = 50) human-readable top calls +// aotConfig(topN = 50) JSON candidates for AOT stubs +// aotStats() {served, declined} AOT stub calls, counted only +// between start() and stop() +class MethodCallProfiler { + public: + static inline bool IsEnabled() { + return enabled_.load(std::memory_order_relaxed); + } + static void Enable(); + static void Disable(); + static void Reset(); + static void RecordCall(const std::string& className, const MethodMeta* meta, + bool isStatic = false); + // LazyGlobals exports accessor: `{ profiler: }`. + static v8::MaybeLocal GetExports(v8::Local context); + + private: + static std::atomic enabled_; + + static void JSStart(const v8::FunctionCallbackInfo& info); + static void JSStop(const v8::FunctionCallbackInfo& info); + static void JSReset(const v8::FunctionCallbackInfo& info); + static void JSReport(const v8::FunctionCallbackInfo& info); + static void JSAOTConfig(const v8::FunctionCallbackInfo& info); + static void JSAOTStats(const v8::FunctionCallbackInfo& info); +}; + +} // namespace tns + +#endif /* MethodCallProfiler_h */ diff --git a/NativeScript/runtime/MethodCallProfiler.mm b/NativeScript/runtime/MethodCallProfiler.mm new file mode 100644 index 000000000..a42534ca2 --- /dev/null +++ b/NativeScript/runtime/MethodCallProfiler.mm @@ -0,0 +1,270 @@ +#include "MethodCallProfiler.h" + +#include +#include +#include +#include +#include + +#include "AOTCalls.h" +#include "Helpers.h" + +using namespace v8; + +namespace tns { + +std::atomic MethodCallProfiler::enabled_{false}; + +namespace { + +struct MethodProfile { + std::string className; + std::string selectorName; + std::string returnType; + std::vector argTypes; + uint64_t count = 0; + bool isStatic = false; +}; + +// Leaked so exit()'s static destructors cannot run under a worker thread that +// is still recording. +std::mutex& profileMutex = *new std::mutex(); +std::unordered_map& profiles = + *new std::unordered_map(); + +std::string EncodingToTypeName(const TypeEncoding* enc) { + switch (enc->type) { + case BinaryTypeEncodingType::VoidEncoding: + return "void"; + case BinaryTypeEncodingType::BoolEncoding: + return "BOOL"; + case BinaryTypeEncodingType::IdEncoding: + return "id"; + case BinaryTypeEncodingType::InterfaceDeclarationReference: + return enc->details.interfaceDeclarationReference.name.valuePtr(); + case BinaryTypeEncodingType::InstanceTypeEncoding: + return "instancetype"; + case BinaryTypeEncodingType::SelectorEncoding: + return "SEL"; + case BinaryTypeEncodingType::ClassEncoding: + return "Class"; + case BinaryTypeEncodingType::IntEncoding: + return "int"; + case BinaryTypeEncodingType::UIntEncoding: + return "uint"; + case BinaryTypeEncodingType::LongEncoding: + return "long"; + case BinaryTypeEncodingType::ULongEncoding: + return "ulong"; + case BinaryTypeEncodingType::LongLongEncoding: + return "longlong"; + case BinaryTypeEncodingType::ULongLongEncoding: + return "ulonglong"; + case BinaryTypeEncodingType::FloatEncoding: + return "float"; + case BinaryTypeEncodingType::DoubleEncoding: + return "double"; + case BinaryTypeEncodingType::CharEncoding: + return "char"; + case BinaryTypeEncodingType::UCharEncoding: + return "uchar"; + case BinaryTypeEncodingType::ShortEncoding: + return "short"; + case BinaryTypeEncodingType::UShortEncoding: + return "ushort"; + case BinaryTypeEncodingType::UnicharEncoding: + return "unichar"; + case BinaryTypeEncodingType::StructDeclarationReference: + return enc->details.declarationReference.name.valuePtr(); + case BinaryTypeEncodingType::PointerEncoding: + case BinaryTypeEncodingType::CStringEncoding: + case BinaryTypeEncodingType::IncompleteArrayEncoding: + return "pointer"; + case BinaryTypeEncodingType::BlockEncoding: + case BinaryTypeEncodingType::FunctionPointerEncoding: + return "block"; + case BinaryTypeEncodingType::ProtocolEncoding: + return "id"; + default: + return ""; + } +} + +// Caller holds profileMutex. +std::vector SortedProfiles(int topN) { + std::vector sorted; + sorted.reserve(profiles.size()); + for (const auto& pair : profiles) { + sorted.push_back(&pair.second); + } + std::sort(sorted.begin(), sorted.end(), + [](const MethodProfile* a, const MethodProfile* b) { return a->count > b->count; }); + if (topN > 0 && (int)sorted.size() > topN) { + sorted.resize(topN); + } + return sorted; +} + +int TopNArgument(const FunctionCallbackInfo& info) { + if (info.Length() > 0 && info[0]->IsNumber()) { + return (int)tns::ToNumber(info.GetIsolate(), info[0]); + } + return 50; +} + +} // namespace + +void MethodCallProfiler::Enable() { enabled_.store(true, std::memory_order_relaxed); } + +void MethodCallProfiler::Disable() { enabled_.store(false, std::memory_order_relaxed); } + +void MethodCallProfiler::Reset() { + std::lock_guard lock(profileMutex); + profiles.clear(); +} + +void MethodCallProfiler::RecordCall(const std::string& className, const MethodMeta* meta, + bool isStatic) { + const char* selector = meta->selectorAsString(); + std::string key = className; + key += isStatic ? "\t+\t" : "\t-\t"; + key += selector; + + std::lock_guard lock(profileMutex); + auto it = profiles.find(key); + if (it != profiles.end()) { + it->second.count++; + return; + } + + MethodProfile profile; + profile.className = className; + profile.selectorName = selector; + profile.count = 1; + profile.isStatic = isStatic; + + const auto* encodings = meta->encodings(); + const TypeEncoding* enc = encodings->first(); + std::string returnName = EncodingToTypeName(enc); + profile.returnType = returnName.empty() ? "?" : returnName; + + int paramCount = encodings->count - 1; + for (int i = 0; i < paramCount; i++) { + enc = enc->next(); + std::string argName = EncodingToTypeName(enc); + profile.argTypes.push_back(argName.empty() ? "?" : argName); + } + + profiles.emplace(std::move(key), std::move(profile)); +} + +void MethodCallProfiler::JSStart(const FunctionCallbackInfo& info) { Enable(); } + +void MethodCallProfiler::JSStop(const FunctionCallbackInfo& info) { Disable(); } + +void MethodCallProfiler::JSReset(const FunctionCallbackInfo& info) { Reset(); } + +void MethodCallProfiler::JSReport(const FunctionCallbackInfo& info) { + int topN = TopNArgument(info); + + std::ostringstream out; + { + std::lock_guard lock(profileMutex); + auto sorted = SortedProfiles(topN); + out << "Top " << sorted.size() << " method calls:\n"; + for (size_t i = 0; i < sorted.size(); i++) { + const auto& p = *sorted[i]; + out << " " << (i + 1) << ". " << p.className << " " << (p.isStatic ? "+" : "-") << "[" + << p.selectorName << "] " << p.returnType << "("; + for (size_t j = 0; j < p.argTypes.size(); j++) { + if (j > 0) out << ", "; + out << p.argTypes[j]; + } + out << ") - " << p.count << " calls\n"; + } + } + + info.GetReturnValue().Set(tns::ToV8String(info.GetIsolate(), out.str())); +} + +void MethodCallProfiler::JSAOTConfig(const FunctionCallbackInfo& info) { + int topN = TopNArgument(info); + auto isUnsupported = [](const std::string& t) { + return t == "?" || t == "pointer" || t == "block"; + }; + + std::ostringstream out; + { + std::lock_guard lock(profileMutex); + auto sorted = SortedProfiles(topN); + out << "[\n"; + bool first = true; + for (const auto* p : sorted) { + bool hasUnsupportedType = isUnsupported(p->returnType); + for (const auto& a : p->argTypes) { + hasUnsupportedType = hasUnsupportedType || isUnsupported(a); + } + if (hasUnsupportedType) continue; + + if (!first) out << ",\n"; + first = false; + + out << " { \"class\": \"" << p->className << "\", \"selector\": \"" << p->selectorName + << "\", \"ret\": \"" << p->returnType << "\", \"args\": ["; + for (size_t j = 0; j < p->argTypes.size(); j++) { + if (j > 0) out << ", "; + out << "\"" << p->argTypes[j] << "\""; + } + out << "]"; + if (p->isStatic) { + out << ", \"static\": true"; + } + out << " }"; + } + out << "\n]"; + } + + info.GetReturnValue().Set(tns::ToV8String(info.GetIsolate(), out.str())); +} + +void MethodCallProfiler::JSAOTStats(const FunctionCallbackInfo& info) { + Isolate* isolate = info.GetIsolate(); + Local context = isolate->GetCurrentContext(); + Local stats = Object::New(isolate); + bool success = stats + ->Set(context, tns::ToV8String(isolate, "served"), + Number::New(isolate, (double)aot::ServedCallCount())) + .FromMaybe(false) && + stats + ->Set(context, tns::ToV8String(isolate, "declined"), + Number::New(isolate, (double)aot::DeclinedCallCount())) + .FromMaybe(false); + if (success) { + info.GetReturnValue().Set(stats); + } +} + +MaybeLocal MethodCallProfiler::GetExports(Local context) { + Isolate* isolate = v8::Isolate::GetCurrent(); + EscapableHandleScope scope(isolate); + Local profiler = Object::New(isolate); + const std::pair methods[] = { + {"start", JSStart}, {"stop", JSStop}, {"reset", JSReset}, + {"report", JSReport}, {"aotConfig", JSAOTConfig}, {"aotStats", JSAOTStats}, + }; + for (const auto& [name, callback] : methods) { + Local function; + if (!v8::Function::New(context, callback).ToLocal(&function) || + !profiler->Set(context, tns::ToV8String(isolate, name), function).FromMaybe(false)) { + return MaybeLocal(); + } + } + + Local exports = Object::New(isolate); + if (!exports->Set(context, tns::ToV8String(isolate, "profiler"), profiler).FromMaybe(false)) { + return MaybeLocal(); + } + return scope.Escape(exports); +} + +} // namespace tns diff --git a/NativeScript/runtime/NativeScriptAOT.h b/NativeScript/runtime/NativeScriptAOT.h new file mode 100644 index 000000000..c5c0a4e0c --- /dev/null +++ b/NativeScript/runtime/NativeScriptAOT.h @@ -0,0 +1,120 @@ +#ifndef NativeScriptAOT_h +#define NativeScriptAOT_h + +// C bridge between app-compiled AOT call stubs and the runtime. +// +// A stub is a plain C function generated per (class, selector) by +// scripts/generate-aot.py from the app's aot-config.json and the metadata +// generator's JSON output. The stub is bound to the method or property getter +// when the class's prototype template is built, so V8 calls it directly in +// place of the generic libffi path. Everything the stub needs from the +// runtime goes through the functions below; it never includes runtime +// headers. +// +// Contract: +// - A handler returns true when it handled the call (set a return value or +// threw into V8) and false to decline, in which case the runtime runs the +// generic path for the same call. A declining handler must not have set a +// return value, thrown, or had side effects. Declining is how stubs stay +// correct for everything they do not model: alloc receivers, argument-count +// mismatches (overloads, optional NSError** out parameters), non-object +// receivers. +// - Argument getters return false when conversion threw into V8; the handler +// must then return true without sending the message. +// - Exceptions thrown by the callee (NSException) must be caught by the stub +// and passed to __ns_aot_throw_exception, which converts them the way the +// generic path does. +// - Values returned by the callee with +1 ownership (init, copy, new, or +// methods flagged as owning their return) are passed with owned = true so +// the runtime balances the retain. The stub file may be compiled with or +// without ARC; the cast objc_msgSend calls are +0 to ARC either way. +// - Struct types are resolved once per stub through __ns_aot_struct_type and +// the handle passed back; the handle is stable for the process lifetime. +// +// This header is public API of the framework and is kept C-compatible. + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef const void* NSAOTCallInfo; +typedef const void* NSAOTStructType; +typedef bool (*NSAOTCallHandler)(NSAOTCallInfo info); + +// --- Receiver --- + +int __ns_aot_arg_count(NSAOTCallInfo info); + +// Returns the receiver for an instance call, or nil when the stub must decline +// (no wrapper, alloc wrapper, non-object wrapper). On success *outSelector is +// the selector to send (the runtime may redirect to a swizzled selector) and +// *outCallSuper tells the stub to use objc_msgSendSuper against the +// receiver's superclass, which is how instances of JS-extended classes reach +// the native implementation. +id __ns_aot_get_target(NSAOTCallInfo info, SEL selector, SEL* outSelector, + bool* outCallSuper); + +// Returns the class for a static call, or nil when the stub must decline. +Class __ns_aot_get_static_class(NSAOTCallInfo info); + +// --- Arguments --- +// Each returns false if the conversion threw into V8. + +bool __ns_aot_arg_object(NSAOTCallInfo info, int index, id* out); +bool __ns_aot_arg_bool(NSAOTCallInfo info, int index, BOOL* out); +bool __ns_aot_arg_int64(NSAOTCallInfo info, int index, int64_t* out); +bool __ns_aot_arg_uint64(NSAOTCallInfo info, int index, uint64_t* out); +bool __ns_aot_arg_double(NSAOTCallInfo info, int index, double* out); +bool __ns_aot_arg_selector(NSAOTCallInfo info, int index, SEL* out); +bool __ns_aot_arg_class(NSAOTCallInfo info, int index, Class* out); +bool __ns_aot_arg_pointer(NSAOTCallInfo info, int index, void** out); +bool __ns_aot_arg_struct(NSAOTCallInfo info, int index, NSAOTStructType type, + void* dest); + +// --- Return values --- + +// marshalToPrimitive mirrors the generic path: NSString, NSNumber, NSDate and +// NSNull become JS primitives unless the declared return type is instancetype +// (and an NSString stays an object when the declared type is NSMutableString). +void __ns_aot_return_object(NSAOTCallInfo info, id value, bool owned, + bool marshalToPrimitive); +void __ns_aot_return_bool(NSAOTCallInfo info, BOOL value); +void __ns_aot_return_int64(NSAOTCallInfo info, int64_t value); +void __ns_aot_return_uint64(NSAOTCallInfo info, uint64_t value); +void __ns_aot_return_double(NSAOTCallInfo info, double value); +void __ns_aot_return_selector(NSAOTCallInfo info, SEL value); +void __ns_aot_return_class(NSAOTCallInfo info, Class value); +void __ns_aot_return_pointer(NSAOTCallInfo info, void* value); +void __ns_aot_return_struct(NSAOTCallInfo info, NSAOTStructType type, + const void* data); + +// --- Structs --- + +// Resolves a struct type by its metadata name (e.g. "CGRect"). Returns NULL +// if the metadata has no such struct; the stub must then decline. +NSAOTStructType __ns_aot_struct_type(const char* name); + +// --- Exceptions --- + +void __ns_aot_throw_exception(NSAOTCallInfo info, id exception); + +// --- Registration --- + +// The generated stubs file exports +// void __ns_register_aot_calls(void (*reg)(const char*, const char*, bool, +// NSAOTCallHandler)); +// which the runtime locates with dlsym at startup and calls once; it must call +// reg(className, selector, isStatic, handler) for every stub. className is +// the class whose metadata declares the member (or adopts the protocol that +// does). + +#ifdef __cplusplus +} +#endif + +#endif /* NativeScriptAOT_h */ diff --git a/NativeScript/runtime/NativeScriptAOTBridge.mm b/NativeScript/runtime/NativeScriptAOTBridge.mm new file mode 100644 index 000000000..99108d598 --- /dev/null +++ b/NativeScript/runtime/NativeScriptAOTBridge.mm @@ -0,0 +1,664 @@ +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "AOTCalls.h" +#include "ArgConverter.h" +#include "Caches.h" +#include "DataWrapper.h" +#include "FFICall.h" +#include "Helpers.h" +#include "Interop.h" +#include "MetadataBuilder.h" +#include "MethodCallProfiler.h" +#include "NativeScriptAOT.h" +#include "NativeScriptException.h" +#include "Pointer.h" +#include "robin_hood.h" + +using namespace v8; + +namespace tns::aot { + +struct ClassStubs { + robin_hood::unordered_map instanceMembers; + robin_hood::unordered_map staticMembers; +}; + +namespace { + +using ClassStubsMap = + std::unordered_map; + +// Written only while DiscoverExternalStubs' call_once runs, read-only after; +// call_once orders those writes before every runtime's template building. +ClassStubsMap& Registry() { + static auto* registry = new ClassStubsMap(); + return *registry; +} +bool hasStubs = false; + +std::mutex registrationMutex; +bool registrationOpen = false; +size_t duplicateRegistrations = 0; +std::string firstDuplicate; + +std::atomic servedCalls{0}; +std::atomic declinedCalls{0}; + +// What NSAOTCallInfo points to. +struct CallFrame { + const FunctionCallbackInfo& info; + // The method (or property getter) the stub is bound to. + const MethodMeta* meta; + // Set at static property getter sites, which always message the declaring + // class rather than the receiver. + const std::string* staticGetterClassName; + // Cursor over meta's parameter encodings, so in-order argument reads walk + // the encoding list once. + mutable const TypeEncoding* paramEncoding = nullptr; + mutable int paramIndex = -1; +}; + +inline const CallFrame& FrameOf(NSAOTCallInfo callInfo) { + return *static_cast(callInfo); +} + +const TypeEncoding* ParameterEncoding(const CallFrame& frame, int index) { + const auto* encodings = frame.meta->encodings(); + if (index < 0 || index >= encodings->count - 1) { + return nullptr; + } + if (frame.paramIndex > index || frame.paramEncoding == nullptr) { + frame.paramEncoding = encodings->first()->next(); + frame.paramIndex = 0; + } + while (frame.paramIndex < index) { + frame.paramEncoding = frame.paramEncoding->next(); + frame.paramIndex++; + } + return frame.paramEncoding; +} + +// Whether a class's instances must be messaged through objc_msgSendSuper, +// i.e. whether the class is in Caches::ClassPrototypes. ClassBuilder can run +// JS between allocating an extended class and entering it there, so the +// answers are dropped whenever ClassPrototypes grows. +struct AOTState { + Caches* caches = nullptr; + size_t classPrototypesSize = 0; + robin_hood::unordered_map callSuperByClass; +}; + +inline bool Dispatch(NSAOTCallHandler handler, const CallFrame& frame) { + bool handled = handler(&frame); + if (MethodCallProfiler::IsEnabled()) { + (handled ? servedCalls : declinedCalls).fetch_add(1, std::memory_order_relaxed); + } + return handled; +} + +bool Find(const StubScope& scope, bool isStatic, SEL selector, v8::FunctionCallback callback, + Binding& out) { + for (const ClassStubs* stubs : scope.classes) { + const auto& members = isStatic ? stubs->staticMembers : stubs->instanceMembers; + auto it = members.find(selector); + if (it != members.end()) { + out.callback = callback; + out.handler = reinterpret_cast(it->second); + return true; + } + } + return false; +} + +} // namespace + +struct Trampolines { + template + static NSAOTCallHandler HandlerOf(const FunctionCallbackInfo& info, + MetadataBuilder::CacheItem*& item) { + item = static_cast*>( + info.Data().As()->Value(v8::kExternalPointerTypeTagDefault)); + return reinterpret_cast(item->userData_); + } + + static void Method(const FunctionCallbackInfo& info) { + MetadataBuilder::CacheItem* item; + NSAOTCallHandler handler = HandlerOf(info, item); + if (!Dispatch(handler, CallFrame{info, item->meta_, nullptr}) && + !info.GetIsolate()->HasPendingException()) { + MetadataBuilder::MethodCallback(info); + } + } + + static void InstanceGetter(const FunctionCallbackInfo& info) { + MetadataBuilder::CacheItem* item; + NSAOTCallHandler handler = HandlerOf(info, item); + if (!Dispatch(handler, CallFrame{info, item->meta_->getter(), nullptr}) && + !info.GetIsolate()->HasPendingException()) { + MetadataBuilder::PropertyGetterCallback(info); + } + } + + static void StaticGetter(const FunctionCallbackInfo& info) { + MetadataBuilder::CacheItem* item; + NSAOTCallHandler handler = HandlerOf(info, item); + if (!Dispatch(handler, CallFrame{info, item->meta_->getter(), &item->className_}) && + !info.GetIsolate()->HasPendingException()) { + MetadataBuilder::PropertyNameGetterCallback(info); + } + } +}; + +void DiscoverExternalStubs() { + static std::once_flag once; + std::call_once(once, [] { + using Registrar = void (*)(void (*)(const char*, const char*, bool, NSAOTCallHandler)); + auto registrar = reinterpret_cast(dlsym(RTLD_DEFAULT, "__ns_register_aot_calls")); + if (registrar == nullptr) { + return; + } + + { + std::lock_guard lock(registrationMutex); + registrationOpen = true; + } + registrar(Register); + { + std::lock_guard lock(registrationMutex); + registrationOpen = false; + if (duplicateRegistrations > 0) { + Log(@"%zu duplicate AOT stub registration(s) ignored; the first registration wins " + @"(first duplicate: %s)", + duplicateRegistrations, firstDuplicate.c_str()); + } + } + hasStubs = !Registry().empty(); + }); +} + +void Register(const char* className, const char* selector, bool isStatic, + NSAOTCallHandler handler) { + if (className == nullptr || selector == nullptr || handler == nullptr) { + return; + } + + std::lock_guard lock(registrationMutex); + if (!registrationOpen) { + Log(@"AOT stub for %s %c[%s] ignored: stubs register only through __ns_register_aot_calls", + className, isStatic ? '+' : '-', selector); + return; + } + + ClassStubs& stubs = Registry()[className]; + auto& members = isStatic ? stubs.staticMembers : stubs.instanceMembers; + if (!members.emplace(sel_registerName(selector), handler).second) { + if (duplicateRegistrations++ == 0) { + firstDuplicate = std::string(isStatic ? "+[" : "-[") + className + " " + selector + "]"; + } + } +} + +StubScope FindStubScope(const char* className) { + StubScope scope; + if (!hasStubs || className == nullptr) { + return scope; + } + + const auto& registry = Registry(); + auto add = [&](std::string_view name) { + auto it = registry.find(name); + if (it != registry.end()) { + scope.classes.push_back(&it->second); + } + }; + + add(className); + Class klass = objc_getClass(className); + for (klass = klass != nil ? class_getSuperclass(klass) : nil; klass != nil; + klass = class_getSuperclass(klass)) { + add(class_getName(klass)); + } + return scope; +} + +bool FindInstanceMethod(const StubScope& scope, SEL selector, Binding& out) { + return Find(scope, false, selector, Trampolines::Method, out); +} + +bool FindStaticMethod(const StubScope& scope, SEL selector, Binding& out) { + return Find(scope, true, selector, Trampolines::Method, out); +} + +bool FindInstanceGetter(const StubScope& scope, SEL getter, Binding& out) { + return Find(scope, false, getter, Trampolines::InstanceGetter, out); +} + +bool FindStaticGetter(const StubScope& scope, SEL getter, Binding& out) { + return Find(scope, true, getter, Trampolines::StaticGetter, out); +} + +uint64_t ServedCallCount() { return servedCalls.load(std::memory_order_relaxed); } + +uint64_t DeclinedCallCount() { return declinedCalls.load(std::memory_order_relaxed); } + +namespace { + +void ThrowUnexpectedException(Isolate* isolate) { + isolate->ThrowException( + Exception::Error(tns::ToV8String(isolate, "Unexpected native error in an AOT call"))); +} + +bool IsNumeric(BinaryTypeEncodingType type) { + switch (type) { + case BinaryTypeEncodingType::BoolEncoding: + case BinaryTypeEncodingType::UnicharEncoding: + return true; + default: + return Interop::IsNumbericType(type); + } +} + +bool IsPointerSized(BinaryTypeEncodingType type) { + switch (type) { + case BinaryTypeEncodingType::IdEncoding: + case BinaryTypeEncodingType::InterfaceDeclarationReference: + case BinaryTypeEncodingType::InstanceTypeEncoding: + case BinaryTypeEncodingType::ProtocolEncoding: + case BinaryTypeEncodingType::ClassEncoding: + case BinaryTypeEncodingType::SelectorEncoding: + case BinaryTypeEncodingType::BlockEncoding: + case BinaryTypeEncodingType::FunctionPointerEncoding: + case BinaryTypeEncodingType::PointerEncoding: + case BinaryTypeEncodingType::CStringEncoding: + case BinaryTypeEncodingType::IncompleteArrayEncoding: + return true; + default: + return false; + } +} + +bool IsStruct(BinaryTypeEncodingType type) { + return type == BinaryTypeEncodingType::StructDeclarationReference || + type == BinaryTypeEncodingType::AnonymousStructEncoding; +} + +// Converts argument `index` with Interop::WriteValue against the parameter's +// declared encoding, exactly as the generic path fills its ffi buffer. `dest` +// must be large enough for that encoding's type. +bool ConvertArgument(NSAOTCallInfo callInfo, int index, bool (*accepts)(BinaryTypeEncodingType), + void* dest, BinaryTypeEncodingType* outType) { + const CallFrame& frame = FrameOf(callInfo); + Isolate* isolate = frame.info.GetIsolate(); + const TypeEncoding* encoding = ParameterEncoding(frame, index); + if (encoding == nullptr || !accepts(encoding->type)) { + std::string message = std::string("AOT stub for ") + frame.meta->selectorAsString() + + " read argument " + std::to_string(index) + + " with a getter that does not match its declared type"; + isolate->ThrowException(Exception::TypeError(tns::ToV8String(isolate, message))); + return false; + } + + try { + Interop::WriteValue(isolate->GetCurrentContext(), encoding, dest, frame.info[index]); + } catch (NativeScriptException& ex) { + ex.ReThrowToV8(isolate); + return false; + } catch (...) { + ThrowUnexpectedException(isolate); + return false; + } + if (outType != nullptr) { + *outType = encoding->type; + } + return !isolate->HasPendingException(); +} + +template +T Widen(BinaryTypeEncodingType type, const void* value) { + switch (type) { + case BinaryTypeEncodingType::BoolEncoding: + return static_cast(*static_cast(value)); + case BinaryTypeEncodingType::CharEncoding: + return static_cast(*static_cast(value)); + case BinaryTypeEncodingType::UCharEncoding: + return static_cast(*static_cast(value)); + case BinaryTypeEncodingType::ShortEncoding: + return static_cast(*static_cast(value)); + case BinaryTypeEncodingType::UShortEncoding: + return static_cast(*static_cast(value)); + case BinaryTypeEncodingType::UnicharEncoding: + return static_cast(*static_cast(value)); + case BinaryTypeEncodingType::IntEncoding: + return static_cast(*static_cast(value)); + case BinaryTypeEncodingType::UIntEncoding: + return static_cast(*static_cast(value)); + case BinaryTypeEncodingType::LongEncoding: + return static_cast(*static_cast(value)); + case BinaryTypeEncodingType::ULongEncoding: + return static_cast(*static_cast(value)); + case BinaryTypeEncodingType::LongLongEncoding: + return static_cast(*static_cast(value)); + case BinaryTypeEncodingType::ULongLongEncoding: + return static_cast(*static_cast(value)); + case BinaryTypeEncodingType::FloatEncoding: + return static_cast(*static_cast(value)); + case BinaryTypeEncodingType::DoubleEncoding: + return static_cast(*static_cast(value)); + default: + return T(); + } +} + +template +bool ReadNumber(NSAOTCallInfo callInfo, int index, T* out) { + alignas(8) uint8_t value[8] = {}; + BinaryTypeEncodingType type; + if (!ConvertArgument(callInfo, index, IsNumeric, value, &type)) { + return false; + } + *out = Widen(type, value); + return true; +} + +template +bool ReadPointer(NSAOTCallInfo callInfo, int index, T* out) { + void* value = nullptr; + if (!ConvertArgument(callInfo, index, IsPointerSized, &value, nullptr)) { + return false; + } + *out = (T)value; + return true; +} + +void SetReturnValue(const FunctionCallbackInfo& info, Local value) { + if (!value.IsEmpty()) { + info.GetReturnValue().Set(value); + } +} + +#ifdef DEBUG +// The generator derives `owned` and `marshalToPrimitive` from the same +// metadata the generic path reads; a mismatch means stale or wrong stubs. +void AssertReturnFlagsMatchMetadata(Isolate* isolate, const MethodMeta* meta, bool owned, + bool marshalToPrimitive) { + const TypeEncoding* returnEncoding = meta->encodings()->first(); + bool expectedOwned = meta->ownsReturnedCocoaObject() || meta->isInitializer(); + bool expectedMarshal = returnEncoding->type != BinaryTypeEncodingType::InstanceTypeEncoding; + bool isMutableString = + returnEncoding->type == BinaryTypeEncodingType::InterfaceDeclarationReference && + std::strcmp(returnEncoding->details.declarationReference.name.valuePtr(), + "NSMutableString") == 0; + bool marshalMatches = + marshalToPrimitive == expectedMarshal || (isMutableString && !marshalToPrimitive); + tns::Assert( + owned == expectedOwned && marshalMatches, isolate, + std::string("AOT stub return flags disagree with metadata for ") + meta->selectorAsString()); +} +#endif + +} // namespace + +} // namespace tns::aot + +using namespace tns; +using tns::aot::FrameOf; + +extern "C" { + +int __ns_aot_arg_count(NSAOTCallInfo callInfo) { return FrameOf(callInfo).info.Length(); } + +// Mirrors ArgConverter::Invoke and Interop::CallFunctionInternal for an +// instance receiver. +id __ns_aot_get_target(NSAOTCallInfo callInfo, SEL selector, SEL* outSelector, bool* outCallSuper) { + *outSelector = selector; + *outCallSuper = false; + + const auto& info = FrameOf(callInfo).info; + Local thiz = info.This(); + if (thiz->InternalFieldCount() < 1) { + return nil; + } + + Isolate* isolate = info.GetIsolate(); + BaseDataWrapper* wrapper = tns::GetValue(isolate, thiz); + if (wrapper == nullptr || wrapper->Type() != WrapperType::ObjCObject) { + return nil; + } + id target = static_cast(wrapper)->Data(); + if (target == nil) { + return nil; + } + + auto* state = Caches::StateFor(isolate); + if (state == nullptr) { + return nil; + } + if (state->caches == nullptr) { + state->caches = Caches::Get(isolate).get(); + } + auto& classPrototypes = state->caches->ClassPrototypes; + if (classPrototypes.size() != state->classPrototypesSize) { + state->callSuperByClass.clear(); + state->classPrototypesSize = classPrototypes.size(); + } + + Class klass = object_getClass(target); + bool callSuper; + auto it = state->callSuperByClass.find(klass); + if (it != state->callSuperByClass.end()) { + callSuper = it->second; + } else { + callSuper = + classPrototypes.find(std::string_view(class_getName(klass))) != classPrototypes.end(); + state->callSuperByClass.emplace(klass, callSuper); + } + + SEL swizzled = Interop::GetSwizzledMethodSelector(selector); + if ([target respondsToSelector:swizzled]) { + *outSelector = swizzled; + } + *outCallSuper = callSuper; + return target; +} + +// Mirrors MetadataBuilder::MethodCallback (class from the receiver) and +// PropertyNameGetterCallback (class the property was registered on). +Class __ns_aot_get_static_class(NSAOTCallInfo callInfo) { + const auto& frame = FrameOf(callInfo); + if (frame.staticGetterClassName != nullptr) { + return objc_getClass(frame.staticGetterClassName->c_str()); + } + + Local thiz = frame.info.This(); + if (!thiz->IsFunction()) { + return nil; + } + BaseDataWrapper* wrapper = tns::GetValue(frame.info.GetIsolate(), thiz); + if (wrapper == nullptr || wrapper->Type() != WrapperType::ObjCClass) { + return nil; + } + return static_cast(wrapper)->Klass(); +} + +bool __ns_aot_arg_object(NSAOTCallInfo info, int index, id* out) { + return aot::ReadPointer(info, index, out); +} + +bool __ns_aot_arg_bool(NSAOTCallInfo info, int index, BOOL* out) { + return aot::ReadNumber(info, index, out); +} + +bool __ns_aot_arg_int64(NSAOTCallInfo info, int index, int64_t* out) { + return aot::ReadNumber(info, index, out); +} + +bool __ns_aot_arg_uint64(NSAOTCallInfo info, int index, uint64_t* out) { + return aot::ReadNumber(info, index, out); +} + +bool __ns_aot_arg_double(NSAOTCallInfo info, int index, double* out) { + return aot::ReadNumber(info, index, out); +} + +bool __ns_aot_arg_selector(NSAOTCallInfo info, int index, SEL* out) { + return aot::ReadPointer(info, index, out); +} + +bool __ns_aot_arg_class(NSAOTCallInfo info, int index, Class* out) { + return aot::ReadPointer(info, index, out); +} + +bool __ns_aot_arg_pointer(NSAOTCallInfo info, int index, void** out) { + return aot::ReadPointer(info, index, out); +} + +bool __ns_aot_arg_struct(NSAOTCallInfo info, int index, NSAOTStructType type, void* dest) { + (void)type; + return aot::ConvertArgument(info, index, aot::IsStruct, dest, nullptr); +} + +void __ns_aot_return_object(NSAOTCallInfo callInfo, id value, bool owned, bool marshalToPrimitive) { + const auto& frame = FrameOf(callInfo); + Isolate* isolate = frame.info.GetIsolate(); +#ifdef DEBUG + aot::AssertReturnFlagsMatchMetadata(isolate, frame.meta, owned, marshalToPrimitive); +#endif + try { + aot::SetReturnValue(frame.info, Interop::ObjectToJsValue(isolate->GetCurrentContext(), value, + frame.meta->encodings()->first(), + marshalToPrimitive, owned, false)); + } catch (NativeScriptException& ex) { + ex.ReThrowToV8(isolate); + } catch (...) { + aot::ThrowUnexpectedException(isolate); + } +} + +void __ns_aot_return_bool(NSAOTCallInfo callInfo, BOOL value) { + const auto& info = FrameOf(callInfo).info; + info.GetReturnValue().Set(v8::Boolean::New(info.GetIsolate(), value)); +} + +void __ns_aot_return_int64(NSAOTCallInfo callInfo, int64_t value) { + const auto& info = FrameOf(callInfo).info; + info.GetReturnValue().Set(Number::New(info.GetIsolate(), static_cast(value))); +} + +void __ns_aot_return_uint64(NSAOTCallInfo callInfo, uint64_t value) { + const auto& info = FrameOf(callInfo).info; + info.GetReturnValue().Set(Number::New(info.GetIsolate(), static_cast(value))); +} + +void __ns_aot_return_double(NSAOTCallInfo callInfo, double value) { + const auto& info = FrameOf(callInfo).info; + info.GetReturnValue().Set(Number::New(info.GetIsolate(), value)); +} + +void __ns_aot_return_selector(NSAOTCallInfo callInfo, SEL value) { + const auto& info = FrameOf(callInfo).info; + Isolate* isolate = info.GetIsolate(); + if (value == nullptr) { + info.GetReturnValue().Set(Null(isolate)); + return; + } + info.GetReturnValue().Set(tns::ToV8String(isolate, sel_getName(value))); +} + +void __ns_aot_return_class(NSAOTCallInfo callInfo, Class value) { + const auto& info = FrameOf(callInfo).info; + Isolate* isolate = info.GetIsolate(); + try { + aot::SetReturnValue(info, Interop::ClassToJsValue(isolate->GetCurrentContext(), value)); + } catch (NativeScriptException& ex) { + ex.ReThrowToV8(isolate); + } catch (...) { + aot::ThrowUnexpectedException(isolate); + } +} + +void __ns_aot_return_pointer(NSAOTCallInfo callInfo, void* value) { + const auto& info = FrameOf(callInfo).info; + Isolate* isolate = info.GetIsolate(); + if (value == nullptr) { + info.GetReturnValue().Set(Null(isolate)); + return; + } + try { + aot::SetReturnValue(info, Pointer::NewInstance(isolate->GetCurrentContext(), value)); + } catch (NativeScriptException& ex) { + ex.ReThrowToV8(isolate); + } catch (...) { + aot::ThrowUnexpectedException(isolate); + } +} + +void __ns_aot_return_struct(NSAOTCallInfo callInfo, NSAOTStructType type, const void* data) { + const auto& info = FrameOf(callInfo).info; + Isolate* isolate = info.GetIsolate(); + const auto* structInfo = static_cast(type); + try { + aot::SetReturnValue( + info, Interop::StructToValue(isolate->GetCurrentContext(), const_cast(data), + *structInfo, nullptr)); + } catch (NativeScriptException& ex) { + ex.ReThrowToV8(isolate); + } catch (...) { + aot::ThrowUnexpectedException(isolate); + } +} + +NSAOTStructType __ns_aot_struct_type(const char* name) { + if (name == nullptr) { + return nullptr; + } + + try { + const Meta* meta = ArgConverter::GetMeta(name); + if (meta == nullptr || meta->type() != MetaType::Struct) { + return nullptr; + } + // The struct-info cache owns each StructInfo for the process lifetime, so + // its address is a stable handle. + return &FFICall::GetStructInfo(static_cast(meta)); + } catch (...) { + return nullptr; + } +} + +// Mirrors the NSException handling in Interop::CallFunctionInternal and its +// rethrow from MetadataBuilder::InvokeMethod. +void __ns_aot_throw_exception(NSAOTCallInfo callInfo, id exception) { + const auto& info = FrameOf(callInfo).info; + Isolate* isolate = info.GetIsolate(); + try { + if (![exception isKindOfClass:[NSException class]]) { + NativeScriptException(tns::ToString(isolate, [exception description] ?: @"nil")) + .ReThrowToV8(isolate); + return; + } + + std::string message; + Local jsError = + Interop::NSExceptionToJsError(isolate->GetCurrentContext(), exception, message); + if (jsError.IsEmpty()) { + NativeScriptException(tns::ToString(isolate, [exception description])).ReThrowToV8(isolate); + } else { + NativeScriptException(isolate, jsError, message).ReThrowToV8(isolate); + } + } catch (NativeScriptException& ex) { + ex.ReThrowToV8(isolate); + } catch (...) { + aot::ThrowUnexpectedException(isolate); + } +} + +} // extern "C" diff --git a/NativeScript/runtime/Runtime.mm b/NativeScript/runtime/Runtime.mm index 216fb7104..776612982 100644 --- a/NativeScript/runtime/Runtime.mm +++ b/NativeScript/runtime/Runtime.mm @@ -1,6 +1,7 @@ #include "Runtime.h" #include #include +#include "AOTCalls.h" #include "ArgConverter.h" #include "BuiltinLoader.h" #include "Caches.h" @@ -417,6 +418,7 @@ void DisposeIsolateWhenPossible(Isolate* isolate) { } ObjectManager::Init(isolate, globalTemplate); LazyGlobals::Init(isolate, globalTemplate); + aot::DiscoverExternalStubs(); MetadataBuilder::RegisterConstantsOnGlobalObject(isolate, globalTemplate, isWorker); isolate->SetCaptureStackTraceForUncaughtExceptions(true, 100, StackTrace::kOverview); diff --git a/TestFixtures/exported-symbols.txt b/TestFixtures/exported-symbols.txt index 9a915ec8e..0ae2c6c1e 100644 --- a/TestFixtures/exported-symbols.txt +++ b/TestFixtures/exported-symbols.txt @@ -221,3 +221,4 @@ __SCNVector3FromFloat3 __SCNVector4FromFloat4 __SCNMatrix4FromMat4 _getStructFromCallback +___ns_register_aot_calls diff --git a/TestRunner/AOT/NativeScriptAOTStubs.m b/TestRunner/AOT/NativeScriptAOTStubs.m new file mode 100644 index 000000000..47a812b1d --- /dev/null +++ b/TestRunner/AOT/NativeScriptAOTStubs.m @@ -0,0 +1,1256 @@ +// Generated by scripts/generate-aot.py. Do not edit. +#import +#import +#import +#import + +typedef void (*AOTSendFn)(void); + +// x86_64 returns a struct through a hidden pointer, and needs the _stret entry +// points, when it is classified MEMORY. Size over 16 bytes approximates that +// classification; smaller structs with unaligned or long double fields are not +// modeled. arm64 never uses _stret. +#if defined(__x86_64__) +#define AOT_SEND_STRET(T) (sizeof(T) > 16 ? (AOTSendFn)objc_msgSend_stret : (AOTSendFn)objc_msgSend) +#define AOT_SEND_SUPER_STRET(T) \ + (sizeof(T) > 16 ? (AOTSendFn)objc_msgSendSuper_stret : (AOTSendFn)objc_msgSendSuper) +#else +#define AOT_SEND_STRET(T) ((AOTSendFn)objc_msgSend) +#define AOT_SEND_SUPER_STRET(T) ((AOTSendFn)objc_msgSendSuper) +#endif + +typedef struct { + int x; + int y; +} AOT_TNSSimpleStruct; +typedef struct { + AOT_TNSSimpleStruct a; + AOT_TNSSimpleStruct b; +} AOT_TNSNestedStruct; +typedef struct { + long long x; + long long y; + long long z; +} AOT_TNSStruct32; +typedef struct { + int x1; + struct { short x2; struct { signed char x3[2]; } y2; } y1[2]; + long long x4; +} AOT_TNSComplexStruct; +typedef struct { + double x; + double y; +} AOT_CGPoint; +typedef struct { + double width; + double height; +} AOT_CGSize; +typedef struct { + AOT_CGPoint origin; + AOT_CGSize size; +} AOT_CGRect; + +static bool AOT_TNSBaseInterface_baseMethod(NSAOTCallInfo info) { + if (__ns_aot_arg_count(info) != 0) return false; + SEL sel = @selector(baseMethod); + bool callSuper = false; + id target = __ns_aot_get_target(info, sel, &sel, &callSuper); + if (target == nil) return false; + @try { + if (callSuper) { + struct objc_super sup = {target, class_getSuperclass(object_getClass(target))}; + ((void (*)(struct objc_super*, SEL))objc_msgSendSuper)(&sup, sel); + } else { + ((void (*)(id, SEL))objc_msgSend)(target, sel); + } + } @catch (NSException* e) { + __ns_aot_throw_exception(info, e); + return true; + } + return true; +} + +static bool AOT_TNSBaseInterface_baseMethod_static(NSAOTCallInfo info) { + if (__ns_aot_arg_count(info) != 0) return false; + SEL sel = @selector(baseMethod); + Class cls = __ns_aot_get_static_class(info); + if (cls == nil) return false; + @try { + ((void (*)(id, SEL))objc_msgSend)((id)cls, sel); + } @catch (NSException* e) { + __ns_aot_throw_exception(info, e); + return true; + } + return true; +} + +static bool AOT_TNSBaseInterface_methodWithParam(NSAOTCallInfo info) { + if (__ns_aot_arg_count(info) != 1) return false; + SEL sel = @selector(methodWithParam:); + bool callSuper = false; + id target = __ns_aot_get_target(info, sel, &sel, &callSuper); + if (target == nil) return false; + id a0 = nil; + if (!__ns_aot_arg_object(info, 0, &a0)) return true; + @try { + if (callSuper) { + struct objc_super sup = {target, class_getSuperclass(object_getClass(target))}; + ((void (*)(struct objc_super*, SEL, id))objc_msgSendSuper)(&sup, sel, a0); + } else { + ((void (*)(id, SEL, id))objc_msgSend)(target, sel, a0); + } + } @catch (NSException* e) { + __ns_aot_throw_exception(info, e); + return true; + } + return true; +} + +static bool AOT_TNSDerivedInterface_methodWith_param(NSAOTCallInfo info) { + if (__ns_aot_arg_count(info) != 2) return false; + SEL sel = @selector(methodWith:param:); + bool callSuper = false; + id target = __ns_aot_get_target(info, sel, &sel, &callSuper); + if (target == nil) return false; + id a0 = nil; + if (!__ns_aot_arg_object(info, 0, &a0)) return true; + id a1 = nil; + if (!__ns_aot_arg_object(info, 1, &a1)) return true; + @try { + if (callSuper) { + struct objc_super sup = {target, class_getSuperclass(object_getClass(target))}; + ((void (*)(struct objc_super*, SEL, id, id))objc_msgSendSuper)(&sup, sel, a0, a1); + } else { + ((void (*)(id, SEL, id, id))objc_msgSend)(target, sel, a0, a1); + } + } @catch (NSException* e) { + __ns_aot_throw_exception(info, e); + return true; + } + return true; +} + +static bool AOT_TNSBaseInterface_callBaseMethod(NSAOTCallInfo info) { + if (__ns_aot_arg_count(info) != 1) return false; + SEL sel = @selector(callBaseMethod:); + bool callSuper = false; + id target = __ns_aot_get_target(info, sel, &sel, &callSuper); + if (target == nil) return false; + BOOL a0 = NO; + if (!__ns_aot_arg_bool(info, 0, &a0)) return true; + @try { + if (callSuper) { + struct objc_super sup = {target, class_getSuperclass(object_getClass(target))}; + ((void (*)(struct objc_super*, SEL, BOOL))objc_msgSendSuper)(&sup, sel, a0); + } else { + ((void (*)(id, SEL, BOOL))objc_msgSend)(target, sel, a0); + } + } @catch (NSException* e) { + __ns_aot_throw_exception(info, e); + return true; + } + return true; +} + +static bool AOT_TNSBaseInterface_baseProperty(NSAOTCallInfo info) { + if (__ns_aot_arg_count(info) != 0) return false; + SEL sel = @selector(baseProperty); + bool callSuper = false; + id target = __ns_aot_get_target(info, sel, &sel, &callSuper); + if (target == nil) return false; + int r = 0; + @try { + if (callSuper) { + struct objc_super sup = {target, class_getSuperclass(object_getClass(target))}; + r = ((int (*)(struct objc_super*, SEL))objc_msgSendSuper)(&sup, sel); + } else { + r = ((int (*)(id, SEL))objc_msgSend)(target, sel); + } + } @catch (NSException* e) { + __ns_aot_throw_exception(info, e); + return true; + } + __ns_aot_return_int64(info, (int64_t)r); + return true; +} + +static bool AOT_TNSBaseInterface_setBaseProperty(NSAOTCallInfo info) { + if (__ns_aot_arg_count(info) != 1) return false; + SEL sel = @selector(setBaseProperty:); + bool callSuper = false; + id target = __ns_aot_get_target(info, sel, &sel, &callSuper); + if (target == nil) return false; + int64_t raw0 = 0; + if (!__ns_aot_arg_int64(info, 0, &raw0)) return true; + int a0 = (int)raw0; + @try { + if (callSuper) { + struct objc_super sup = {target, class_getSuperclass(object_getClass(target))}; + ((void (*)(struct objc_super*, SEL, int))objc_msgSendSuper)(&sup, sel, a0); + } else { + ((void (*)(id, SEL, int))objc_msgSend)(target, sel, a0); + } + } @catch (NSException* e) { + __ns_aot_throw_exception(info, e); + return true; + } + return true; +} + +static bool AOT_TNSBaseInterface_baseProperty_static(NSAOTCallInfo info) { + if (__ns_aot_arg_count(info) != 0) return false; + SEL sel = @selector(baseProperty); + Class cls = __ns_aot_get_static_class(info); + if (cls == nil) return false; + int r = 0; + @try { + r = ((int (*)(id, SEL))objc_msgSend)((id)cls, sel); + } @catch (NSException* e) { + __ns_aot_throw_exception(info, e); + return true; + } + __ns_aot_return_int64(info, (int64_t)r); + return true; +} + +static bool AOT_TNSApi_customGetter(NSAOTCallInfo info) { + if (__ns_aot_arg_count(info) != 0) return false; + SEL sel = @selector(customGetter); + bool callSuper = false; + id target = __ns_aot_get_target(info, sel, &sel, &callSuper); + if (target == nil) return false; + int r = 0; + @try { + if (callSuper) { + struct objc_super sup = {target, class_getSuperclass(object_getClass(target))}; + r = ((int (*)(struct objc_super*, SEL))objc_msgSendSuper)(&sup, sel); + } else { + r = ((int (*)(id, SEL))objc_msgSend)(target, sel); + } + } @catch (NSException* e) { + __ns_aot_throw_exception(info, e); + return true; + } + __ns_aot_return_int64(info, (int64_t)r); + return true; +} + +static bool AOT_TNSApi_methodThrowsException(NSAOTCallInfo info) { + if (__ns_aot_arg_count(info) != 0) return false; + SEL sel = @selector(methodThrowsException); + bool callSuper = false; + id target = __ns_aot_get_target(info, sel, &sel, &callSuper); + if (target == nil) return false; + @try { + if (callSuper) { + struct objc_super sup = {target, class_getSuperclass(object_getClass(target))}; + ((void (*)(struct objc_super*, SEL))objc_msgSendSuper)(&sup, sel); + } else { + ((void (*)(id, SEL))objc_msgSend)(target, sel); + } + } @catch (NSException* e) { + __ns_aot_throw_exception(info, e); + return true; + } + return true; +} + +static bool AOT_TNSSwizzleKlass_instanceMethod(NSAOTCallInfo info) { + if (__ns_aot_arg_count(info) != 1) return false; + SEL sel = @selector(instanceMethod:); + bool callSuper = false; + id target = __ns_aot_get_target(info, sel, &sel, &callSuper); + if (target == nil) return false; + int64_t raw0 = 0; + if (!__ns_aot_arg_int64(info, 0, &raw0)) return true; + int a0 = (int)raw0; + int r = 0; + @try { + if (callSuper) { + struct objc_super sup = {target, class_getSuperclass(object_getClass(target))}; + r = ((int (*)(struct objc_super*, SEL, int))objc_msgSendSuper)(&sup, sel, a0); + } else { + r = ((int (*)(id, SEL, int))objc_msgSend)(target, sel, a0); + } + } @catch (NSException* e) { + __ns_aot_throw_exception(info, e); + return true; + } + __ns_aot_return_int64(info, (int64_t)r); + return true; +} + +static bool AOT_TNSSwizzleKlass_staticMethod_static(NSAOTCallInfo info) { + if (__ns_aot_arg_count(info) != 1) return false; + SEL sel = @selector(staticMethod:); + Class cls = __ns_aot_get_static_class(info); + if (cls == nil) return false; + int64_t raw0 = 0; + if (!__ns_aot_arg_int64(info, 0, &raw0)) return true; + int a0 = (int)raw0; + int r = 0; + @try { + r = ((int (*)(id, SEL, int))objc_msgSend)((id)cls, sel, a0); + } @catch (NSException* e) { + __ns_aot_throw_exception(info, e); + return true; + } + __ns_aot_return_int64(info, (int64_t)r); + return true; +} + +static bool AOT_TNSPrimitives_methodWithChar(NSAOTCallInfo info) { + if (__ns_aot_arg_count(info) != 1) return false; + SEL sel = @selector(methodWithChar:); + bool callSuper = false; + id target = __ns_aot_get_target(info, sel, &sel, &callSuper); + if (target == nil) return false; + int64_t raw0 = 0; + if (!__ns_aot_arg_int64(info, 0, &raw0)) return true; + signed char a0 = (signed char)raw0; + signed char r = 0; + @try { + if (callSuper) { + struct objc_super sup = {target, class_getSuperclass(object_getClass(target))}; + r = ((signed char (*)(struct objc_super*, SEL, signed char))objc_msgSendSuper)(&sup, sel, a0); + } else { + r = ((signed char (*)(id, SEL, signed char))objc_msgSend)(target, sel, a0); + } + } @catch (NSException* e) { + __ns_aot_throw_exception(info, e); + return true; + } + __ns_aot_return_int64(info, (int64_t)r); + return true; +} + +static bool AOT_TNSPrimitives_methodWithUChar(NSAOTCallInfo info) { + if (__ns_aot_arg_count(info) != 1) return false; + SEL sel = @selector(methodWithUChar:); + bool callSuper = false; + id target = __ns_aot_get_target(info, sel, &sel, &callSuper); + if (target == nil) return false; + uint64_t raw0 = 0; + if (!__ns_aot_arg_uint64(info, 0, &raw0)) return true; + unsigned char a0 = (unsigned char)raw0; + unsigned char r = 0; + @try { + if (callSuper) { + struct objc_super sup = {target, class_getSuperclass(object_getClass(target))}; + r = ((unsigned char (*)(struct objc_super*, SEL, unsigned char))objc_msgSendSuper)(&sup, sel, a0); + } else { + r = ((unsigned char (*)(id, SEL, unsigned char))objc_msgSend)(target, sel, a0); + } + } @catch (NSException* e) { + __ns_aot_throw_exception(info, e); + return true; + } + __ns_aot_return_uint64(info, (uint64_t)r); + return true; +} + +static bool AOT_TNSPrimitives_methodWithInt(NSAOTCallInfo info) { + if (__ns_aot_arg_count(info) != 1) return false; + SEL sel = @selector(methodWithInt:); + bool callSuper = false; + id target = __ns_aot_get_target(info, sel, &sel, &callSuper); + if (target == nil) return false; + int64_t raw0 = 0; + if (!__ns_aot_arg_int64(info, 0, &raw0)) return true; + int a0 = (int)raw0; + int r = 0; + @try { + if (callSuper) { + struct objc_super sup = {target, class_getSuperclass(object_getClass(target))}; + r = ((int (*)(struct objc_super*, SEL, int))objc_msgSendSuper)(&sup, sel, a0); + } else { + r = ((int (*)(id, SEL, int))objc_msgSend)(target, sel, a0); + } + } @catch (NSException* e) { + __ns_aot_throw_exception(info, e); + return true; + } + __ns_aot_return_int64(info, (int64_t)r); + return true; +} + +static bool AOT_TNSPrimitives_methodWithFloat(NSAOTCallInfo info) { + if (__ns_aot_arg_count(info) != 1) return false; + SEL sel = @selector(methodWithFloat:); + bool callSuper = false; + id target = __ns_aot_get_target(info, sel, &sel, &callSuper); + if (target == nil) return false; + double raw0 = 0; + if (!__ns_aot_arg_double(info, 0, &raw0)) return true; + float a0 = (float)raw0; + float r = 0; + @try { + if (callSuper) { + struct objc_super sup = {target, class_getSuperclass(object_getClass(target))}; + r = ((float (*)(struct objc_super*, SEL, float))objc_msgSendSuper)(&sup, sel, a0); + } else { + r = ((float (*)(id, SEL, float))objc_msgSend)(target, sel, a0); + } + } @catch (NSException* e) { + __ns_aot_throw_exception(info, e); + return true; + } + __ns_aot_return_double(info, (double)r); + return true; +} + +static bool AOT_TNSPrimitives_methodWithDouble_static(NSAOTCallInfo info) { + if (__ns_aot_arg_count(info) != 1) return false; + SEL sel = @selector(methodWithDouble:); + Class cls = __ns_aot_get_static_class(info); + if (cls == nil) return false; + double raw0 = 0; + if (!__ns_aot_arg_double(info, 0, &raw0)) return true; + double a0 = (double)raw0; + double r = 0; + @try { + r = ((double (*)(id, SEL, double))objc_msgSend)((id)cls, sel, a0); + } @catch (NSException* e) { + __ns_aot_throw_exception(info, e); + return true; + } + __ns_aot_return_double(info, r); + return true; +} + +static bool AOT_TNSPrimitives_methodWithBool2(NSAOTCallInfo info) { + if (__ns_aot_arg_count(info) != 1) return false; + SEL sel = @selector(methodWithBool2:); + bool callSuper = false; + id target = __ns_aot_get_target(info, sel, &sel, &callSuper); + if (target == nil) return false; + BOOL a0 = NO; + if (!__ns_aot_arg_bool(info, 0, &a0)) return true; + BOOL r = NO; + @try { + if (callSuper) { + struct objc_super sup = {target, class_getSuperclass(object_getClass(target))}; + r = ((BOOL (*)(struct objc_super*, SEL, BOOL))objc_msgSendSuper)(&sup, sel, a0); + } else { + r = ((BOOL (*)(id, SEL, BOOL))objc_msgSend)(target, sel, a0); + } + } @catch (NSException* e) { + __ns_aot_throw_exception(info, e); + return true; + } + __ns_aot_return_bool(info, r); + return true; +} + +static bool AOT_TNSPrimitives_methodWithBool3(NSAOTCallInfo info) { + if (__ns_aot_arg_count(info) != 1) return false; + SEL sel = @selector(methodWithBool3:); + bool callSuper = false; + id target = __ns_aot_get_target(info, sel, &sel, &callSuper); + if (target == nil) return false; + BOOL a0 = NO; + if (!__ns_aot_arg_bool(info, 0, &a0)) return true; + BOOL r = NO; + @try { + if (callSuper) { + struct objc_super sup = {target, class_getSuperclass(object_getClass(target))}; + r = ((BOOL (*)(struct objc_super*, SEL, BOOL))objc_msgSendSuper)(&sup, sel, a0); + } else { + r = ((BOOL (*)(id, SEL, BOOL))objc_msgSend)(target, sel, a0); + } + } @catch (NSException* e) { + __ns_aot_throw_exception(info, e); + return true; + } + __ns_aot_return_bool(info, r); + return true; +} + +static bool AOT_TNSPrimitives_methodWithSelector(NSAOTCallInfo info) { + if (__ns_aot_arg_count(info) != 1) return false; + SEL sel = @selector(methodWithSelector:); + bool callSuper = false; + id target = __ns_aot_get_target(info, sel, &sel, &callSuper); + if (target == nil) return false; + SEL a0 = NULL; + if (!__ns_aot_arg_selector(info, 0, &a0)) return true; + SEL r = NULL; + @try { + if (callSuper) { + struct objc_super sup = {target, class_getSuperclass(object_getClass(target))}; + r = ((SEL (*)(struct objc_super*, SEL, SEL))objc_msgSendSuper)(&sup, sel, a0); + } else { + r = ((SEL (*)(id, SEL, SEL))objc_msgSend)(target, sel, a0); + } + } @catch (NSException* e) { + __ns_aot_throw_exception(info, e); + return true; + } + __ns_aot_return_selector(info, r); + return true; +} + +static bool AOT_TNSPrimitives_methodWithClass(NSAOTCallInfo info) { + if (__ns_aot_arg_count(info) != 1) return false; + SEL sel = @selector(methodWithClass:); + bool callSuper = false; + id target = __ns_aot_get_target(info, sel, &sel, &callSuper); + if (target == nil) return false; + Class a0 = Nil; + if (!__ns_aot_arg_class(info, 0, &a0)) return true; + Class r = Nil; + @try { + if (callSuper) { + struct objc_super sup = {target, class_getSuperclass(object_getClass(target))}; + r = ((Class (*)(struct objc_super*, SEL, Class))objc_msgSendSuper)(&sup, sel, a0); + } else { + r = ((Class (*)(id, SEL, Class))objc_msgSend)(target, sel, a0); + } + } @catch (NSException* e) { + __ns_aot_throw_exception(info, e); + return true; + } + __ns_aot_return_class(info, r); + return true; +} + +static bool AOT_TNSPrimitives_methodWithNSNumber(NSAOTCallInfo info) { + if (__ns_aot_arg_count(info) != 1) return false; + SEL sel = @selector(methodWithNSNumber:); + bool callSuper = false; + id target = __ns_aot_get_target(info, sel, &sel, &callSuper); + if (target == nil) return false; + id a0 = nil; + if (!__ns_aot_arg_object(info, 0, &a0)) return true; + id r = nil; + @try { + if (callSuper) { + struct objc_super sup = {target, class_getSuperclass(object_getClass(target))}; + r = ((id (*)(struct objc_super*, SEL, id))objc_msgSendSuper)(&sup, sel, a0); + } else { + r = ((id (*)(id, SEL, id))objc_msgSend)(target, sel, a0); + } + } @catch (NSException* e) { + __ns_aot_throw_exception(info, e); + return true; + } + __ns_aot_return_object(info, r, false, true); + return true; +} + +static bool AOT_TNSPrimitives_methodWithId_static(NSAOTCallInfo info) { + if (__ns_aot_arg_count(info) != 1) return false; + SEL sel = @selector(methodWithId:); + Class cls = __ns_aot_get_static_class(info); + if (cls == nil) return false; + id a0 = nil; + if (!__ns_aot_arg_object(info, 0, &a0)) return true; + id r = nil; + @try { + r = ((id (*)(id, SEL, id))objc_msgSend)((id)cls, sel, a0); + } @catch (NSException* e) { + __ns_aot_throw_exception(info, e); + return true; + } + __ns_aot_return_object(info, r, false, true); + return true; +} + +static bool AOT_TNSObjCTypes_methodWithNSDate(NSAOTCallInfo info) { + if (__ns_aot_arg_count(info) != 1) return false; + SEL sel = @selector(methodWithNSDate:); + bool callSuper = false; + id target = __ns_aot_get_target(info, sel, &sel, &callSuper); + if (target == nil) return false; + id a0 = nil; + if (!__ns_aot_arg_object(info, 0, &a0)) return true; + id r = nil; + @try { + if (callSuper) { + struct objc_super sup = {target, class_getSuperclass(object_getClass(target))}; + r = ((id (*)(struct objc_super*, SEL, id))objc_msgSendSuper)(&sup, sel, a0); + } else { + r = ((id (*)(id, SEL, id))objc_msgSend)(target, sel, a0); + } + } @catch (NSException* e) { + __ns_aot_throw_exception(info, e); + return true; + } + __ns_aot_return_object(info, r, false, true); + return true; +} + +static bool AOT_TNSTestNativeCallbacks_recordsSimpleStruct_static(NSAOTCallInfo info) { + if (__ns_aot_arg_count(info) != 1) return false; + static NSAOTStructType st0 = NULL; + if (!st0) st0 = __ns_aot_struct_type("TNSSimpleStruct"); + if (!st0) return false; + SEL sel = @selector(recordsSimpleStruct:); + Class cls = __ns_aot_get_static_class(info); + if (cls == nil) return false; + AOT_TNSSimpleStruct a0; + if (!__ns_aot_arg_struct(info, 0, st0, &a0)) return true; + AOT_TNSSimpleStruct r; + @try { + r = ((AOT_TNSSimpleStruct (*)(id, SEL, AOT_TNSSimpleStruct))AOT_SEND_STRET(AOT_TNSSimpleStruct))((id)cls, sel, a0); + } @catch (NSException* e) { + __ns_aot_throw_exception(info, e); + return true; + } + __ns_aot_return_struct(info, st0, &r); + return true; +} + +static bool AOT_TNSTestNativeCallbacks_recordsNestedStruct_static(NSAOTCallInfo info) { + if (__ns_aot_arg_count(info) != 1) return false; + static NSAOTStructType st0 = NULL; + if (!st0) st0 = __ns_aot_struct_type("TNSNestedStruct"); + if (!st0) return false; + SEL sel = @selector(recordsNestedStruct:); + Class cls = __ns_aot_get_static_class(info); + if (cls == nil) return false; + AOT_TNSNestedStruct a0; + if (!__ns_aot_arg_struct(info, 0, st0, &a0)) return true; + AOT_TNSNestedStruct r; + @try { + r = ((AOT_TNSNestedStruct (*)(id, SEL, AOT_TNSNestedStruct))AOT_SEND_STRET(AOT_TNSNestedStruct))((id)cls, sel, a0); + } @catch (NSException* e) { + __ns_aot_throw_exception(info, e); + return true; + } + __ns_aot_return_struct(info, st0, &r); + return true; +} + +static bool AOT_TNSTestNativeCallbacks_recordsStruct32_static(NSAOTCallInfo info) { + if (__ns_aot_arg_count(info) != 1) return false; + static NSAOTStructType st0 = NULL; + if (!st0) st0 = __ns_aot_struct_type("TNSStruct32"); + if (!st0) return false; + SEL sel = @selector(recordsStruct32:); + Class cls = __ns_aot_get_static_class(info); + if (cls == nil) return false; + AOT_TNSStruct32 a0; + if (!__ns_aot_arg_struct(info, 0, st0, &a0)) return true; + AOT_TNSStruct32 r; + @try { + r = ((AOT_TNSStruct32 (*)(id, SEL, AOT_TNSStruct32))AOT_SEND_STRET(AOT_TNSStruct32))((id)cls, sel, a0); + } @catch (NSException* e) { + __ns_aot_throw_exception(info, e); + return true; + } + __ns_aot_return_struct(info, st0, &r); + return true; +} + +static bool AOT_TNSTestNativeCallbacks_recordsComplexStruct_static(NSAOTCallInfo info) { + if (__ns_aot_arg_count(info) != 1) return false; + static NSAOTStructType st0 = NULL; + if (!st0) st0 = __ns_aot_struct_type("TNSComplexStruct"); + if (!st0) return false; + SEL sel = @selector(recordsComplexStruct:); + Class cls = __ns_aot_get_static_class(info); + if (cls == nil) return false; + AOT_TNSComplexStruct a0; + if (!__ns_aot_arg_struct(info, 0, st0, &a0)) return true; + AOT_TNSComplexStruct r; + @try { + r = ((AOT_TNSComplexStruct (*)(id, SEL, AOT_TNSComplexStruct))AOT_SEND_STRET(AOT_TNSComplexStruct))((id)cls, sel, a0); + } @catch (NSException* e) { + __ns_aot_throw_exception(info, e); + return true; + } + __ns_aot_return_struct(info, st0, &r); + return true; +} + +static bool AOT_RectClass_getRect(NSAOTCallInfo info) { + if (__ns_aot_arg_count(info) != 0) return false; + static NSAOTStructType st0 = NULL; + if (!st0) st0 = __ns_aot_struct_type("CGRect"); + if (!st0) return false; + SEL sel = @selector(getRect); + bool callSuper = false; + id target = __ns_aot_get_target(info, sel, &sel, &callSuper); + if (target == nil) return false; + AOT_CGRect r; + @try { + if (callSuper) { + struct objc_super sup = {target, class_getSuperclass(object_getClass(target))}; + r = ((AOT_CGRect (*)(struct objc_super*, SEL))AOT_SEND_SUPER_STRET(AOT_CGRect))(&sup, sel); + } else { + r = ((AOT_CGRect (*)(id, SEL))AOT_SEND_STRET(AOT_CGRect))(target, sel); + } + } @catch (NSException* e) { + __ns_aot_throw_exception(info, e); + return true; + } + __ns_aot_return_struct(info, st0, &r); + return true; +} + +static bool AOT_UIScreen_mainScreen_static(NSAOTCallInfo info) { + if (__ns_aot_arg_count(info) != 0) return false; + SEL sel = @selector(mainScreen); + Class cls = __ns_aot_get_static_class(info); + if (cls == nil) return false; + id r = nil; + @try { + r = ((id (*)(id, SEL))objc_msgSend)((id)cls, sel); + } @catch (NSException* e) { + __ns_aot_throw_exception(info, e); + return true; + } + __ns_aot_return_object(info, r, false, true); + return true; +} + +static bool AOT_UIScreen_scale(NSAOTCallInfo info) { + if (__ns_aot_arg_count(info) != 0) return false; + SEL sel = @selector(scale); + bool callSuper = false; + id target = __ns_aot_get_target(info, sel, &sel, &callSuper); + if (target == nil) return false; + double r = 0; + @try { + if (callSuper) { + struct objc_super sup = {target, class_getSuperclass(object_getClass(target))}; + r = ((double (*)(struct objc_super*, SEL))objc_msgSendSuper)(&sup, sel); + } else { + r = ((double (*)(id, SEL))objc_msgSend)(target, sel); + } + } @catch (NSException* e) { + __ns_aot_throw_exception(info, e); + return true; + } + __ns_aot_return_double(info, r); + return true; +} + +static bool AOT_UIScreen_nativeScale(NSAOTCallInfo info) { + if (__ns_aot_arg_count(info) != 0) return false; + SEL sel = @selector(nativeScale); + bool callSuper = false; + id target = __ns_aot_get_target(info, sel, &sel, &callSuper); + if (target == nil) return false; + double r = 0; + @try { + if (callSuper) { + struct objc_super sup = {target, class_getSuperclass(object_getClass(target))}; + r = ((double (*)(struct objc_super*, SEL))objc_msgSendSuper)(&sup, sel); + } else { + r = ((double (*)(id, SEL))objc_msgSend)(target, sel); + } + } @catch (NSException* e) { + __ns_aot_throw_exception(info, e); + return true; + } + __ns_aot_return_double(info, r); + return true; +} + +static bool AOT_UIScreen_bounds(NSAOTCallInfo info) { + if (__ns_aot_arg_count(info) != 0) return false; + static NSAOTStructType st0 = NULL; + if (!st0) st0 = __ns_aot_struct_type("CGRect"); + if (!st0) return false; + SEL sel = @selector(bounds); + bool callSuper = false; + id target = __ns_aot_get_target(info, sel, &sel, &callSuper); + if (target == nil) return false; + AOT_CGRect r; + @try { + if (callSuper) { + struct objc_super sup = {target, class_getSuperclass(object_getClass(target))}; + r = ((AOT_CGRect (*)(struct objc_super*, SEL))AOT_SEND_SUPER_STRET(AOT_CGRect))(&sup, sel); + } else { + r = ((AOT_CGRect (*)(id, SEL))AOT_SEND_STRET(AOT_CGRect))(target, sel); + } + } @catch (NSException* e) { + __ns_aot_throw_exception(info, e); + return true; + } + __ns_aot_return_struct(info, st0, &r); + return true; +} + +static bool AOT_NSObject_description(NSAOTCallInfo info) { + if (__ns_aot_arg_count(info) != 0) return false; + SEL sel = @selector(description); + bool callSuper = false; + id target = __ns_aot_get_target(info, sel, &sel, &callSuper); + if (target == nil) return false; + id r = nil; + @try { + if (callSuper) { + struct objc_super sup = {target, class_getSuperclass(object_getClass(target))}; + r = ((id (*)(struct objc_super*, SEL))objc_msgSendSuper)(&sup, sel); + } else { + r = ((id (*)(id, SEL))objc_msgSend)(target, sel); + } + } @catch (NSException* e) { + __ns_aot_throw_exception(info, e); + return true; + } + __ns_aot_return_object(info, r, false, true); + return true; +} + +static bool AOT_NSObject_respondsToSelector(NSAOTCallInfo info) { + if (__ns_aot_arg_count(info) != 1) return false; + SEL sel = @selector(respondsToSelector:); + bool callSuper = false; + id target = __ns_aot_get_target(info, sel, &sel, &callSuper); + if (target == nil) return false; + SEL a0 = NULL; + if (!__ns_aot_arg_selector(info, 0, &a0)) return true; + BOOL r = NO; + @try { + if (callSuper) { + struct objc_super sup = {target, class_getSuperclass(object_getClass(target))}; + r = ((BOOL (*)(struct objc_super*, SEL, SEL))objc_msgSendSuper)(&sup, sel, a0); + } else { + r = ((BOOL (*)(id, SEL, SEL))objc_msgSend)(target, sel, a0); + } + } @catch (NSException* e) { + __ns_aot_throw_exception(info, e); + return true; + } + __ns_aot_return_bool(info, r); + return true; +} + +static bool AOT_NSObject_isKindOfClass(NSAOTCallInfo info) { + if (__ns_aot_arg_count(info) != 1) return false; + SEL sel = @selector(isKindOfClass:); + bool callSuper = false; + id target = __ns_aot_get_target(info, sel, &sel, &callSuper); + if (target == nil) return false; + Class a0 = Nil; + if (!__ns_aot_arg_class(info, 0, &a0)) return true; + BOOL r = NO; + @try { + if (callSuper) { + struct objc_super sup = {target, class_getSuperclass(object_getClass(target))}; + r = ((BOOL (*)(struct objc_super*, SEL, Class))objc_msgSendSuper)(&sup, sel, a0); + } else { + r = ((BOOL (*)(id, SEL, Class))objc_msgSend)(target, sel, a0); + } + } @catch (NSException* e) { + __ns_aot_throw_exception(info, e); + return true; + } + __ns_aot_return_bool(info, r); + return true; +} + +static bool AOT_NSObject_isEqual(NSAOTCallInfo info) { + if (__ns_aot_arg_count(info) != 1) return false; + SEL sel = @selector(isEqual:); + bool callSuper = false; + id target = __ns_aot_get_target(info, sel, &sel, &callSuper); + if (target == nil) return false; + id a0 = nil; + if (!__ns_aot_arg_object(info, 0, &a0)) return true; + BOOL r = NO; + @try { + if (callSuper) { + struct objc_super sup = {target, class_getSuperclass(object_getClass(target))}; + r = ((BOOL (*)(struct objc_super*, SEL, id))objc_msgSendSuper)(&sup, sel, a0); + } else { + r = ((BOOL (*)(id, SEL, id))objc_msgSend)(target, sel, a0); + } + } @catch (NSException* e) { + __ns_aot_throw_exception(info, e); + return true; + } + __ns_aot_return_bool(info, r); + return true; +} + +static bool AOT_NSArray_count(NSAOTCallInfo info) { + if (__ns_aot_arg_count(info) != 0) return false; + SEL sel = @selector(count); + bool callSuper = false; + id target = __ns_aot_get_target(info, sel, &sel, &callSuper); + if (target == nil) return false; + unsigned long r = 0; + @try { + if (callSuper) { + struct objc_super sup = {target, class_getSuperclass(object_getClass(target))}; + r = ((unsigned long (*)(struct objc_super*, SEL))objc_msgSendSuper)(&sup, sel); + } else { + r = ((unsigned long (*)(id, SEL))objc_msgSend)(target, sel); + } + } @catch (NSException* e) { + __ns_aot_throw_exception(info, e); + return true; + } + __ns_aot_return_uint64(info, (uint64_t)r); + return true; +} + +static bool AOT_NSArray_objectAtIndex(NSAOTCallInfo info) { + if (__ns_aot_arg_count(info) != 1) return false; + SEL sel = @selector(objectAtIndex:); + bool callSuper = false; + id target = __ns_aot_get_target(info, sel, &sel, &callSuper); + if (target == nil) return false; + uint64_t raw0 = 0; + if (!__ns_aot_arg_uint64(info, 0, &raw0)) return true; + unsigned long a0 = (unsigned long)raw0; + id r = nil; + @try { + if (callSuper) { + struct objc_super sup = {target, class_getSuperclass(object_getClass(target))}; + r = ((id (*)(struct objc_super*, SEL, unsigned long))objc_msgSendSuper)(&sup, sel, a0); + } else { + r = ((id (*)(id, SEL, unsigned long))objc_msgSend)(target, sel, a0); + } + } @catch (NSException* e) { + __ns_aot_throw_exception(info, e); + return true; + } + __ns_aot_return_object(info, r, false, true); + return true; +} + +static bool AOT_NSMutableArray_addObject(NSAOTCallInfo info) { + if (__ns_aot_arg_count(info) != 1) return false; + SEL sel = @selector(addObject:); + bool callSuper = false; + id target = __ns_aot_get_target(info, sel, &sel, &callSuper); + if (target == nil) return false; + id a0 = nil; + if (!__ns_aot_arg_object(info, 0, &a0)) return true; + @try { + if (callSuper) { + struct objc_super sup = {target, class_getSuperclass(object_getClass(target))}; + ((void (*)(struct objc_super*, SEL, id))objc_msgSendSuper)(&sup, sel, a0); + } else { + ((void (*)(id, SEL, id))objc_msgSend)(target, sel, a0); + } + } @catch (NSException* e) { + __ns_aot_throw_exception(info, e); + return true; + } + return true; +} + +static bool AOT_NSMutableArray_removeLastObject(NSAOTCallInfo info) { + if (__ns_aot_arg_count(info) != 0) return false; + SEL sel = @selector(removeLastObject); + bool callSuper = false; + id target = __ns_aot_get_target(info, sel, &sel, &callSuper); + if (target == nil) return false; + @try { + if (callSuper) { + struct objc_super sup = {target, class_getSuperclass(object_getClass(target))}; + ((void (*)(struct objc_super*, SEL))objc_msgSendSuper)(&sup, sel); + } else { + ((void (*)(id, SEL))objc_msgSend)(target, sel); + } + } @catch (NSException* e) { + __ns_aot_throw_exception(info, e); + return true; + } + return true; +} + +static bool AOT_NSArray_indexOfObject(NSAOTCallInfo info) { + if (__ns_aot_arg_count(info) != 1) return false; + SEL sel = @selector(indexOfObject:); + bool callSuper = false; + id target = __ns_aot_get_target(info, sel, &sel, &callSuper); + if (target == nil) return false; + id a0 = nil; + if (!__ns_aot_arg_object(info, 0, &a0)) return true; + unsigned long r = 0; + @try { + if (callSuper) { + struct objc_super sup = {target, class_getSuperclass(object_getClass(target))}; + r = ((unsigned long (*)(struct objc_super*, SEL, id))objc_msgSendSuper)(&sup, sel, a0); + } else { + r = ((unsigned long (*)(id, SEL, id))objc_msgSend)(target, sel, a0); + } + } @catch (NSException* e) { + __ns_aot_throw_exception(info, e); + return true; + } + __ns_aot_return_uint64(info, (uint64_t)r); + return true; +} + +static bool AOT_NSDictionary_objectForKey(NSAOTCallInfo info) { + if (__ns_aot_arg_count(info) != 1) return false; + SEL sel = @selector(objectForKey:); + bool callSuper = false; + id target = __ns_aot_get_target(info, sel, &sel, &callSuper); + if (target == nil) return false; + id a0 = nil; + if (!__ns_aot_arg_object(info, 0, &a0)) return true; + id r = nil; + @try { + if (callSuper) { + struct objc_super sup = {target, class_getSuperclass(object_getClass(target))}; + r = ((id (*)(struct objc_super*, SEL, id))objc_msgSendSuper)(&sup, sel, a0); + } else { + r = ((id (*)(id, SEL, id))objc_msgSend)(target, sel, a0); + } + } @catch (NSException* e) { + __ns_aot_throw_exception(info, e); + return true; + } + __ns_aot_return_object(info, r, false, true); + return true; +} + +static bool AOT_NSString_length(NSAOTCallInfo info) { + if (__ns_aot_arg_count(info) != 0) return false; + SEL sel = @selector(length); + bool callSuper = false; + id target = __ns_aot_get_target(info, sel, &sel, &callSuper); + if (target == nil) return false; + unsigned long r = 0; + @try { + if (callSuper) { + struct objc_super sup = {target, class_getSuperclass(object_getClass(target))}; + r = ((unsigned long (*)(struct objc_super*, SEL))objc_msgSendSuper)(&sup, sel); + } else { + r = ((unsigned long (*)(id, SEL))objc_msgSend)(target, sel); + } + } @catch (NSException* e) { + __ns_aot_throw_exception(info, e); + return true; + } + __ns_aot_return_uint64(info, (uint64_t)r); + return true; +} + +static bool AOT_NSString_isEqualToString(NSAOTCallInfo info) { + if (__ns_aot_arg_count(info) != 1) return false; + SEL sel = @selector(isEqualToString:); + bool callSuper = false; + id target = __ns_aot_get_target(info, sel, &sel, &callSuper); + if (target == nil) return false; + id a0 = nil; + if (!__ns_aot_arg_object(info, 0, &a0)) return true; + BOOL r = NO; + @try { + if (callSuper) { + struct objc_super sup = {target, class_getSuperclass(object_getClass(target))}; + r = ((BOOL (*)(struct objc_super*, SEL, id))objc_msgSendSuper)(&sup, sel, a0); + } else { + r = ((BOOL (*)(id, SEL, id))objc_msgSend)(target, sel, a0); + } + } @catch (NSException* e) { + __ns_aot_throw_exception(info, e); + return true; + } + __ns_aot_return_bool(info, r); + return true; +} + +static bool AOT_NSNumber_numberWithInt_static(NSAOTCallInfo info) { + if (__ns_aot_arg_count(info) != 1) return false; + SEL sel = @selector(numberWithInt:); + Class cls = __ns_aot_get_static_class(info); + if (cls == nil) return false; + int64_t raw0 = 0; + if (!__ns_aot_arg_int64(info, 0, &raw0)) return true; + int a0 = (int)raw0; + id r = nil; + @try { + r = ((id (*)(id, SEL, int))objc_msgSend)((id)cls, sel, a0); + } @catch (NSException* e) { + __ns_aot_throw_exception(info, e); + return true; + } + __ns_aot_return_object(info, r, false, true); + return true; +} + +static bool AOT_NSNumber_numberWithDouble_static(NSAOTCallInfo info) { + if (__ns_aot_arg_count(info) != 1) return false; + SEL sel = @selector(numberWithDouble:); + Class cls = __ns_aot_get_static_class(info); + if (cls == nil) return false; + double raw0 = 0; + if (!__ns_aot_arg_double(info, 0, &raw0)) return true; + double a0 = (double)raw0; + id r = nil; + @try { + r = ((id (*)(id, SEL, double))objc_msgSend)((id)cls, sel, a0); + } @catch (NSException* e) { + __ns_aot_throw_exception(info, e); + return true; + } + __ns_aot_return_object(info, r, false, true); + return true; +} + +static bool AOT_NSNumber_doubleValue(NSAOTCallInfo info) { + if (__ns_aot_arg_count(info) != 0) return false; + SEL sel = @selector(doubleValue); + bool callSuper = false; + id target = __ns_aot_get_target(info, sel, &sel, &callSuper); + if (target == nil) return false; + double r = 0; + @try { + if (callSuper) { + struct objc_super sup = {target, class_getSuperclass(object_getClass(target))}; + r = ((double (*)(struct objc_super*, SEL))objc_msgSendSuper)(&sup, sel); + } else { + r = ((double (*)(id, SEL))objc_msgSend)(target, sel); + } + } @catch (NSException* e) { + __ns_aot_throw_exception(info, e); + return true; + } + __ns_aot_return_double(info, r); + return true; +} + +static bool AOT_NSProcessInfo_processInfo_static(NSAOTCallInfo info) { + if (__ns_aot_arg_count(info) != 0) return false; + SEL sel = @selector(processInfo); + Class cls = __ns_aot_get_static_class(info); + if (cls == nil) return false; + id r = nil; + @try { + r = ((id (*)(id, SEL))objc_msgSend)((id)cls, sel); + } @catch (NSException* e) { + __ns_aot_throw_exception(info, e); + return true; + } + __ns_aot_return_object(info, r, false, true); + return true; +} + +static bool AOT_UIView_setNeedsLayout(NSAOTCallInfo info) { + if (__ns_aot_arg_count(info) != 0) return false; + SEL sel = @selector(setNeedsLayout); + bool callSuper = false; + id target = __ns_aot_get_target(info, sel, &sel, &callSuper); + if (target == nil) return false; + @try { + if (callSuper) { + struct objc_super sup = {target, class_getSuperclass(object_getClass(target))}; + ((void (*)(struct objc_super*, SEL))objc_msgSendSuper)(&sup, sel); + } else { + ((void (*)(id, SEL))objc_msgSend)(target, sel); + } + } @catch (NSException* e) { + __ns_aot_throw_exception(info, e); + return true; + } + return true; +} + +static bool AOT_UIColor_colorWithRed_green_blue_alpha_static(NSAOTCallInfo info) { + if (__ns_aot_arg_count(info) != 4) return false; + SEL sel = @selector(colorWithRed:green:blue:alpha:); + Class cls = __ns_aot_get_static_class(info); + if (cls == nil) return false; + double raw0 = 0; + if (!__ns_aot_arg_double(info, 0, &raw0)) return true; + double a0 = (double)raw0; + double raw1 = 0; + if (!__ns_aot_arg_double(info, 1, &raw1)) return true; + double a1 = (double)raw1; + double raw2 = 0; + if (!__ns_aot_arg_double(info, 2, &raw2)) return true; + double a2 = (double)raw2; + double raw3 = 0; + if (!__ns_aot_arg_double(info, 3, &raw3)) return true; + double a3 = (double)raw3; + id r = nil; + @try { + r = ((id (*)(id, SEL, double, double, double, double))objc_msgSend)((id)cls, sel, a0, a1, a2, a3); + } @catch (NSException* e) { + __ns_aot_throw_exception(info, e); + return true; + } + __ns_aot_return_object(info, r, false, true); + return true; +} + +static bool AOT_TNSPrimitives_methodWithInt_static(NSAOTCallInfo info) { + if (__ns_aot_arg_count(info) != 1) return false; + SEL sel = @selector(methodWithInt:); + Class cls = __ns_aot_get_static_class(info); + if (cls == nil) return false; + int64_t raw0 = 0; + if (!__ns_aot_arg_int64(info, 0, &raw0)) return true; + int a0 = (int)raw0; + int r = 0; + @try { + r = ((int (*)(id, SEL, int))objc_msgSend)((id)cls, sel, a0); + } @catch (NSException* e) { + __ns_aot_throw_exception(info, e); + return true; + } + __ns_aot_return_int64(info, (int64_t)r); + return true; +} + +__attribute__((visibility("default"))) +void __ns_register_aot_calls(void (*reg)(const char*, const char*, bool, NSAOTCallHandler)) { + reg("TNSBaseInterface", "baseMethod", false, AOT_TNSBaseInterface_baseMethod); + reg("TNSBaseInterface", "baseMethod", true, AOT_TNSBaseInterface_baseMethod_static); + reg("TNSBaseInterface", "methodWithParam:", false, AOT_TNSBaseInterface_methodWithParam); + reg("TNSDerivedInterface", "methodWithParam:", false, AOT_TNSBaseInterface_methodWithParam); + reg("TNSDerivedInterface", "methodWith:param:", false, AOT_TNSDerivedInterface_methodWith_param); + reg("TNSBaseInterface", "callBaseMethod:", false, AOT_TNSBaseInterface_callBaseMethod); + reg("TNSBaseInterface", "baseProperty", false, AOT_TNSBaseInterface_baseProperty); + reg("TNSBaseInterface", "setBaseProperty:", false, AOT_TNSBaseInterface_setBaseProperty); + reg("TNSBaseInterface", "baseProperty", true, AOT_TNSBaseInterface_baseProperty_static); + reg("TNSApi", "customGetter", false, AOT_TNSApi_customGetter); + reg("TNSApi", "methodThrowsException", false, AOT_TNSApi_methodThrowsException); + reg("TNSSwizzleKlass", "instanceMethod:", false, AOT_TNSSwizzleKlass_instanceMethod); + reg("TNSSwizzleKlass", "staticMethod:", true, AOT_TNSSwizzleKlass_staticMethod_static); + reg("TNSPrimitives", "methodWithChar:", false, AOT_TNSPrimitives_methodWithChar); + reg("TNSPrimitives", "methodWithUChar:", false, AOT_TNSPrimitives_methodWithUChar); + reg("TNSPrimitives", "methodWithInt:", false, AOT_TNSPrimitives_methodWithInt); + reg("TNSPrimitives", "methodWithFloat:", false, AOT_TNSPrimitives_methodWithFloat); + reg("TNSPrimitives", "methodWithDouble:", true, AOT_TNSPrimitives_methodWithDouble_static); + reg("TNSPrimitives", "methodWithBool2:", false, AOT_TNSPrimitives_methodWithBool2); + reg("TNSPrimitives", "methodWithBool3:", false, AOT_TNSPrimitives_methodWithBool3); + reg("TNSPrimitives", "methodWithSelector:", false, AOT_TNSPrimitives_methodWithSelector); + reg("TNSPrimitives", "methodWithClass:", false, AOT_TNSPrimitives_methodWithClass); + reg("TNSPrimitives", "methodWithNSNumber:", false, AOT_TNSPrimitives_methodWithNSNumber); + reg("TNSPrimitives", "methodWithId:", true, AOT_TNSPrimitives_methodWithId_static); + reg("TNSObjCTypes", "methodWithNSDate:", false, AOT_TNSObjCTypes_methodWithNSDate); + reg("TNSTestNativeCallbacks", "recordsSimpleStruct:", true, AOT_TNSTestNativeCallbacks_recordsSimpleStruct_static); + reg("TNSTestNativeCallbacks", "recordsNestedStruct:", true, AOT_TNSTestNativeCallbacks_recordsNestedStruct_static); + reg("TNSTestNativeCallbacks", "recordsStruct32:", true, AOT_TNSTestNativeCallbacks_recordsStruct32_static); + reg("TNSTestNativeCallbacks", "recordsComplexStruct:", true, AOT_TNSTestNativeCallbacks_recordsComplexStruct_static); + reg("RectClass", "getRect", false, AOT_RectClass_getRect); + reg("UIScreen", "mainScreen", true, AOT_UIScreen_mainScreen_static); + reg("UIScreen", "scale", false, AOT_UIScreen_scale); + reg("UIScreen", "nativeScale", false, AOT_UIScreen_nativeScale); + reg("UIScreen", "bounds", false, AOT_UIScreen_bounds); + reg("NSObject", "description", false, AOT_NSObject_description); + reg("NSObject", "respondsToSelector:", false, AOT_NSObject_respondsToSelector); + reg("NSObject", "isKindOfClass:", false, AOT_NSObject_isKindOfClass); + reg("NSObject", "isEqual:", false, AOT_NSObject_isEqual); + reg("NSArray", "count", false, AOT_NSArray_count); + reg("NSMutableArray", "count", false, AOT_NSArray_count); + reg("NSArray", "objectAtIndex:", false, AOT_NSArray_objectAtIndex); + reg("NSMutableArray", "objectAtIndex:", false, AOT_NSArray_objectAtIndex); + reg("NSMutableArray", "addObject:", false, AOT_NSMutableArray_addObject); + reg("NSMutableArray", "removeLastObject", false, AOT_NSMutableArray_removeLastObject); + reg("NSArray", "indexOfObject:", false, AOT_NSArray_indexOfObject); + reg("NSMutableArray", "indexOfObject:", false, AOT_NSArray_indexOfObject); + reg("NSDictionary", "objectForKey:", false, AOT_NSDictionary_objectForKey); + reg("NSMutableDictionary", "objectForKey:", false, AOT_NSDictionary_objectForKey); + reg("NSString", "length", false, AOT_NSString_length); + reg("NSString", "isEqualToString:", false, AOT_NSString_isEqualToString); + reg("NSNumber", "numberWithInt:", true, AOT_NSNumber_numberWithInt_static); + reg("NSNumber", "numberWithDouble:", true, AOT_NSNumber_numberWithDouble_static); + reg("NSNumber", "doubleValue", false, AOT_NSNumber_doubleValue); + reg("NSProcessInfo", "processInfo", true, AOT_NSProcessInfo_processInfo_static); + reg("UIView", "setNeedsLayout", false, AOT_UIView_setNeedsLayout); + reg("UIColor", "colorWithRed:green:blue:alpha:", true, AOT_UIColor_colorWithRed_green_blue_alpha_static); + reg("TNSPrimitives", "methodWithInt:", true, AOT_TNSPrimitives_methodWithInt_static); +} diff --git a/TestRunner/AOT/README.md b/TestRunner/AOT/README.md new file mode 100644 index 000000000..8c1afdb67 --- /dev/null +++ b/TestRunner/AOT/README.md @@ -0,0 +1,13 @@ +# TestRunner AOT stubs + +`NativeScriptAOTStubs.m` is generated from `aot-config.json` and compiled into the TestRunner executable, where the runtime finds `__ns_register_aot_calls` with `dlsym` (the symbol is listed in `TestFixtures/exported-symbols.txt`). `AOTDirectCallsTests.js` exercises it. + +Regenerate after changing the config, the fixtures or the generator: + +```sh +python3 scripts/generate-aot.py TestRunner/AOT/aot-config.json -m /arm64 -o TestRunner/AOT/NativeScriptAOTStubs.m --report +``` + +`` is the directory the metadata generator writes when the TestRunner is built with `NS_JSON_METADATA_PATH=` set (see `metadata-generator/build-step-metadata-generator.py`). + +The config's trailing entries are deliberately unsupported (initializer, NSError out-parameter, variadic, block, typed pointer, unknown selector or class) so the generator's skip paths stay covered. diff --git a/TestRunner/AOT/aot-config.json b/TestRunner/AOT/aot-config.json new file mode 100644 index 000000000..9832ca9c7 --- /dev/null +++ b/TestRunner/AOT/aot-config.json @@ -0,0 +1,69 @@ +{ + "methods": [ + {"class": "TNSBaseInterface", "selector": "baseMethod"}, + {"class": "TNSBaseInterface", "selector": "baseMethod", "static": true}, + {"class": "TNSDerivedInterface", "selector": "methodWithParam:"}, + {"class": "TNSDerivedInterface", "selector": "methodWith:param:"}, + {"class": "TNSBaseInterface", "selector": "callBaseMethod:"}, + {"class": "TNSBaseInterface", "selector": "baseProperty"}, + {"class": "TNSBaseInterface", "selector": "setBaseProperty:"}, + {"class": "TNSBaseInterface", "selector": "baseProperty", "static": true}, + {"class": "TNSApi", "selector": "customGetter"}, + {"class": "TNSApi", "selector": "methodThrowsException"}, + {"class": "TNSSwizzleKlass", "selector": "instanceMethod:"}, + {"class": "TNSSwizzleKlass", "selector": "staticMethod:", "static": true}, + {"class": "TNSPrimitives", "selector": "methodWithChar:"}, + {"class": "TNSPrimitives", "selector": "methodWithUChar:"}, + {"class": "TNSPrimitives", "selector": "methodWithInt:"}, + {"class": "TNSPrimitives", "selector": "methodWithULongLong:"}, + {"class": "TNSPrimitives", "selector": "methodWithLongLong:", "static": true}, + {"class": "TNSPrimitives", "selector": "methodWithFloat:"}, + {"class": "TNSPrimitives", "selector": "methodWithDouble:", "static": true}, + {"class": "TNSPrimitives", "selector": "methodWithBool2:"}, + {"class": "TNSPrimitives", "selector": "methodWithBool3:"}, + {"class": "TNSPrimitives", "selector": "methodWithSelector:"}, + {"class": "TNSPrimitives", "selector": "methodWithClass:"}, + {"class": "TNSPrimitives", "selector": "methodWithNSNumber:"}, + {"class": "TNSPrimitives", "selector": "methodWithId:", "static": true}, + {"class": "TNSObjCTypes", "selector": "methodWithNSDate:"}, + {"class": "TNSTestNativeCallbacks", "selector": "recordsSimpleStruct:", "static": true}, + {"class": "TNSTestNativeCallbacks", "selector": "recordsNestedStruct:", "static": true}, + {"class": "TNSTestNativeCallbacks", "selector": "recordsStruct32:", "static": true}, + {"class": "TNSTestNativeCallbacks", "selector": "recordsComplexStruct:", "static": true}, + {"class": "RectClass", "selector": "getRect"}, + + {"class": "UIScreen", "selector": "mainScreen", "static": true}, + {"class": "UIScreen", "selector": "scale"}, + {"class": "UIScreen", "selector": "nativeScale"}, + {"class": "UIScreen", "selector": "bounds"}, + {"class": "NSObject", "selector": "description"}, + {"class": "NSObject", "selector": "respondsToSelector:"}, + {"class": "NSObject", "selector": "isKindOfClass:"}, + {"class": "NSObject", "selector": "isEqual:"}, + {"class": "NSMutableArray", "selector": "count"}, + {"class": "NSMutableArray", "selector": "objectAtIndex:"}, + {"class": "NSMutableArray", "selector": "addObject:"}, + {"class": "NSMutableArray", "selector": "removeLastObject"}, + {"class": "NSMutableArray", "selector": "indexOfObject:"}, + {"class": "NSMutableDictionary", "selector": "objectForKey:"}, + {"class": "NSString", "selector": "length"}, + {"class": "NSString", "selector": "isEqualToString:"}, + {"class": "NSNumber", "selector": "numberWithInt:", "static": true}, + {"class": "NSNumber", "selector": "numberWithDouble:", "static": true}, + {"class": "NSNumber", "selector": "doubleValue"}, + {"class": "NSProcessInfo", "selector": "processInfo", "static": true}, + {"class": "UIView", "selector": "setNeedsLayout"}, + {"class": "UIColor", "selector": "colorWithRed:green:blue:alpha:", "static": true}, + {"class": "TNSPrimitives", "selector": "methodWithInt:", "static": true}, + + {"class": "TNSBaseInterface", "selector": "initBaseMethod"}, + {"class": "TNSApi", "selector": "method:error:"}, + {"class": "NSArray", "selector": "arrayWithObjects:", "static": true}, + {"class": "TNSObjCTypes", "selector": "methodWithSimpleBlock:"}, + {"class": "TNSObjCTypes", "selector": "methodWithLongLongOutParameter:"}, + {"class": "TNSPrimitives", "selector": "methodWithUnichar:"}, + {"class": "TNSPrimitives", "selector": "methodWithProtocol:"}, + {"class": "TNSPrimitives", "selector": "noSuchSelector"}, + {"class": "TNSNoSuchClass", "selector": "foo"} + ] +} diff --git a/TestRunner/app/tests/AOTDirectCallsTests.js b/TestRunner/app/tests/AOTDirectCallsTests.js new file mode 100644 index 000000000..6cb7af8a2 --- /dev/null +++ b/TestRunner/app/tests/AOTDirectCallsTests.js @@ -0,0 +1,378 @@ +// Stubs come from TestRunner/AOT/aot-config.json. Members asserted as generic +// here must stay out of that config. +describe(module.id, function () { + function stats() { + return __native_call_profiler.aotStats(); + } + + function measure(fn) { + var before = stats(); + var value, error; + try { + value = fn(); + } catch (e) { + error = e; + } + var after = stats(); + return { + value: value, + error: error, + served: after.served - before.served, + declined: after.declined - before.declined + }; + } + + // The counters only move while the profiler runs. + beforeEach(function () { + __native_call_profiler.start(); + }); + + afterEach(function () { + __native_call_profiler.stop(); + TNSClearOutput(); + }); + + describe("aotStats", function () { + it("returns served and declined counters", function () { + var r = measure(function () { return TNSPrimitives.methodWithDouble(1); }); + expect(r.served).toBe(1); + var s = stats(); + expect(Object.keys(s).sort()).toEqual(["declined", "served"]); + expect(Number.isInteger(s.served)).toBe(true); + expect(Number.isInteger(s.declined)).toBe(true); + expect(s.served).toBeGreaterThan(0); + }); + + it("does not count itself", function () { + var r = measure(function () { return stats(); }); + expect(r.served).toBe(0); + expect(r.declined).toBe(0); + }); + }); + + describe("members", function () { + it("instance method", function () { + var instance = TNSBaseInterface.alloc().init(); + var r = measure(function () { instance.callBaseMethod(false); }); + expect(r.served).toBe(1); + expect(TNSGetOutput()).toBe("instance baseMethod called"); + }); + + it("inherited method on a derived instance", function () { + var instance = TNSDerivedInterface.alloc().init(); + var r = measure(function () { instance.callBaseMethod(true); }); + expect(r.served).toBe(1); + expect(TNSGetOutput()).toBe("derived overloaded instance baseMethod: called"); + }); + + it("NSObject members on a fixture instance", function () { + var instance = TNSPrimitives.alloc().init(); + var r = measure(function () { + return [ + instance.description, + instance.isKindOfClass(NSObject), + instance.isKindOfClass(NSString), + instance.respondsToSelector("methodWithInt:"), + instance.respondsToSelector("noSuchSelector") + ]; + }); + expect(r.served).toBe(5); + expect(r.value[0]).toMatch(/^$/); + expect(r.value.slice(1)).toEqual([true, false, true, false]); + }); + + it("ancestor stub binds where a protocol re-registers the member", function () { + var screen = UIScreen.mainScreen; + var r = measure(function () { return screen.respondsToSelector("scale"); }); + expect(r.served).toBe(1); + expect(r.value).toBe(true); + }); + + it("NSObject member on a derived fixture instance", function () { + var instance = TNSDerivedInterface.alloc().init(); + var r = measure(function () { return instance.description; }); + expect(r.served).toBe(1); + expect(r.value).toMatch(/^$/); + }); + + it("static method", function () { + var r = measure(function () { return TNSPrimitives.methodWithDouble(1.7976931348623157e+308); }); + expect(r.served).toBe(1); + expect(r.value).toBe(1.7976931348623157e+308); + }); + + it("instance property getter", function () { + var instance = TNSBaseInterface.alloc().init(); + var r = measure(function () { + instance.baseProperty = 1; + return instance.baseProperty; + }); + expect(r.served).toBe(1); + expect(r.value).toBe(0); + expect(TNSGetOutput()).toBe("instance setBaseProperty: calledinstance baseProperty called"); + }); + + it("custom-named property getter", function () { + var object = new TNSApi(); + object.property = 3; + var r = measure(function () { return object.property; }); + expect(r.served).toBe(1); + expect(r.value).toBe(3); + }); + + it("static property getter", function () { + var r = measure(function () { return TNSBaseInterface.baseProperty; }); + expect(r.served).toBe(1); + expect(r.value).toBe(0); + expect(TNSGetOutput()).toBe("static baseProperty called"); + }); + + it("static property getter through a derived constructor", function () { + var r = measure(function () { return TNSDerivedInterface.baseProperty; }); + expect(r.served).toBe(1); + expect(TNSGetOutput()).toBe("static baseProperty called"); + }); + + it("UIScreen class property and instance getters", function () { + var r = measure(function () { + var screen = UIScreen.mainScreen; + return { screen: screen, scale: screen.scale, nativeScale: screen.nativeScale, bounds: screen.bounds }; + }); + expect(r.served).toBe(4); + expect(r.value.screen instanceof UIScreen).toBe(true); + expect(r.value.scale).toBeGreaterThan(0); + expect(r.value.nativeScale).toBeGreaterThan(0); + expect(r.value.bounds.size.width).toBeGreaterThan(0); + expect(r.value.bounds.size.height).toBeGreaterThan(0); + }); + }); + + describe("structs", function () { + it("argument and return round trip from a literal", function () { + var r = measure(function () { return TNSTestNativeCallbacks.recordsSimpleStruct({ x: 7, y: 8 }); }); + expect(r.served).toBe(1); + expect(TNSGetOutput()).toBe("7 8"); + expect(r.value.x).toBe(7); + expect(r.value.y).toBe(8); + }); + + it("argument from a struct wrapper", function () { + var record = new TNSSimpleStruct(); + record.x = 7; + record.y = 8; + var r = measure(function () { return TNSTestNativeCallbacks.recordsSimpleStruct(record); }); + expect(r.served).toBe(1); + expect(TNSGetOutput()).toBe("7 8"); + expect(r.value.x).toBe(7); + expect(r.value.y).toBe(8); + }); + + it("nested struct round trip", function () { + var record = new TNSNestedStruct({ a: { x: 1, y: 2 }, b: { x: 3, y: 4 } }); + var r = measure(function () { return TNSTestNativeCallbacks.recordsNestedStruct(record); }); + expect(r.served).toBe(1); + expect(TNSGetOutput()).toBe("1 2 3 4"); + expect(r.value.b.y).toBe(4); + }); + + it("struct return from a protocol method", function () { + var object = RectClass.new(); + var r = measure(function () { return object.getRect(); }); + expect(r.served).toBe(1); + expect(r.value.origin.x).toBe(1); + expect(r.value.origin.y).toBe(2); + expect(r.value.size.width).toBe(3); + expect(r.value.size.height).toBe(4); + }); + }); + + describe("arguments", function () { + var primitives; + beforeEach(function () { + primitives = TNSPrimitives.alloc().init(); + }); + + it("object", function () { + var r = measure(function () { return primitives.methodWithNSNumber(0); }); + expect(r.served).toBe(1); + expect(r.value).toBe(0); + expect(TNSGetOutput()).toBe("0"); + }); + + it("object returned as the same wrapper", function () { + var object = NSObject.new(); + var r = measure(function () { return TNSPrimitives.methodWithId(object); }); + expect(r.served).toBe(1); + expect(r.value).toBe(object); + }); + + it("NSDate", function () { + var objcTypes = TNSObjCTypes.alloc().init(); + var r = measure(function () { return objcTypes.methodWithNSDate(new Date(1e12)); }); + expect(r.served).toBe(1); + expect(r.value).toEqual(new Date(1e12)); + }); + + it("SEL", function () { + var r = measure(function () { return primitives.methodWithSelector("init"); }); + expect(r.served).toBe(1); + expect(r.value).toBe("init"); + expect(TNSGetOutput()).toBe("init"); + }); + + it("Class", function () { + var r = measure(function () { return primitives.methodWithClass(NSObject); }); + expect(r.served).toBe(1); + expect(r.value).toBe(NSObject); + expect(TNSGetOutput()).toBe("NSObject"); + }); + + it("integers", function () { + var r = measure(function () { + return [primitives.methodWithChar(-128), primitives.methodWithUChar(255), + primitives.methodWithInt(-2147483648)]; + }); + expect(r.served).toBe(3); + expect(r.value).toEqual([-128, 255, -2147483648]); + expect(TNSGetOutput()).toBe("-128255-2147483648"); + }); + + it("float", function () { + var r = measure(function () { return primitives.methodWithFloat(3.40282347e+38); }); + expect(r.served).toBe(1); + expect(r.value).toBe(3.4028234663852886e+38); + }); + + it("booleans", function () { + var r = measure(function () { + return [primitives.methodWithBool2(true), primitives.methodWithBool3(false)]; + }); + expect(r.served).toBe(2); + expect(r.value).toEqual([true, false]); + expect(TNSGetOutput()).toBe("10"); + }); + }); + + describe("exceptions", function () { + it("NSException matches the generic path", function () { + var api = TNSApi.alloc().init(); + var stubbed = measure(function () { api.methodThrowsException(); }); + var generic = measure(function () { TNSApi.methodThrowsException(); }); + + expect(stubbed.served).toBe(1); + expect(generic.served + generic.declined).toBe(0); + + expect(stubbed.error instanceof Error).toBe(true); + expect(generic.error instanceof Error).toBe(true); + expect(stubbed.error.name).toBe("NSGenericException"); + expect(stubbed.error.message).toBe("No reason"); + expect(stubbed.error.name).toBe(generic.error.name); + expect(stubbed.error.message).toBe(generic.error.message); + expect(Object.keys(stubbed.error).sort()).toEqual(Object.keys(generic.error).sort()); + + var nativeException = stubbed.error.nativeException; + expect(nativeException instanceof NSException).toBe(true); + expect(nativeException.name).toBe("NSGenericException"); + expect(nativeException.reason).toBe("No reason"); + }); + }); + + describe("JS-extended classes", function () { + it("override calling super reaches the native method once", function () { + var overrideCalls = 0; + var JSBase = TNSBaseInterface.extend({ + callBaseMethod: function (withArgs) { + overrideCalls++; + return TNSBaseInterface.prototype.callBaseMethod.call(this, withArgs); + } + }); + var instance = JSBase.alloc().init(); + var r = measure(function () { instance.callBaseMethod(false); }); + expect(r.error).toBeUndefined(); + expect(overrideCalls).toBe(1); + expect(r.served).toBe(1); + expect(TNSGetOutput()).toBe("instance baseMethod called"); + }); + + it("getter override calling the base getter", function () { + var JSApi = TNSApi.extend({ + get property() { + return -Object.getOwnPropertyDescriptor(TNSApi.prototype, "property").get.call(this); + }, + set property(x) { + Object.getOwnPropertyDescriptor(TNSApi.prototype, "property").set.call(this, x * 2); + } + }); + var object = new JSApi(); + object.property = 3; + var r = measure(function () { return object.property; }); + expect(r.served).toBe(1); + expect(r.value).toBe(-6); + }); + + it("non-overridden method on an extended instance", function () { + var instance = TNSPrimitives.extend({}).alloc().init(); + var r = measure(function () { return instance.methodWithInt(5); }); + expect(r.served).toBe(1); + expect(r.value).toBe(5); + }); + }); + + describe("declines", function () { + it("missing argument throws the generic error", function () { + var primitives = TNSPrimitives.alloc().init(); + var stubbed = measure(function () { return primitives.methodWithInt(); }); + var generic = measure(function () { return primitives.methodWithShort(); }); + expect(stubbed.declined).toBe(1); + expect(stubbed.served).toBe(0); + expect(generic.served + generic.declined).toBe(0); + expect(stubbed.error).toBeDefined(); + expect(String(stubbed.error)).toBe(String(generic.error)); + }); + + it("extra argument throws the generic error", function () { + var primitives = TNSPrimitives.alloc().init(); + var stubbed = measure(function () { return primitives.methodWithInt(1, 2); }); + var generic = measure(function () { return primitives.methodWithShort(1, 2); }); + expect(stubbed.declined).toBe(1); + expect(stubbed.error).toBeDefined(); + expect(String(stubbed.error)).toBe(String(generic.error)); + }); + + it("overload with another argument count resolves on the generic path", function () { + var instance = TNSDerivedInterface.alloc().init(); + var oneArg = measure(function () { instance.methodWithParam(1); }); + expect(oneArg.declined).toBe(1); + expect(oneArg.served).toBe(0); + expect(TNSGetOutput()).toBe("instance methodWithParam: called with 1 param"); + + TNSClearOutput(); + var twoArgs = measure(function () { instance.methodWithParam(1, 2); }); + expect(twoArgs.served).toBe(1); + expect(TNSGetOutput()).toBe("instance methodWith:param: called with 2 params"); + }); + + it("getter with a non-native receiver returns undefined", function () { + var stubbedGet = Object.getOwnPropertyDescriptor(TNSApi.prototype, "property").get; + var genericGet = Object.getOwnPropertyDescriptor(TNSBaseInterface.prototype, "baseReadOnlyProperty").get; + [{}, 5].forEach(function (receiver) { + var stubbed = measure(function () { return stubbedGet.call(receiver); }); + var generic = measure(function () { return genericGet.call(receiver); }); + expect(stubbed.declined).toBe(1); + expect(stubbed.error).toBeUndefined(); + expect(stubbed.value).toBeUndefined(); + expect(generic.error).toBeUndefined(); + expect(generic.value).toBeUndefined(); + }); + }); + + it("static method with a non-class receiver", function () { + var method = TNSPrimitives.methodWithDouble; + var r = measure(function () { return method.call({}, 1.5); }); + expect(r.declined).toBe(1); + expect(r.served).toBe(0); + expect(r.error).toBeUndefined(); + expect(r.value).toBe(1.5); + }); + }); +}); diff --git a/TestRunner/app/tests/index.js b/TestRunner/app/tests/index.js index 31bfe10a5..5b9c6ee09 100644 --- a/TestRunner/app/tests/index.js +++ b/TestRunner/app/tests/index.js @@ -120,6 +120,7 @@ require("./Inheritance/ProtocolImplementationTests"); require("./Inheritance/TypeScriptTests"); // require("./MethodCallsTests"); +require("./AOTDirectCallsTests"); require("./StaleWrapperCacheTests"); //import "./FunctionsTests"; require("./VersionDiffTests"); diff --git a/metadata-generator/build-step-metadata-generator.py b/metadata-generator/build-step-metadata-generator.py index 1acd946c9..fdf51b1c4 100755 --- a/metadata-generator/build-step-metadata-generator.py +++ b/metadata-generator/build-step-metadata-generator.py @@ -113,6 +113,7 @@ def map_and_list(func, iterable): "Library/Developer/Shared/Documentation/DocSets/com.apple.adc.documentation.{}.docset" .format(docset_platform)) yaml_output_folder = env_or_none("NS_DEBUG_METADATA_PATH") or env_or_none("TNS_DEBUG_METADATA_PATH") +json_output_folder = env_or_none("NS_JSON_METADATA_PATH") strict_includes = env_or_none("NS_DEBUG_METADATA_STRICT_INCLUDES") or env_or_none("TNS_DEBUG_METADATA_STRICT_INCLUDES") @@ -146,6 +147,12 @@ def generate_metadata(arch): generator_call.extend(["-output-yaml", current_yaml_output_folder]) print("Generating debug metadata in: \"{}\"".format(current_yaml_output_folder)) + # optionally add json output folder + if json_output_folder is not None: + current_json_output_folder = os.path.join(json_output_folder, arch) + generator_call.extend(["-output-json", current_json_output_folder]) + print("Generating JSON metadata in: \"{}\"".format(current_json_output_folder)) + whitelist_file_name = os.path.join(src_root, "whitelist.mdg") if os.path.exists(whitelist_file_name): generator_call.extend(["--whitelist-modules-file", whitelist_file_name]) diff --git a/metadata-generator/src/CMakeLists.txt b/metadata-generator/src/CMakeLists.txt index 037abdc98..8fd309851 100644 --- a/metadata-generator/src/CMakeLists.txt +++ b/metadata-generator/src/CMakeLists.txt @@ -33,6 +33,7 @@ set(GENERATOR_HEADERS Utils/stream.h Utils/StringHasher.h Utils/StringUtils.h + Json/JsonSerializer.h Yaml/MetaYamlTraits.h Yaml/YamlSerializer.h ) diff --git a/metadata-generator/src/Json/JsonSerializer.h b/metadata-generator/src/Json/JsonSerializer.h new file mode 100644 index 000000000..7d31f2d4e --- /dev/null +++ b/metadata-generator/src/Json/JsonSerializer.h @@ -0,0 +1,511 @@ +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include "Meta/MetaEntities.h" +#include "Meta/Utils.h" + +namespace Json { + +static std::string typeTypeToString(Meta::TypeType t) { + switch (t) { + case Meta::TypeType::TypeVoid: + return "Void"; + case Meta::TypeType::TypeBool: + return "Bool"; + case Meta::TypeType::TypeShort: + return "Short"; + case Meta::TypeType::TypeUShort: + return "Ushort"; + case Meta::TypeType::TypeInt: + return "Int"; + case Meta::TypeType::TypeUInt: + return "UInt"; + case Meta::TypeType::TypeLong: + return "Long"; + case Meta::TypeType::TypeULong: + return "ULong"; + case Meta::TypeType::TypeLongLong: + return "LongLong"; + case Meta::TypeType::TypeULongLong: + return "ULongLong"; + case Meta::TypeType::TypeSignedChar: + return "Char"; + case Meta::TypeType::TypeUnsignedChar: + return "UChar"; + case Meta::TypeType::TypeUnichar: + return "Unichar"; + case Meta::TypeType::TypeCString: + return "CString"; + case Meta::TypeType::TypeFloat: + return "Float"; + case Meta::TypeType::TypeDouble: + return "Double"; + case Meta::TypeType::TypeSelector: + return "Selector"; + case Meta::TypeType::TypeClass: + return "Class"; + case Meta::TypeType::TypeInstancetype: + return "Instancetype"; + case Meta::TypeType::TypeId: + return "Id"; + case Meta::TypeType::TypeConstantArray: + return "ConstantArray"; + case Meta::TypeType::TypeIncompleteArray: + return "IncompleteArray"; + case Meta::TypeType::TypeInterface: + return "Interface"; + case Meta::TypeType::TypeBridgedInterface: + return "BridgedInterface"; + case Meta::TypeType::TypePointer: + return "Pointer"; + case Meta::TypeType::TypeFunctionPointer: + return "FunctionPointer"; + case Meta::TypeType::TypeBlock: + return "Block"; + case Meta::TypeType::TypeStruct: + return "Struct"; + case Meta::TypeType::TypeUnion: + return "Union"; + case Meta::TypeType::TypeAnonymousStruct: + return "AnonymousStruct"; + case Meta::TypeType::TypeAnonymousUnion: + return "AnonymousUnion"; + case Meta::TypeType::TypeEnum: + return "Enum"; + case Meta::TypeType::TypeVaList: + return "VaList"; + case Meta::TypeType::TypeProtocol: + return "Protocol"; + case Meta::TypeType::TypeTypeArgument: + return "TypeArgument"; + case Meta::TypeType::TypeExtVector: + return "ExtVector"; + case Meta::TypeType::TypeNullable: + return "Nullable"; + case Meta::TypeType::TypeNonNullable: + return "NonNullable"; + } + return "Unknown"; +} + +static std::string metaTypeToString(Meta::MetaType t) { + switch (t) { + case Meta::MetaType::Undefined: + return "Undefined"; + case Meta::MetaType::Struct: + return "Struct"; + case Meta::MetaType::Union: + return "Union"; + case Meta::MetaType::Function: + return "Function"; + case Meta::MetaType::Enum: + return "Enum"; + case Meta::MetaType::Var: + return "Var"; + case Meta::MetaType::Interface: + return "Interface"; + case Meta::MetaType::Protocol: + return "Protocol"; + case Meta::MetaType::Category: + return "Category"; + case Meta::MetaType::Method: + return "Method"; + case Meta::MetaType::Property: + return "Property"; + case Meta::MetaType::EnumConstant: + return "EnumConstant"; + } + return "Unknown"; +} + +static llvm::json::Object typeToJson(Meta::Type* type) { + llvm::json::Object obj; + obj["Type"] = typeTypeToString(type->getType()); + + switch (type->getType()) { + case Meta::TypeType::TypeId: { + auto& t = type->as(); + llvm::json::Array protocols; + for (auto* p : t.protocols) protocols.push_back(p->jsName); + obj["WithProtocols"] = std::move(protocols); + break; + } + case Meta::TypeType::TypeClass: { + auto& t = type->as(); + llvm::json::Array protocols; + for (auto* p : t.protocols) protocols.push_back(p->jsName); + if (!protocols.empty()) obj["WithProtocols"] = std::move(protocols); + break; + } + case Meta::TypeType::TypeConstantArray: { + auto& t = type->as(); + obj["ArrayType"] = typeToJson(t.innerType); + obj["Size"] = t.size; + break; + } + case Meta::TypeType::TypeIncompleteArray: { + auto& t = type->as(); + obj["ArrayType"] = typeToJson(t.innerType); + break; + } + case Meta::TypeType::TypeInterface: { + auto& t = type->as(); + obj["Name"] = t.interface->name; + if (!t.typeArguments.empty()) { + llvm::json::Array args; + for (auto* a : t.typeArguments) args.push_back(typeToJson(a)); + obj["TypeParameters"] = std::move(args); + } + llvm::json::Array protocols; + for (auto* p : t.protocols) protocols.push_back(p->jsName); + obj["WithProtocols"] = std::move(protocols); + break; + } + case Meta::TypeType::TypeBridgedInterface: { + auto& t = type->as(); + obj["Name"] = t.name; + std::string bridgedTo = t.isId() ? "id" + : (t.bridgedInterface == nullptr + ? "[None]" + : t.bridgedInterface->jsName); + obj["BridgedTo"] = bridgedTo; + break; + } + case Meta::TypeType::TypePointer: { + auto& t = type->as(); + obj["PointerType"] = typeToJson(t.innerType); + break; + } + case Meta::TypeType::TypeFunctionPointer: { + auto& t = type->as(); + llvm::json::Array sig; + for (auto* s : t.signature) sig.push_back(typeToJson(s)); + obj["Signature"] = std::move(sig); + break; + } + case Meta::TypeType::TypeBlock: { + auto& t = type->as(); + llvm::json::Array sig; + for (auto* s : t.signature) sig.push_back(typeToJson(s)); + obj["Signature"] = std::move(sig); + break; + } + case Meta::TypeType::TypeStruct: { + auto& t = type->as(); + obj["Module"] = t.structMeta->module->getFullModuleName(); + obj["Name"] = t.structMeta->name; + break; + } + case Meta::TypeType::TypeUnion: { + auto& t = type->as(); + obj["Module"] = t.unionMeta->module->getFullModuleName(); + obj["Name"] = t.unionMeta->name; + break; + } + case Meta::TypeType::TypeAnonymousStruct: { + auto& t = type->as(); + llvm::json::Array fields; + for (auto& f : t.fields) { + llvm::json::Object field; + field["Name"] = f.name; + field["Signature"] = typeToJson(f.encoding); + fields.push_back(std::move(field)); + } + obj["Fields"] = std::move(fields); + break; + } + case Meta::TypeType::TypeAnonymousUnion: { + auto& t = type->as(); + llvm::json::Array fields; + for (auto& f : t.fields) { + llvm::json::Object field; + field["Name"] = f.name; + field["Signature"] = typeToJson(f.encoding); + fields.push_back(std::move(field)); + } + obj["Fields"] = std::move(fields); + break; + } + case Meta::TypeType::TypeEnum: { + auto& t = type->as(); + obj["Name"] = t.enumMeta->jsName; + if (t.underlyingType != nullptr) + obj["UnderlyingType"] = typeToJson(t.underlyingType); + break; + } + case Meta::TypeType::TypeTypeArgument: { + auto& t = type->as(); + obj["Name"] = t.name; + obj["UnderlyingType"] = typeToJson(t.underlyingType); + llvm::json::Array protocols; + for (auto* p : t.protocols) protocols.push_back(p->jsName); + if (!protocols.empty()) obj["WithProtocols"] = std::move(protocols); + break; + } + case Meta::TypeType::TypeExtVector: { + auto& t = type->as(); + obj["InnerType"] = typeToJson(t.innerType); + obj["Size"] = t.size; + break; + } + case Meta::TypeType::TypeNullable: { + auto& t = type->as(); + obj["InnerType"] = typeToJson(t.innerType); + break; + } + case Meta::TypeType::TypeNonNullable: { + auto& t = type->as(); + obj["InnerType"] = typeToJson(t.innerType); + break; + } + default: + break; + } + return obj; +} + +static llvm::json::Array flagsToJson(Meta::MetaFlags flags) { + llvm::json::Array arr; + if (flags & Meta::MetaFlags::IsIosAppExtensionAvailable) + arr.push_back("IsIosAppExtensionAvailable"); + if (flags & Meta::MetaFlags::MemberIsOptional) + arr.push_back("MemberIsOptional"); + if (flags & Meta::MetaFlags::FunctionIsVariadic) + arr.push_back("FunctionIsVariadic"); + if (flags & Meta::MetaFlags::FunctionOwnsReturnedCocoaObject) + arr.push_back("FunctionOwnsReturnedCocoaObject"); + if (flags & Meta::MetaFlags::FunctionReturnsUnmanaged) + arr.push_back("FunctionReturnsUnmanaged"); + if (flags & Meta::MetaFlags::MethodIsVariadic) + arr.push_back("MethodIsVariadic"); + if (flags & Meta::MetaFlags::MethodIsNullTerminatedVariadic) + arr.push_back("MethodIsNullTerminatedVariadic"); + if (flags & Meta::MetaFlags::MethodOwnsReturnedCocoaObject) + arr.push_back("MethodOwnsReturnedCocoaObject"); + if (flags & Meta::MetaFlags::MethodHasErrorOutParameter) + arr.push_back("MethodHasErrorOutParameter"); + if (flags & Meta::MetaFlags::MethodIsInitializer) + arr.push_back("MethodIsInitializer"); + return arr; +} + +static void mapBaseMetaJson(llvm::json::Object& obj, Meta::Meta* meta) { + obj["Name"] = meta->name; + obj["JsName"] = meta->jsName; + if (!meta->demangledName.empty()) obj["DemangledName"] = meta->demangledName; + obj["Filename"] = meta->fileName; + obj["Module"] = meta->module ? meta->module->getFullModuleName() : ""; + if (!meta->introducedIn.isUnknown()) + obj["IntroducedIn"] = meta->introducedIn.toString(); + obj["Flags"] = flagsToJson(meta->flags); + obj["Type"] = metaTypeToString(meta->type); +} + +static llvm::json::Object methodToJson(Meta::MethodMeta* meta) { + llvm::json::Object obj; + mapBaseMetaJson(obj, meta); + llvm::json::Array sig; + for (auto* t : meta->signature) sig.push_back(typeToJson(t)); + obj["Signature"] = std::move(sig); + return obj; +} + +static llvm::json::Object propertyToJson(Meta::PropertyMeta* meta) { + llvm::json::Object obj; + mapBaseMetaJson(obj, meta); + if (meta->getter) obj["Getter"] = methodToJson(meta->getter); + if (meta->setter) obj["Setter"] = methodToJson(meta->setter); + return obj; +} + +static void mapBaseClassMetaJson(llvm::json::Object& obj, + Meta::BaseClassMeta* meta) { + mapBaseMetaJson(obj, meta); + + llvm::json::Array instanceMethods; + for (auto* m : meta->instanceMethods) + instanceMethods.push_back(methodToJson(m)); + obj["InstanceMethods"] = std::move(instanceMethods); + + llvm::json::Array staticMethods; + for (auto* m : meta->staticMethods) staticMethods.push_back(methodToJson(m)); + obj["StaticMethods"] = std::move(staticMethods); + + llvm::json::Array instanceProperties; + for (auto* p : meta->instanceProperties) + instanceProperties.push_back(propertyToJson(p)); + obj["InstanceProperties"] = std::move(instanceProperties); + + llvm::json::Array staticProperties; + for (auto* p : meta->staticProperties) + staticProperties.push_back(propertyToJson(p)); + obj["StaticProperties"] = std::move(staticProperties); + + llvm::json::Array protocols; + for (auto* p : meta->protocols) protocols.push_back(p->jsName); + obj["Protocols"] = std::move(protocols); +} + +static llvm::json::Object metaToJson(Meta::Meta* meta) { + llvm::json::Object obj; + + switch (meta->type) { + case Meta::MetaType::Function: { + auto& m = meta->as(); + mapBaseMetaJson(obj, meta); + llvm::json::Array sig; + for (auto* t : m.signature) sig.push_back(typeToJson(t)); + obj["Signature"] = std::move(sig); + break; + } + case Meta::MetaType::Struct: { + auto& m = meta->as(); + auto& rec = m.as(); + mapBaseMetaJson(obj, meta); + llvm::json::Array fields; + for (auto& f : rec.fields) { + llvm::json::Object field; + field["Name"] = f.name; + field["Signature"] = typeToJson(f.encoding); + fields.push_back(std::move(field)); + } + obj["Fields"] = std::move(fields); + break; + } + case Meta::MetaType::Union: { + auto& m = meta->as(); + auto& rec = m.as(); + mapBaseMetaJson(obj, meta); + llvm::json::Array fields; + for (auto& f : rec.fields) { + llvm::json::Object field; + field["Name"] = f.name; + field["Signature"] = typeToJson(f.encoding); + fields.push_back(std::move(field)); + } + obj["Fields"] = std::move(fields); + break; + } + case Meta::MetaType::Var: { + auto& m = meta->as(); + mapBaseMetaJson(obj, meta); + obj["Signature"] = typeToJson(m.signature); + if (m.hasValue) obj["Value"] = m.value; + break; + } + case Meta::MetaType::Enum: { + auto& m = meta->as(); + mapBaseMetaJson(obj, meta); + llvm::json::Array fullNameFields; + for (auto& f : m.fullNameFields) { + llvm::json::Object field; + field[f.name] = f.value; + fullNameFields.push_back(std::move(field)); + } + obj["FullNameFields"] = std::move(fullNameFields); + llvm::json::Array swiftNameFields; + for (auto& f : m.swiftNameFields) { + llvm::json::Object field; + field[f.name] = f.value; + swiftNameFields.push_back(std::move(field)); + } + obj["SwiftNameFields"] = std::move(swiftNameFields); + break; + } + case Meta::MetaType::EnumConstant: { + auto& m = meta->as(); + mapBaseMetaJson(obj, meta); + obj["Value"] = m.value; + break; + } + case Meta::MetaType::Interface: { + auto& m = meta->as(); + auto& bc = m.as(); + mapBaseClassMetaJson(obj, &bc); + if (m.base != nullptr) obj["Base"] = m.base->jsName; + break; + } + case Meta::MetaType::Protocol: { + auto& m = meta->as(); + auto& bc = m.as(); + mapBaseClassMetaJson(obj, &bc); + break; + } + case Meta::MetaType::Category: { + auto& m = meta->as(); + auto& bc = m.as(); + mapBaseClassMetaJson(obj, &bc); + if (m.extendedInterface) + obj["ExtendedInterface"] = m.extendedInterface->jsName; + break; + } + case Meta::MetaType::Method: { + auto& m = meta->as(); + obj = methodToJson(&m); + break; + } + case Meta::MetaType::Property: { + auto& m = meta->as(); + obj = propertyToJson(&m); + break; + } + default: + mapBaseMetaJson(obj, meta); + break; + } + return obj; +} + +static llvm::json::Object moduleToJson( + std::pair>& modulePair) { + llvm::json::Object root; + + llvm::json::Object moduleObj; + moduleObj["FullName"] = modulePair.first->getFullModuleName(); + moduleObj["IsPartOfFramework"] = modulePair.first->isPartOfFramework(); + moduleObj["IsSystemModule"] = modulePair.first->IsSystem; + std::vector libs; + Meta::Utils::getAllLinkLibraries(modulePair.first, libs); + llvm::json::Array libsArr; + for (auto& lib : libs) { + llvm::json::Object libObj; + libObj["Library"] = lib.Library; + libObj["IsFramework"] = lib.IsFramework; + libsArr.push_back(std::move(libObj)); + } + moduleObj["Libraries"] = std::move(libsArr); + root["Module"] = std::move(moduleObj); + + llvm::json::Array items; + for (auto* meta : modulePair.second) items.push_back(metaToJson(meta)); + root["Items"] = std::move(items); + + return root; +} + +class JsonSerializer { + public: + static void serialize( + const std::string& outputFilePath, + std::pair>& modulePair) { + std::error_code errorCode; + llvm::raw_fd_ostream fileStream(outputFilePath, errorCode, + llvm::sys::fs::OpenFlags::OF_None); + if (errorCode) + throw std::runtime_error(std::string("Unable to open file ") + + outputFilePath + "."); + + llvm::json::Object root = moduleToJson(modulePair); + fileStream << llvm::formatv("{0:2}", llvm::json::Value(std::move(root))); + fileStream.close(); + } +}; + +} // namespace Json diff --git a/metadata-generator/src/main.cpp b/metadata-generator/src/main.cpp index f4b7b8aee..cc969e282 100644 --- a/metadata-generator/src/main.cpp +++ b/metadata-generator/src/main.cpp @@ -1,5 +1,15 @@ +#include +#include +#include +#include +#include + +#include +#include + #include "Binary/binarySerializer.h" #include "HeadersParser/Parser.h" +#include "Json/JsonSerializer.h" #include "Meta/DeclarationConverterVisitor.h" #include "Meta/Filters/HandleExceptionalMetasFilter.h" #include "Meta/Filters/HandleMethodsAndPropertiesWithSameNameFilter.h" @@ -10,13 +20,6 @@ #include "TypeScript/DefinitionWriter.h" #include "TypeScript/DocSetManager.h" #include "Yaml/YamlSerializer.h" -#include -#include -#include -#include -#include -#include -#include // Command line parameters llvm::cl::opt cla_verbose("verbose", llvm::cl::desc("Set verbose output mode"), llvm::cl::value_desc("bool")); @@ -24,6 +27,9 @@ llvm::cl::opt cla_strictIncludes("strict-includes", llvm::cl::desc("Set llvm::cl::opt cla_outputUmbrellaHeaderFile("output-umbrella", llvm::cl::desc("Specify the output umbrella header file"), llvm::cl::value_desc("file_path")); llvm::cl::opt cla_inputUmbrellaHeaderFile("input-umbrella", llvm::cl::desc("Specify the input umbrella header file"), llvm::cl::value_desc("file_path")); llvm::cl::opt cla_outputYamlFolder("output-yaml", llvm::cl::desc("Specify the output yaml folder"), llvm::cl::value_desc("")); +llvm::cl::opt cla_outputJsonFolder( + "output-json", llvm::cl::desc("Specify the output json folder"), + llvm::cl::value_desc("")); llvm::cl::opt cla_outputModuleMapsFolder("output-modulemaps", llvm::cl::desc("Specify the fodler where modulemap files of all parsed modules will be dumped"), llvm::cl::value_desc("")); llvm::cl::opt cla_outputBinFile("output-bin", llvm::cl::desc("Specify the output binary metadata file"), llvm::cl::value_desc("")); llvm::cl::opt cla_outputDtsFolder("output-typescript", llvm::cl::desc("Specify the output .d.ts folder"), llvm::cl::value_desc("")); @@ -94,6 +100,18 @@ class MetaGenerationConsumer : public clang::ASTConsumer { } } + // Serialize Meta objects to JSON + if (!cla_outputJsonFolder.empty()) { + llvm::sys::fs::create_directories(cla_outputJsonFolder); + for (std::pair >& + modulePair : metasByModules) { + std::string jsonFileName = + modulePair.first->getFullModuleName() + ".json"; + Json::JsonSerializer::serialize( + cla_outputJsonFolder + "/" + jsonFileName, modulePair); + } + } + // Serialize Meta objects to binary metadata if (!cla_outputBinFile.empty()) { binary::MetaFile file(metaContainer.size() / 10); // Average number of hash collisions: 10 per bucket diff --git a/scripts/aot-config.example.json b/scripts/aot-config.example.json new file mode 100644 index 000000000..aff23a5b3 --- /dev/null +++ b/scripts/aot-config.example.json @@ -0,0 +1,13 @@ +{ + "methods": [ + {"class": "UIScreen", "selector": "scale"}, + {"class": "UIView", "selector": "frame"}, + {"class": "UIView", "selector": "setNeedsLayout"}, + {"class": "UIView", "selector": "addSubview:"}, + {"class": "UIView", "selector": "convertPoint:toView:"}, + {"class": "UILabel", "selector": "description"}, + {"class": "NSDictionary", "selector": "objectForKey:"}, + {"class": "UIColor", "selector": "colorWithRed:green:blue:alpha:", "static": true}, + {"class": "NSIndexPath", "selector": "indexPathForItem:inSection:", "static": true} + ] +} diff --git a/scripts/generate-aot.py b/scripts/generate-aot.py new file mode 100644 index 000000000..0b75f21c8 --- /dev/null +++ b/scripts/generate-aot.py @@ -0,0 +1,762 @@ +#!/usr/bin/env python3 +"""Generate NativeScript AOT call stubs. + +Reads an AOT config listing (class, selector) pairs and the metadata +generator's JSON output (one .json per module, from -output-json), +and writes one Objective-C file of stubs that send each message through a +cast objc_msgSend with the exact C ABI types from metadata. The stubs talk to +the runtime only through ; the file needs no +framework header because struct layouts are emitted from metadata. + +Config schema: + {"methods": [{"class": "UIScreen", "selector": "scale"}, + {"class": "UIColor", "selector": "colorWithRed:green:blue:alpha:", + "static": true}]} +Unknown keys are ignored. + +Usage: + generate-aot.py CONFIG --metadata DIR [--output FILE] [--report] +""" + +import argparse +import json +import os +import re +import sys + + +class InputError(Exception): + pass + + +class Unsupported(Exception): + def __init__(self, message, category): + super().__init__(message) + self.category = category + + +RECORD = "struct not representable" + + +# --------------------------------------------------------------------------- +# Metadata +# --------------------------------------------------------------------------- + + +class Metadata: + def __init__(self, directory): + if not os.path.isdir(directory): + raise InputError(f"metadata directory not found: {directory}") + self.classes = {} + self.protocols = {} + self.records = {} # (kind, module, name) -> item + self.records_by_name = {} # (kind, name) -> item + self.enums = {} + files = sorted(f for f in os.listdir(directory) if f.endswith(".json")) + if not files: + raise InputError(f"no .json files in {directory}") + for name in files: + path = os.path.join(directory, name) + try: + with open(path) as f: + data = json.load(f) + items = data["Items"] + except (ValueError, KeyError, TypeError) as e: + raise InputError(f"malformed metadata file {path}: {e}") + for item in items: + self._index(item) + + def _index(self, item): + kind = item.get("Type") + if kind in ("Interface", "Protocol"): + table = self.classes if kind == "Interface" else self.protocols + for key in ("Name", "JsName", "DemangledName"): + value = item.get(key) + if value: + table.setdefault((key, value), item) + elif kind in ("Struct", "Union"): + self.records.setdefault((kind, item.get("Module"), item["Name"]), item) + self.records_by_name.setdefault((kind, item["Name"]), item) + elif kind == "Enum": + self.enums.setdefault(item.get("JsName") or item["Name"], item) + + @staticmethod + def _lookup(table, name): + for key in ("Name", "JsName", "DemangledName"): + item = table.get((key, name)) + if item is not None: + return item + return None + + def find_class(self, name): + return self._lookup(self.classes, name) + + def find_protocol(self, name): + return self._lookup(self.protocols, name) + + def find_record(self, kind, module, name): + item = self.records.get((kind, module, name)) + if item is None: + item = self.records_by_name.get((kind, name)) + return item + + def find_method(self, class_name, selector, is_static): + """Returns (method, binding class name) or None. + + Searches the declaration's own methods and property accessors, then + its protocols, then its Base chain. The binding class is the one whose + template the runtime binds the member on: the declaring class, or for + a protocol member the class in the chain that adopts the protocol. A + protocol name is accepted as the starting point and binds itself. + """ + pending = [] + cls = self.find_class(class_name) + if cls is not None: + pending.append((cls, cls["Name"])) + proto = self.find_protocol(class_name) + if proto is not None: + pending.append((proto, cls["Name"] if cls is not None else proto["Name"])) + methods_key = "StaticMethods" if is_static else "InstanceMethods" + props_key = "StaticProperties" if is_static else "InstanceProperties" + seen = set() + while pending: + decl, binder = pending.pop(0) + ident = (decl.get("Type"), decl.get("Name")) + if ident in seen: + continue + seen.add(ident) + for method in decl.get(methods_key) or []: + if method.get("Name") == selector: + return method, binder + for prop in decl.get(props_key) or []: + for accessor in ("Getter", "Setter"): + method = prop.get(accessor) + if method and method.get("Name") == selector: + return method, binder + protocols = [self.find_protocol(p) for p in decl.get("Protocols") or []] + pending[0:0] = [(p, binder) for p in protocols if p is not None] + base = self.find_class(decl["Base"]) if decl.get("Base") else None + if base is not None: + pending.append((base, base["Name"])) + return None + + +# --------------------------------------------------------------------------- +# Types +# --------------------------------------------------------------------------- + +# metadata kind -> (C type, signed?) +INTEGERS = { + "Char": ("signed char", True), + "UChar": ("unsigned char", False), + "Short": ("short", True), + "Ushort": ("unsigned short", False), + "UShort": ("unsigned short", False), + "Int": ("int", True), + "UInt": ("unsigned int", False), + "Long": ("long", True), + "ULong": ("unsigned long", False), + "LongLong": ("long long", True), + "ULongLong": ("unsigned long long", False), +} + +OBJECT_KINDS = ("Id", "Instancetype", "Interface", "BridgedInterface") + + +class CType: + """A resolved parameter or return type. + + kind is one of void, bool, int, uint, float, double, sel, class, object, + pointer, struct. + """ + + def __init__(self, kind, ctype, record=None): + self.kind = kind + self.ctype = ctype + self.record = record + + +def describe(t): + if not isinstance(t, dict): + return repr(t) + kind = t.get("Type") + name = t.get("Name") + return f"{kind} {name}" if name else str(kind) + + +class TypeMapper: + def __init__(self, metadata): + self.md = metadata + self.records = {} # id(item) -> RecordDef + self.record_order = [] + self.vectors = {} # (ctype, size) -> typedef name + self.typedef_names = set() + + def unwrap(self, t): + while isinstance(t, dict): + kind = t.get("Type") + if kind in ("Nullable", "NonNullable"): + t = t.get("InnerType") + elif kind == "TypeArgument": + t = t.get("UnderlyingType") + elif kind == "Enum": + underlying = t.get("UnderlyingType") + if underlying is None: + item = self.md.enums.get(t.get("Name")) + underlying = item.get("UnderlyingType") if item else None + if underlying is None: + raise Unsupported(f"enum {t.get('Name')} has no underlying type in metadata", + "enum without underlying type in metadata") + t = underlying + else: + return t + raise InputError(f"malformed type {t!r}") + + def resolve(self, t, is_return): + t = self.unwrap(t) + kind = t.get("Type") + if kind == "Void": + if not is_return: + raise Unsupported("void parameter", "void parameter") + return CType("void", "void") + if kind == "Bool": + return CType("bool", "BOOL") + if kind in ("LongLong", "ULongLong"): + # The generic path converts these to and from BigInt beyond 2^53, + # which the bridge's 64-bit getters and setters do not model. + raise Unsupported(f"{kind} (BigInt marshalling)", "long long (BigInt marshalling)") + if kind in INTEGERS: + ctype, signed = INTEGERS[kind] + return CType("int" if signed else "uint", ctype) + if kind == "Float": + return CType("float", "float") + if kind == "Double": + return CType("double", "double") + if kind == "Selector": + return CType("sel", "SEL") + if kind == "Class": + return CType("class", "Class") + if kind in OBJECT_KINDS: + if kind == "BridgedInterface" and t.get("BridgedTo") == "[None]": + raise Unsupported(f"unbridged CF type {t.get('Name')}", "unbridged CF type") + return CType("object", "id") + if kind == "Pointer": + inner = self.unwrap(t.get("PointerType")) + if inner.get("Type") != "Void": + raise Unsupported(f"typed pointer to {describe(inner)} (runtime needs the pointee type)", + "typed pointer") + return CType("pointer", "void*") + if kind == "Struct": + record = self.record(t, "Struct") + return CType("struct", record.cname, record) + if kind == "Block": + what = "block " + ("return" if is_return else "parameter") + raise Unsupported(what, what) + if kind == "FunctionPointer": + what = "function pointer " + ("return" if is_return else "parameter") + raise Unsupported(what, what) + raise Unsupported(f"type {describe(t)}", f"type {kind}") + + # --- records --- + + def record(self, t, kind): + item = self.md.find_record(kind, t.get("Module"), t.get("Name")) + if item is None: + raise Unsupported(f"{kind.lower()} {t.get('Name')} not found in metadata", RECORD) + key = id(item) + existing = self.records.get(key) + if existing is not None: + if existing.error: + raise existing.error + if existing.building: + raise Unsupported(f"{kind.lower()} {item['Name']} contains itself by value", RECORD) + return existing + rec = RecordDef(item, kind, self._unique_name("AOT_" + sanitize(item["Name"]))) + self.records[key] = rec + rec.building = True + try: + fields = item.get("Fields") or [] + if not fields: + raise Unsupported(f"{kind.lower()} {item['Name']} has no fields in metadata", RECORD) + rec.fields = self._fields(fields) + except Unsupported as e: + rec.error = Unsupported(f"{kind.lower()} {item['Name']}: {e}", e.category) + raise rec.error + finally: + rec.building = False + self.record_order.append(rec) + return rec + + def _unique_name(self, base): + name = base + n = 2 + while name in self.typedef_names: + name = f"{base}_{n}" + n += 1 + self.typedef_names.add(name) + return name + + def _fields(self, fields): + out = [] + for i, field in enumerate(fields): + name = field.get("Name") or "" + if not IDENT.match(name) or name in C_KEYWORDS: + name = f"f{i}" + # Without BitWidth in metadata a bit-field struct (NSDecimal) gets + # the same wrong layout as the runtime's own description of it. + width = field.get("BitWidth") + if width is None: + out.append(self.field_decl(field.get("Signature"), name)) + elif int(width) == 0: + out.append(self.field_decl(field.get("Signature"), "") + " : 0") + else: + out.append(self.field_decl(field.get("Signature"), name) + f" : {int(width)}") + return out + + def field_decl(self, t, name): + """Returns a C declaration for a field of type t named name.""" + t = self.unwrap(t) + kind = t.get("Type") + if kind == "Bool": + return f"BOOL {name}" + if kind in INTEGERS: + return f"{INTEGERS[kind][0]} {name}" + if kind == "Unichar": + return f"unsigned short {name}" + if kind in ("Float", "Double"): + return f"{kind.lower()} {name}" + if kind in ("Pointer", "CString", "FunctionPointer", "Block", "Selector", + "Class", "Protocol") or kind in OBJECT_KINDS: + return f"void* {name}" + if kind in ("Struct", "Union"): + return f"{self.record(t, kind).cname} {name}" + if kind in ("AnonymousStruct", "AnonymousUnion"): + keyword = "struct" if kind == "AnonymousStruct" else "union" + if not t.get("Fields"): + raise Unsupported(f"field {name} is an empty {keyword}", RECORD) + inner = "; ".join(self._fields(t["Fields"])) + ";" + return f"{keyword} {{ {inner} }} {name}" + if kind == "ConstantArray": + return self.field_decl(t.get("ArrayType"), f"{name}[{int(t.get('Size'))}]") + if kind == "ExtVector": + return f"{self.vector(t)} {name}" + raise Unsupported(f"field {name} of type {describe(t)}", RECORD) + + def vector(self, t): + inner = self.unwrap(t.get("InnerType")) + kind = inner.get("Type") + if kind in INTEGERS: + ctype = INTEGERS[kind][0] + elif kind in ("Float", "Double"): + ctype = kind.lower() + else: + raise Unsupported(f"vector of {describe(inner)}", RECORD) + size = int(t.get("Size")) + key = (ctype, size) + if key not in self.vectors: + self.vectors[key] = self._unique_name(f"AOT_{ctype.replace(' ', '_')}{size}") + return self.vectors[key] + + def emit_typedefs(self): + lines = [] + for (ctype, size), name in sorted(self.vectors.items(), key=lambda kv: kv[1]): + lines.append(f"typedef {ctype} {name} __attribute__((ext_vector_type({size})));") + for rec in self.record_order: + keyword = "struct" if rec.kind == "Struct" else "union" + lines.append(f"typedef {keyword} {{") + for decl in rec.fields: + lines.append(f" {decl};") + lines.append(f"}} {rec.cname};") + return lines + + +class RecordDef: + def __init__(self, item, kind, cname): + self.item = item + self.kind = kind + self.cname = cname + self.fields = [] + self.error = None + self.building = False + + @property + def runtime_name(self): + return self.item.get("JsName") or self.item["Name"] + + +IDENT = re.compile(r"^[A-Za-z_][A-Za-z0-9_]*$") +C_KEYWORDS = { + "auto", "break", "case", "char", "const", "continue", "default", "do", + "double", "else", "enum", "extern", "float", "for", "goto", "if", "inline", + "int", "long", "register", "restrict", "return", "short", "signed", + "sizeof", "static", "struct", "switch", "typedef", "union", "unsigned", + "void", "volatile", "while", "id", "SEL", "Class", "BOOL", "YES", "NO", + "nil", "self", "super", "in", "out", "inout", "bycopy", "byref", "oneway", +} + + +# --------------------------------------------------------------------------- +# Code generation +# --------------------------------------------------------------------------- + + +def sanitize(name): + return re.sub(r"[^A-Za-z0-9_]", "_", name) + + +def sanitize_selector(sel): + return sanitize(sel.rstrip(":").replace(":", "_")) + + +def method_stub_name(cls, sel, is_static): + suffix = "_static" if is_static else "" + return f"AOT_{sanitize(cls)}_{sanitize_selector(sel)}{suffix}" + + +def _build_msgsend_call(fn, receiver, ret, args): + params = ", ".join(["id", "SEL"] + [a.ctype for a in args]) + values = "".join(f", a{i}" for i in range(len(args))) + return f"(({ret.ctype} (*)({params})){fn})({receiver}, sel{values})" + + +def build_super_call(fn, ret, args): + params = ", ".join(["struct objc_super*", "SEL"] + [a.ctype for a in args]) + values = "".join(f", a{i}" for i in range(len(args))) + return f"(({ret.ctype} (*)({params})){fn})(&sup, sel{values})" + + +ARG_GETTERS = { + "object": ("id", "nil", "__ns_aot_arg_object"), + "sel": ("SEL", "NULL", "__ns_aot_arg_selector"), + "class": ("Class", "Nil", "__ns_aot_arg_class"), + "pointer": ("void*", "NULL", "__ns_aot_arg_pointer"), +} + +RETURN_SETTERS = { + "bool": "__ns_aot_return_bool(info, r);", + "int": "__ns_aot_return_int64(info, (int64_t)r);", + "uint": "__ns_aot_return_uint64(info, (uint64_t)r);", + "float": "__ns_aot_return_double(info, (double)r);", + "double": "__ns_aot_return_double(info, r);", + "sel": "__ns_aot_return_selector(info, r);", + "class": "__ns_aot_return_class(info, r);", + "pointer": "__ns_aot_return_pointer(info, r);", +} + +RETURN_INIT = { + "bool": "NO", "int": "0", "uint": "0", "float": "0", "double": "0", + "sel": "NULL", "class": "Nil", "object": "nil", "pointer": "NULL", +} + + +class Stub: + def __init__(self, cls, selector, is_static, method, ret, args, name): + self.cls = cls + self.selector = selector + self.is_static = is_static + self.method = method + self.ret = ret + self.args = args + self.name = name + self.registrations = [] + + def flags(self): + return set(self.method.get("Flags") or []) + + def emit(self): + out = [] + w = out.append + w(f"static bool {self.name}(NSAOTCallInfo info) {{") + w(f" if (__ns_aot_arg_count(info) != {len(self.args)}) return false;") + + struct_vars = {} + for t in [self.ret] + self.args: + if t.kind == "struct" and t.record.cname not in struct_vars: + var = f"st{len(struct_vars)}" + struct_vars[t.record.cname] = var + w(f" static NSAOTStructType {var} = NULL;") + w(f" if (!{var}) {var} = __ns_aot_struct_type(\"{t.record.runtime_name}\");") + w(f" if (!{var}) return false;") + + w(f" SEL sel = @selector({self.selector});") + if self.is_static: + w(" Class cls = __ns_aot_get_static_class(info);") + w(" if (cls == nil) return false;") + else: + w(" bool callSuper = false;") + w(" id target = __ns_aot_get_target(info, sel, &sel, &callSuper);") + w(" if (target == nil) return false;") + + for i, a in enumerate(self.args): + k = a.kind + if k == "bool": + w(f" BOOL a{i} = NO;") + w(f" if (!__ns_aot_arg_bool(info, {i}, &a{i})) return true;") + elif k in ("int", "uint"): + raw = "int64_t" if k == "int" else "uint64_t" + getter = "__ns_aot_arg_int64" if k == "int" else "__ns_aot_arg_uint64" + w(f" {raw} raw{i} = 0;") + w(f" if (!{getter}(info, {i}, &raw{i})) return true;") + w(f" {a.ctype} a{i} = ({a.ctype})raw{i};") + elif k in ("float", "double"): + w(f" double raw{i} = 0;") + w(f" if (!__ns_aot_arg_double(info, {i}, &raw{i})) return true;") + w(f" {a.ctype} a{i} = ({a.ctype})raw{i};") + elif k == "struct": + var = struct_vars[a.record.cname] + w(f" {a.ctype} a{i};") + w(f" if (!__ns_aot_arg_struct(info, {i}, {var}, &a{i})) return true;") + else: + ctype, init, getter = ARG_GETTERS[k] + w(f" {ctype} a{i} = {init};") + w(f" if (!{getter}(info, {i}, &a{i})) return true;") + + rk = self.ret.kind + if rk == "struct": + w(f" {self.ret.ctype} r;") + elif rk != "void": + w(f" {self.ret.ctype} r = {RETURN_INIT[rk]};") + assign = "" if rk == "void" else "r = " + if rk == "struct": + send_fn = f"AOT_SEND_STRET({self.ret.ctype})" + super_fn = f"AOT_SEND_SUPER_STRET({self.ret.ctype})" + else: + send_fn = "objc_msgSend" + super_fn = "objc_msgSendSuper" + + w(" @try {") + if self.is_static: + w(f" {assign}{_build_msgsend_call(send_fn, '(id)cls', self.ret, self.args)};") + else: + w(" if (callSuper) {") + w(" struct objc_super sup = {target, class_getSuperclass(object_getClass(target))};") + w(f" {assign}{build_super_call(super_fn, self.ret, self.args)};") + w(" } else {") + w(f" {assign}{_build_msgsend_call(send_fn, 'target', self.ret, self.args)};") + w(" }") + w(" } @catch (NSException* e) {") + w(" __ns_aot_throw_exception(info, e);") + w(" return true;") + w(" }") + + if rk == "object": + owned = "true" if "MethodOwnsReturnedCocoaObject" in self.flags() else "false" + marshal = "false" if self.keeps_object_return() else "true" + w(f" __ns_aot_return_object(info, r, {owned}, {marshal});") + elif rk == "struct": + w(f" __ns_aot_return_struct(info, {struct_vars[self.ret.record.cname]}, &r);") + elif rk != "void": + w(f" {RETURN_SETTERS[rk]}") + w(" return true;") + w("}") + return "\n".join(out) + + def keeps_object_return(self): + """Mirrors the generic path, which leaves instancetype and + NSMutableString returns as objects instead of JS primitives.""" + t = self.method["Signature"][0] + while isinstance(t, dict) and t.get("Type") in ("Nullable", "NonNullable"): + t = t.get("InnerType") + if not isinstance(t, dict): + return False + if t.get("Type") == "Instancetype": + return True + return t.get("Type") == "Interface" and t.get("Name") == "NSMutableString" + + +SKIP_FLAGS = ( + ("MethodIsInitializer", "initializer"), + ("MethodHasErrorOutParameter", "NSError** out-parameter"), + ("MethodIsVariadic", "variadic"), + ("MethodIsNullTerminatedVariadic", "variadic"), +) + + +# ARC rejects these in @selector(), and the stubs must compile under ARC. +ARC_FORBIDDEN_SELECTORS = {"retain", "release", "autorelease", "dealloc", "retainCount"} + + +def plan_entry(md, mapper, entry): + """Returns (Stub, None, None) or (None, reason, category).""" + cls, selector, is_static = entry + found = md.find_method(cls, selector, is_static) + if found is None: + if md.find_class(cls) is None and md.find_protocol(cls) is None: + return None, "class not found in metadata", "class not found" + return None, "selector not found in metadata", "selector not found" + method, binder = found + if selector in ARC_FORBIDDEN_SELECTORS: + return None, "memory-management selector", "memory-management selector" + flags = set(method.get("Flags") or []) + for flag, reason in SKIP_FLAGS: + if flag in flags: + return None, reason, reason + signature = method.get("Signature") + if not isinstance(signature, list) or not signature: + raise InputError(f"method {selector} has no Signature") + if len(signature) - 1 != selector.count(":"): + return None, "parameter count does not match selector", "parameter count mismatch" + try: + ret = mapper.resolve(signature[0], True) + args = [mapper.resolve(t, False) for t in signature[1:]] + except Unsupported as e: + return None, f"unsupported: {e}", f"unsupported: {e.category}" + return Stub(binder, selector, is_static, method, ret, args, None), None, None + + +PREAMBLE = """\ +// Generated by scripts/generate-aot.py. Do not edit. +#import +#import +#import +#import + +typedef void (*AOTSendFn)(void); + +// x86_64 returns a struct through a hidden pointer, and needs the _stret entry +// points, when it is classified MEMORY. Size over 16 bytes approximates that +// classification; smaller structs with unaligned or long double fields are not +// modeled. arm64 never uses _stret. +#if defined(__x86_64__) +#define AOT_SEND_STRET(T) (sizeof(T) > 16 ? (AOTSendFn)objc_msgSend_stret : (AOTSendFn)objc_msgSend) +#define AOT_SEND_SUPER_STRET(T) \\ + (sizeof(T) > 16 ? (AOTSendFn)objc_msgSendSuper_stret : (AOTSendFn)objc_msgSendSuper) +#else +#define AOT_SEND_STRET(T) ((AOTSendFn)objc_msgSend) +#define AOT_SEND_SUPER_STRET(T) ((AOTSendFn)objc_msgSendSuper) +#endif +""" + + +def gen_external_registration(stubs): + lines = ['__attribute__((visibility("default")))', + "void __ns_register_aot_calls(void (*reg)(const char*, const char*, bool," + " NSAOTCallHandler)) {"] + for s in stubs: + static_str = "true" if s.is_static else "false" + for cls in s.registrations: + lines.append(f' reg("{cls}", "{s.selector}", {static_str}, {s.name});') + lines.append("}") + return "\n".join(lines) + + +def load_config(path): + try: + with open(path) as f: + config = json.load(f) + except (OSError, ValueError) as e: + raise InputError(f"cannot read config {path}: {e}") + methods = config.get("methods") if isinstance(config, dict) else None + if not isinstance(methods, list): + raise InputError(f"{path}: expected an object with a \"methods\" array") + entries = [] + seen = set() + for i, m in enumerate(methods): + if not isinstance(m, dict): + raise InputError(f"{path}: methods[{i}] is not an object") + cls = m.get("class") + sel = m.get("selector") + is_static = m.get("static", False) + if not isinstance(cls, str) or not cls or not isinstance(sel, str) or not sel: + raise InputError(f"{path}: methods[{i}] needs non-empty \"class\" and \"selector\" strings") + if not isinstance(is_static, bool): + raise InputError(f"{path}: methods[{i}].static must be a boolean") + if not re.match(r"^[A-Za-z_][A-Za-z0-9_:]*$", sel): + raise InputError(f"{path}: methods[{i}] has an invalid selector {sel!r}") + if not re.match(r"^[A-Za-z_][A-Za-z0-9_.$]*$", cls): + raise InputError(f"{path}: methods[{i}] has an invalid class name {cls!r}") + key = (cls, sel, is_static) + if key not in seen: + seen.add(key) + entries.append(key) + return entries + + +def generate(config_path, metadata_dir, output_path, report): + entries = load_config(config_path) + md = Metadata(metadata_dir) + mapper = TypeMapper(md) + + stubs = [] + by_key = {} + outcomes = [] + names = set() + for entry in entries: + stub, reason, category = plan_entry(md, mapper, entry) + if stub is not None: + key = (stub.cls, stub.selector, stub.is_static) + if key in by_key: + stub = by_key[key] + else: + base = method_stub_name(stub.cls, stub.selector, stub.is_static) + name = base + n = 2 + while name in names: + name = f"{base}_{n}" + n += 1 + names.add(name) + stub.name = name + by_key[key] = stub + stubs.append(stub) + # The runtime binds at the declaring class; the config name is + # registered too so a lookup keyed on it also resolves. + for cls in (stub.cls, entry[0]): + if cls not in stub.registrations: + stub.registrations.append(cls) + reason = None + category = stub.cls + outcomes.append((entry, reason, category)) + + parts = [PREAMBLE] + typedefs = mapper.emit_typedefs() + if typedefs: + parts.append("\n".join(typedefs) + "\n") + for s in stubs: + parts.append(s.emit() + "\n") + parts.append(gen_external_registration(stubs) + "\n") + + out_dir = os.path.dirname(os.path.abspath(output_path)) + os.makedirs(out_dir, exist_ok=True) + with open(output_path, "w") as f: + f.write("\n".join(parts)) + + skipped = [o for o in outcomes if o[1] is not None] + for (cls, sel, is_static), reason, detail in outcomes if report else skipped: + prefix = "+" if is_static else "-" + if reason is not None: + status = f"skipped: {reason}" + elif detail == cls: + status = "generated" + else: + status = f"generated, declared on {detail}" + print(f"{prefix}[{cls} {sel}] {status}") + print(f"wrote {output_path}") + print(f"generated {len(outcomes) - len(skipped)} entries as {len(stubs)} stubs, skipped {len(skipped)}") + by_category = {} + for _entry, _reason, category in skipped: + by_category[category] = by_category.get(category, 0) + 1 + for category, n in sorted(by_category.items(), key=lambda kv: (-kv[1], kv[0])): + print(f" {n:4d} {category}") + + +def main(argv): + parser = argparse.ArgumentParser( + description="Generate NativeScript AOT call stubs from an AOT config and metadata JSON.") + parser.add_argument("config", help="AOT config JSON ({\"methods\": [...]})") + parser.add_argument("-m", "--metadata", required=True, + help="directory of .json files from the metadata generator's -output-json") + parser.add_argument("-o", "--output", default="NativeScriptAOTStubs.m", + help="output Objective-C file (default: %(default)s)") + parser.add_argument("--report", action="store_true", + help="list the outcome of every config entry") + args = parser.parse_args(argv) + try: + generate(args.config, args.metadata, args.output, args.report) + except InputError as e: + print(f"error: {e}", file=sys.stderr) + return 2 + return 0 + + +if __name__ == "__main__": + sys.exit(main(sys.argv[1:])) diff --git a/v8ios.xcodeproj/project.pbxproj b/v8ios.xcodeproj/project.pbxproj index 8d4bac3ae..7d15666cc 100644 --- a/v8ios.xcodeproj/project.pbxproj +++ b/v8ios.xcodeproj/project.pbxproj @@ -273,6 +273,11 @@ C2DDEB8E229EAC8300345BFE /* Interop.h in Headers */ = {isa = PBXBuildFile; fileRef = C2DDEB65229EAC8100345BFE /* Interop.h */; }; C2DDEB8F229EAC8300345BFE /* ArgConverter.h in Headers */ = {isa = PBXBuildFile; fileRef = C2DDEB66229EAC8100345BFE /* ArgConverter.h */; }; C2DDEB90229EAC8300345BFE /* MetadataBuilder.h in Headers */ = {isa = PBXBuildFile; fileRef = C2DDEB67229EAC8100345BFE /* MetadataBuilder.h */; }; + 35550BC20A65733EB364EF65 /* AOTCalls.h in Headers */ = {isa = PBXBuildFile; fileRef = 5BDF867F4303941D505BD0D7 /* AOTCalls.h */; }; + 734C2098143923198254BB11 /* NativeScriptAOTBridge.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9FF02F7EF762E15F74F91E42 /* NativeScriptAOTBridge.mm */; }; + 404F17E0B24AFAC78D4A86EB /* MethodCallProfiler.h in Headers */ = {isa = PBXBuildFile; fileRef = 97B4A35271DB242D64E467D1 /* MethodCallProfiler.h */; }; + 20CF1F3B8B1364F5743665BE /* MethodCallProfiler.mm in Sources */ = {isa = PBXBuildFile; fileRef = FF50F7CA10E462BE9CDEC3F6 /* MethodCallProfiler.mm */; }; + F3759DC6A4C5420F6B4BDA2F /* NativeScriptAOT.h in Headers */ = {isa = PBXBuildFile; fileRef = 1400A6C0FE41C83FF782CF6E /* NativeScriptAOT.h */; settings = {ATTRIBUTES = (Public, ); }; }; C2DDEB91229EAC8300345BFE /* FFICall.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2DDEB68229EAC8100345BFE /* FFICall.cpp */; }; C2DDEB92229EAC8300345BFE /* WeakRef.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2DDEB69229EAC8100345BFE /* WeakRef.cpp */; }; 4A5C201A2E2B000100000006 /* BuiltinLoader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4A5C201A2E2B000100000001 /* BuiltinLoader.cpp */; }; @@ -316,6 +321,7 @@ C2DDEBB4229EAC8300345BFE /* DictionaryAdapter.mm in Sources */ = {isa = PBXBuildFile; fileRef = C2DDEB8B229EAC8300345BFE /* DictionaryAdapter.mm */; }; C2DDEBB5229EAC8300345BFE /* Tasks.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2DDEB8C229EAC8300345BFE /* Tasks.cpp */; }; C2DDEBDC229EB37A00345BFE /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = C2DDEBDB229EB37A00345BFE /* main.m */; }; + CA38E3620BA8910F9CA06043 /* NativeScriptAOTStubs.m in Sources */ = {isa = PBXBuildFile; fileRef = 1D3A17968C10D5BB75B88EAB /* NativeScriptAOTStubs.m */; }; C2DDEBDE229EB4A900345BFE /* app in Resources */ = {isa = PBXBuildFile; fileRef = C2DDEBDD229EB4A900345BFE /* app */; }; C2F4D0AD232F85E20008A2EB /* SymbolIterator.mm in Sources */ = {isa = PBXBuildFile; fileRef = C2F4D0AB232F85E20008A2EB /* SymbolIterator.mm */; }; C2F4D0AE232F85E20008A2EB /* SymbolIterator.h in Headers */ = {isa = PBXBuildFile; fileRef = C2F4D0AC232F85E20008A2EB /* SymbolIterator.h */; }; @@ -792,6 +798,11 @@ C2DDEB65229EAC8100345BFE /* Interop.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Interop.h; sourceTree = ""; }; C2DDEB66229EAC8100345BFE /* ArgConverter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ArgConverter.h; sourceTree = ""; }; C2DDEB67229EAC8100345BFE /* MetadataBuilder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MetadataBuilder.h; sourceTree = ""; }; + 5BDF867F4303941D505BD0D7 /* AOTCalls.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AOTCalls.h; sourceTree = ""; }; + 9FF02F7EF762E15F74F91E42 /* NativeScriptAOTBridge.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = NativeScriptAOTBridge.mm; sourceTree = ""; }; + 97B4A35271DB242D64E467D1 /* MethodCallProfiler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MethodCallProfiler.h; sourceTree = ""; }; + FF50F7CA10E462BE9CDEC3F6 /* MethodCallProfiler.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MethodCallProfiler.mm; sourceTree = ""; }; + 1400A6C0FE41C83FF782CF6E /* NativeScriptAOT.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NativeScriptAOT.h; sourceTree = ""; }; C2DDEB68229EAC8100345BFE /* FFICall.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FFICall.cpp; sourceTree = ""; }; C2DDEB69229EAC8100345BFE /* WeakRef.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WeakRef.cpp; sourceTree = ""; }; 4A5C201A2E2B000100000001 /* BuiltinLoader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BuiltinLoader.cpp; sourceTree = ""; }; @@ -844,6 +855,7 @@ C2DDEB8B229EAC8300345BFE /* DictionaryAdapter.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = DictionaryAdapter.mm; sourceTree = ""; }; C2DDEB8C229EAC8300345BFE /* Tasks.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Tasks.cpp; sourceTree = ""; }; C2DDEBDB229EB37A00345BFE /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 1D3A17968C10D5BB75B88EAB /* NativeScriptAOTStubs.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NativeScriptAOTStubs.m; sourceTree = ""; }; C2DDEBDD229EB4A900345BFE /* app */ = {isa = PBXFileReference; lastKnownFileType = folder; path = app; sourceTree = ""; }; C2F4CBB322C60BFD0036B56F /* metadata-generator */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "metadata-generator"; sourceTree = BUILT_PRODUCTS_DIR; }; C2F4D0AB232F85E20008A2EB /* SymbolIterator.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = SymbolIterator.mm; sourceTree = ""; }; @@ -1422,6 +1434,7 @@ children = ( C2DDEBDD229EB4A900345BFE /* app */, C2DDEBDA229EB37A00345BFE /* Source Files */, + D942047BA67E24E9ECF511AC /* AOT */, C2DDEB26229EA89200345BFE /* Info.plist */, C266567722AA405100EE15CC /* Default-568h@2x.png */, ); @@ -1508,6 +1521,11 @@ C2DDEB70229EAC8200345BFE /* Metadata.mm */, C2DDEB67229EAC8100345BFE /* MetadataBuilder.h */, C2DDEB71229EAC8200345BFE /* MetadataBuilder.mm */, + 5BDF867F4303941D505BD0D7 /* AOTCalls.h */, + 9FF02F7EF762E15F74F91E42 /* NativeScriptAOTBridge.mm */, + 97B4A35271DB242D64E467D1 /* MethodCallProfiler.h */, + FF50F7CA10E462BE9CDEC3F6 /* MethodCallProfiler.mm */, + 1400A6C0FE41C83FF782CF6E /* NativeScriptAOT.h */, C2DDEB89229EAC8300345BFE /* ModuleInternal.h */, AA8C47AF2E27114300649BF5 /* ModuleInternalCallbacks.h */, AA8C47B02E27114300649BF5 /* ModuleInternalCallbacks.mm */, @@ -1601,6 +1619,14 @@ path = "Source Files"; sourceTree = ""; }; + D942047BA67E24E9ECF511AC /* AOT */ = { + isa = PBXGroup; + children = ( + 1D3A17968C10D5BB75B88EAB /* NativeScriptAOTStubs.m */, + ); + path = AOT; + sourceTree = ""; + }; C2FF3016225203FD00933782 /* Frameworks */ = { isa = PBXGroup; children = ( @@ -1695,6 +1721,9 @@ 3C48F68D2F57905500C14231 /* json.hpp in Headers */, C247C17022F82842001D2CA2 /* libffi.h in Headers */, C2DDEB90229EAC8300345BFE /* MetadataBuilder.h in Headers */, + 35550BC20A65733EB364EF65 /* AOTCalls.h in Headers */, + 404F17E0B24AFAC78D4A86EB /* MethodCallProfiler.h in Headers */, + F3759DC6A4C5420F6B4BDA2F /* NativeScriptAOT.h in Headers */, C266569F22B282BA00EE15CC /* FunctionReference.h in Headers */, C2FEA17022A3C75C00A5C0FC /* InlineFunctions.h in Headers */, C2DDEBA1229EAC8300345BFE /* FFICall.h in Headers */, @@ -2289,6 +2318,7 @@ buildActionMask = 2147483647; files = ( C2DDEBDC229EB37A00345BFE /* main.m in Sources */, + CA38E3620BA8910F9CA06043 /* NativeScriptAOTStubs.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2339,6 +2369,8 @@ C298C027233C9AEA000DDF54 /* TSHelpers.cpp in Sources */, C2FEA16F22A3C75C00A5C0FC /* InlineFunctions.cpp in Sources */, C2DDEB9A229EAC8300345BFE /* MetadataBuilder.mm in Sources */, + 734C2098143923198254BB11 /* NativeScriptAOTBridge.mm in Sources */, + 20CF1F3B8B1364F5743665BE /* MethodCallProfiler.mm in Sources */, C266569722AFFFB000EE15CC /* Reference.cpp in Sources */, 6573B9EE291FE5B700B0ED7C /* JSIRuntime.m in Sources */, F6191AB229C0FCE8003F588F /* InspectorServer.mm in Sources */,