From d1e1fed0399d2c546e76014a00b7fdcf24fb109c Mon Sep 17 00:00:00 2001 From: Eduardo Speroni Date: Fri, 12 Jun 2026 14:34:36 -0300 Subject: [PATCH 1/2] fix(metadata-generator): strip nullability wrappers before structural type checks Since #357/#363, _Nullable/_Nonnull annotations are represented as NullableType/NonNullableType wrapper nodes in the type tree, breaking every consumer that pattern-matches on type kind without unwrapping: - MethodHasErrorOutParameter: `NSError * _Nullable * _Nullable` no longer matched Pointer(Interface NSError). #367 added a single-level unwrap, but wrappers can nest (e.g. BNNSFilterCreateLayerPadding, whose typedef carries its own nullability), so it could still miss. - CF Create Rule: functions returning `CFXxxRef _Nullable` skipped the FunctionOwnsReturnedCocoaObject/FunctionReturnsUnmanaged flags, breaking CF memory management for ~200 SDK functions. - Utils::areTypesEqual: wrapper kinds fell into `default: return true`, so any two Nullable types compared equal (and Nullable(A) != A), affecting RemoveDuplicateMembersFilter. - .d.ts output: hasClosedGenerics/getClosedGenericsIfAny missed wrapped interface returns, dropping generic type parameters (e.g. dictionaryWithContentsOfFile lost ). - YAML output: Nullable/NonNullable had no serialization cases and were silently dropped. Fix: add Type::stripNullability(), which unwraps arbitrarily nested nullability wrappers, and use it at every structural inspection site. createFromAttributedType now also collapses nesting at creation (the outermost annotation wins), so wrappers can no longer stack. The binary serializer remains intentionally transparent to these wrappers, so the runtime metadata shape is unchanged. Adds a TNSApi fixture with a nullability-carrying typedef error parameter (`typedef NSError* _Nullable TNSNullableError`) plus a matching marshalling test to cover the nested-annotation case. Supersedes the runtime fallback proposed in #382 by fixing the flag at the source; #382's diagnostics improvements remain worth cherry-picking separately. Fixes regression introduced in #357/#363, completes #367. --- TestFixtures/Api/TNSApi.h | 6 + TestFixtures/Api/TNSApi.m | 12 + .../app/tests/Marshalling/ObjCTypesTests.js | 19 + metadata-generator/src/Meta/MetaFactory.cpp | 18 +- metadata-generator/src/Meta/TypeEntities.h | 25 + metadata-generator/src/Meta/TypeFactory.cpp | 8 +- metadata-generator/src/Meta/Utils.cpp | 261 ++-- .../src/TypeScript/DefinitionWriter.cpp | 26 +- metadata-generator/src/Yaml/MetaYamlTraits.h | 1108 ++++++++--------- 9 files changed, 779 insertions(+), 704 deletions(-) diff --git a/TestFixtures/Api/TNSApi.h b/TestFixtures/Api/TNSApi.h index a4ea7e9f..58ed3b31 100644 --- a/TestFixtures/Api/TNSApi.h +++ b/TestFixtures/Api/TNSApi.h @@ -34,6 +34,12 @@ typedef UIColor NIKColor; - (BOOL)method:(NSInteger)errorCode error:(NSError**)outError; - (BOOL)methodNullable:(NSInteger)errorCode error:(NSError* _Nullable* _Nullable)outError; + +// The typedef carries its own nullability, so the parameter type ends up with +// nested nullability annotations (like BNNSFilterCreateLayerPadding in the SDK) +typedef NSError* _Nullable TNSNullableError; +- (BOOL)methodTypedefNullable:(NSInteger)errorCode + error:(TNSNullableError _Nullable* _Nullable)outError; @end @interface TNSConflictingSelectorTypes1 : NSObject diff --git a/TestFixtures/Api/TNSApi.m b/TestFixtures/Api/TNSApi.m index 0f007726..2085b567 100644 --- a/TestFixtures/Api/TNSApi.m +++ b/TestFixtures/Api/TNSApi.m @@ -56,6 +56,18 @@ - (BOOL)methodNullable:(NSInteger)errorCode error:(NSError* _Nullable* _Nullable return errorCode == 0; } +- (BOOL)methodTypedefNullable:(NSInteger)errorCode + error:(TNSNullableError _Nullable* _Nullable)outError { + if (outError) { + if (errorCode != 0) { + *outError = [NSError errorWithDomain:@"TNSErrorDomain" code:errorCode userInfo:nil]; + } else { + *outError = nil; + } + } + return errorCode == 0; +} + @end @implementation TNSConflictingSelectorTypes1 diff --git a/TestRunner/app/tests/Marshalling/ObjCTypesTests.js b/TestRunner/app/tests/Marshalling/ObjCTypesTests.js index c5dc8364..d1b4ca83 100644 --- a/TestRunner/app/tests/Marshalling/ObjCTypesTests.js +++ b/TestRunner/app/tests/Marshalling/ObjCTypesTests.js @@ -575,4 +575,23 @@ describe(module.id, function () { TNSApi.new().methodNullableError(1, errorRef); expect(errorRef.value instanceof NSError).toBe(true); }); + + it("NSErrorOutParameterWithNullabilityCarryingTypedef", function () { + expect(function () { + TNSApi.new().methodTypedefNullableError(0); + }).not.toThrow(); + + var isThrown = false; + try { + TNSApi.new().methodTypedefNullableError(1); + } catch (e) { + isThrown = true; + expect(e.stack).toEqual(jasmine.any(String)); + } + expect(isThrown).toBe(true); + + var errorRef = new interop.Reference(); + TNSApi.new().methodTypedefNullableError(1, errorRef); + expect(errorRef.value instanceof NSError).toBe(true); + }); }); diff --git a/metadata-generator/src/Meta/MetaFactory.cpp b/metadata-generator/src/Meta/MetaFactory.cpp index 326b4a7a..7f969a3f 100644 --- a/metadata-generator/src/Meta/MetaFactory.cpp +++ b/metadata-generator/src/Meta/MetaFactory.cpp @@ -269,7 +269,7 @@ void MetaFactory::createFromFunction(const clang::FunctionDecl& function, // Clang doesn't handle The Create Rule automatically like for methods, so we // have to do it manually if (!(returnsRetained || returnsNotRetained) && - functionMeta.signature[0]->is(TypeBridgedInterface)) { + functionMeta.signature[0]->stripNullability()->is(TypeBridgedInterface)) { if (function.hasAttr()) { std::string functionName = function.getNameAsString(); if (functionName.find("Create") != string::npos || @@ -490,23 +490,19 @@ void MetaFactory::createFromMethod(const clang::ObjCMethodDecl& method, isNullTerminatedVariadic); // set MethodHasErrorOutParameter flag + // Nullability annotations (`NSError * _Nullable * _Nullable`) are wrapper + // nodes in the type tree, so strip them before inspecting the structure. if (method.parameters().size() > 0) { clang::ParmVarDecl* lastParameter = method.parameters()[method.parameters().size() - 1]; Type* type = _typeFactory.create(lastParameter->getType()).get(); - if (type != nullptr && (type->is(TypeType::TypeNullable) || - type->is(TypeType::TypeNonNullable))) { - type = type->is(TypeType::TypeNullable) - ? type->as().innerType - : type->as().innerType; + if (type != nullptr) { + type = type->stripNullability(); } if (type != nullptr && type->is(TypeType::TypePointer)) { Type* innerType = type->as().innerType; - if (innerType != nullptr && (innerType->is(TypeType::TypeNullable) || - innerType->is(TypeType::TypeNonNullable))) { - innerType = innerType->is(TypeType::TypeNullable) - ? innerType->as().innerType - : innerType->as().innerType; + if (innerType != nullptr) { + innerType = innerType->stripNullability(); } if (innerType != nullptr && innerType->is(TypeType::TypeInterface) && innerType->as().interface->jsName == "NSError") { diff --git a/metadata-generator/src/Meta/TypeEntities.h b/metadata-generator/src/Meta/TypeEntities.h index 5f3b5d6a..ff4aa214 100644 --- a/metadata-generator/src/Meta/TypeEntities.h +++ b/metadata-generator/src/Meta/TypeEntities.h @@ -74,6 +74,15 @@ class Type { bool is(TypeType type) const { return this->type == type; } + // Returns the underlying type with any NullableType/NonNullableType wrappers + // removed (handles arbitrarily nested wrappers). Use this before inspecting + // the structure of a type (e.g. is(TypePointer), as()), since + // nullability annotations are represented as wrapper nodes in the type tree. + Type* stripNullability(); + const Type* stripNullability() const { + return const_cast(this)->stripNullability(); + } + template T visit(TypeVisitor& visitor) const { switch (this->type) { @@ -335,4 +344,20 @@ class NonNullableType : public Type { Type* innerType; }; + +inline Type* Type::stripNullability() { + Type* current = this; + while (true) { + Type* inner = nullptr; + if (current->is(TypeType::TypeNullable)) { + inner = current->as().innerType; + } else if (current->is(TypeType::TypeNonNullable)) { + inner = current->as().innerType; + } + if (inner == nullptr) { + return current; + } + current = inner; + } +} } // namespace Meta diff --git a/metadata-generator/src/Meta/TypeFactory.cpp b/metadata-generator/src/Meta/TypeFactory.cpp index 0b3de2f5..8e4bfc07 100644 --- a/metadata-generator/src/Meta/TypeFactory.cpp +++ b/metadata-generator/src/Meta/TypeFactory.cpp @@ -506,10 +506,14 @@ shared_ptr TypeFactory::createFromAttributedType( const clang::AttributedType* type) { auto innerType = this->create(type->getModifiedType()); if (auto nullability = type->getImmediateNullability()) { + // Strip any nullability wrapper the inner type may already carry (e.g. a + // typedef that has its own annotation) so wrappers never nest and the + // outermost annotation wins. The stripped type stays alive in _cache. + Type* unwrapped = innerType->stripNullability(); if (*nullability == clang::NullabilityKind::Nullable) { - return make_shared(innerType.get()); + return make_shared(unwrapped); } else if (*nullability == clang::NullabilityKind::NonNull) { - return make_shared(innerType.get()); + return make_shared(unwrapped); } } return innerType; diff --git a/metadata-generator/src/Meta/Utils.cpp b/metadata-generator/src/Meta/Utils.cpp index 73333b15..52706023 100644 --- a/metadata-generator/src/Meta/Utils.cpp +++ b/metadata-generator/src/Meta/Utils.cpp @@ -1,186 +1,201 @@ #include "Utils.h" + #include "TypeEntities.h" namespace Meta { -bool areRecordFieldListsEqual(const std::vector& vector1, const std::vector& vector2) -{ - if (vector1.size() != vector2.size()) { - return false; - } +bool areRecordFieldListsEqual(const std::vector& vector1, + const std::vector& vector2) { + if (vector1.size() != vector2.size()) { + return false; + } - for (std::vector::size_type i = 0; i < vector1.size(); i++) { - if ((vector1[i].name != vector2[i].name) || !Utils::areTypesEqual(*vector1[i].encoding, *vector2[i].encoding)) { - return false; - } + for (std::vector::size_type i = 0; i < vector1.size(); i++) { + if ((vector1[i].name != vector2[i].name) || + !Utils::areTypesEqual(*vector1[i].encoding, *vector2[i].encoding)) { + return false; } - return true; + } + return true; } // TODO: This logic should be moved in types (and meta entities) entites -bool Utils::areTypesEqual(const Type& type1, const Type& type2) -{ - if (type1.getType() != type2.getType()) - return false; +bool Utils::areTypesEqual(const Type& type1In, const Type& type2In) { + // Nullability annotations are wrapper nodes in the type tree and are not + // significant for equality (e.g. a protocol redeclaration differing only in + // _Nullable/_Nonnull is the same member), so compare the underlying types. + const Type& type1 = *type1In.stripNullability(); + const Type& type2 = *type2In.stripNullability(); + if (type1.getType() != type2.getType()) return false; - switch (type1.getType()) { + switch (type1.getType()) { case TypeType::TypeClass: { - const ClassType& classType1 = type1.as(); - const ClassType& classType2 = type2.as(); - return classType1.protocols == classType2.protocols; + const ClassType& classType1 = type1.as(); + const ClassType& classType2 = type2.as(); + return classType1.protocols == classType2.protocols; } case TypeType::TypeId: { - const IdType& idType1 = type1.as(); - const IdType& idType2 = type2.as(); - return idType1.protocols == idType2.protocols; + const IdType& idType1 = type1.as(); + const IdType& idType2 = type2.as(); + return idType1.protocols == idType2.protocols; }; case TypeType::TypeConstantArray: { - const ConstantArrayType& arrayType1 = type1.as(); - const ConstantArrayType& arrayType2 = type2.as(); - return arrayType1.size == arrayType2.size && areTypesEqual(*arrayType1.innerType, *arrayType2.innerType); + const ConstantArrayType& arrayType1 = type1.as(); + const ConstantArrayType& arrayType2 = type2.as(); + return arrayType1.size == arrayType2.size && + areTypesEqual(*arrayType1.innerType, *arrayType2.innerType); }; case TypeType::TypeExtVector: { - const ExtVectorType& arrayType1 = type1.as(); - const ExtVectorType& arrayType2 = type2.as(); - return arrayType1.size == arrayType2.size && areTypesEqual(*arrayType1.innerType, *arrayType2.innerType); + const ExtVectorType& arrayType1 = type1.as(); + const ExtVectorType& arrayType2 = type2.as(); + return arrayType1.size == arrayType2.size && + areTypesEqual(*arrayType1.innerType, *arrayType2.innerType); }; case TypeType::TypeIncompleteArray: { - const IncompleteArrayType& arrayType1 = type1.as(); - const IncompleteArrayType& arrayType2 = type2.as(); - return areTypesEqual(*arrayType1.innerType, *arrayType2.innerType); + const IncompleteArrayType& arrayType1 = type1.as(); + const IncompleteArrayType& arrayType2 = type2.as(); + return areTypesEqual(*arrayType1.innerType, *arrayType2.innerType); }; case TypeType::TypePointer: { - const PointerType& pointerType1 = type1.as(); - const PointerType& pointerType2 = type2.as(); - return areTypesEqual(*pointerType1.innerType, *pointerType2.innerType); + const PointerType& pointerType1 = type1.as(); + const PointerType& pointerType2 = type2.as(); + return areTypesEqual(*pointerType1.innerType, *pointerType2.innerType); }; case TypeType::TypeBlock: { - const BlockType& blockType1 = type1.as(); - const BlockType& blockType2 = type2.as(); - return Utils::areTypesEqual(blockType1.signature, blockType2.signature); + const BlockType& blockType1 = type1.as(); + const BlockType& blockType2 = type2.as(); + return Utils::areTypesEqual(blockType1.signature, blockType2.signature); }; case TypeType::TypeFunctionPointer: { - const FunctionPointerType& functionType1 = type1.as(); - const FunctionPointerType& functionType2 = type2.as(); - return Utils::areTypesEqual(functionType1.signature, functionType2.signature); + const FunctionPointerType& functionType1 = + type1.as(); + const FunctionPointerType& functionType2 = + type2.as(); + return Utils::areTypesEqual(functionType1.signature, + functionType2.signature); }; case TypeType::TypeInterface: { - const InterfaceType& interfaceType1 = type1.as(); - const InterfaceType& interfaceType2 = type2.as(); - return interfaceType1.interface == interfaceType2.interface && interfaceType1.protocols == interfaceType2.protocols; + const InterfaceType& interfaceType1 = type1.as(); + const InterfaceType& interfaceType2 = type2.as(); + return interfaceType1.interface == interfaceType2.interface && + interfaceType1.protocols == interfaceType2.protocols; }; case TypeType::TypeBridgedInterface: { - const BridgedInterfaceType& interfaceType1 = type1.as(); - const BridgedInterfaceType& interfaceType2 = type2.as(); - return interfaceType1.name == interfaceType2.name; + const BridgedInterfaceType& interfaceType1 = + type1.as(); + const BridgedInterfaceType& interfaceType2 = + type2.as(); + return interfaceType1.name == interfaceType2.name; }; case TypeType::TypeStruct: { - const StructType& structType1 = type1.as(); - const StructType& structType2 = type2.as(); - return structType1.structMeta == structType2.structMeta; + const StructType& structType1 = type1.as(); + const StructType& structType2 = type2.as(); + return structType1.structMeta == structType2.structMeta; }; case TypeType::TypeUnion: { - const UnionType& unionType1 = type1.as(); - const UnionType& unionType2 = type2.as(); - return unionType1.unionMeta == unionType2.unionMeta; + const UnionType& unionType1 = type1.as(); + const UnionType& unionType2 = type2.as(); + return unionType1.unionMeta == unionType2.unionMeta; }; case TypeType::TypeAnonymousStruct: { - const AnonymousStructType& structType1 = type1.as(); - const AnonymousStructType& structType2 = type2.as(); - return areRecordFieldListsEqual(structType1.fields, structType2.fields); + const AnonymousStructType& structType1 = type1.as(); + const AnonymousStructType& structType2 = type2.as(); + return areRecordFieldListsEqual(structType1.fields, structType2.fields); }; case TypeType::TypeAnonymousUnion: { - const AnonymousUnionType& unionType1 = type1.as(); - const AnonymousUnionType& unionType2 = type2.as(); - return areRecordFieldListsEqual(unionType1.fields, unionType2.fields); + const AnonymousUnionType& unionType1 = type1.as(); + const AnonymousUnionType& unionType2 = type2.as(); + return areRecordFieldListsEqual(unionType1.fields, unionType2.fields); }; case TypeType::TypeTypeArgument: { - const TypeArgumentType& argType1 = type1.as(); - const TypeArgumentType& argType2 = type2.as(); - return areTypesEqual(*argType1.underlyingType, *argType2.underlyingType); + const TypeArgumentType& argType1 = type1.as(); + const TypeArgumentType& argType2 = type2.as(); + return areTypesEqual(*argType1.underlyingType, *argType2.underlyingType); }; default: { - return true; - } + return true; } + } } -bool Utils::areTypesEqual(const std::vector& vector1, const std::vector& vector2) -{ - if (vector1.size() != vector2.size()) { - return false; - } +bool Utils::areTypesEqual(const std::vector& vector1, + const std::vector& vector2) { + if (vector1.size() != vector2.size()) { + return false; + } - for (std::vector::size_type i = 0; i < vector1.size(); i++) { - if (!Utils::areTypesEqual(*vector1[i], *vector2[i])) { - return false; - } + for (std::vector::size_type i = 0; i < vector1.size(); i++) { + if (!Utils::areTypesEqual(*vector1[i], *vector2[i])) { + return false; } - return true; + } + return true; } -static bool isalpha(const std::vector& strings, size_t index) -{ - for (auto& str : strings) { - if (!std::isalpha(str[index])) { - return false; - } +static bool isalpha(const std::vector& strings, size_t index) { + for (auto& str : strings) { + if (!std::isalpha(str[index])) { + return false; } - return true; + } + return true; } -static std::string createValidPrefix(const std::vector& fieldNames, const std::string& prefix) -{ - if (!prefix.empty()) { - for (const std::string& field : fieldNames) { - if (std::isdigit(field[prefix.size()])) { - int newPrefixLength = prefix.size(); - while (newPrefixLength > 0 && !std::isupper(field[newPrefixLength])) { - newPrefixLength--; - } - - std::string newPrefix = prefix.substr(0, newPrefixLength); - return createValidPrefix(fieldNames, newPrefix); - } +static std::string createValidPrefix(const std::vector& fieldNames, + const std::string& prefix) { + if (!prefix.empty()) { + for (const std::string& field : fieldNames) { + if (std::isdigit(field[prefix.size()])) { + int newPrefixLength = prefix.size(); + while (newPrefixLength > 0 && !std::isupper(field[newPrefixLength])) { + newPrefixLength--; } - bool allMembersStartWithUnderscore = true; - for (const std::string& field : fieldNames) { - if (field[prefix.size()] != '_') { - allMembersStartWithUnderscore = false; - break; - } - } - if (allMembersStartWithUnderscore) { - return createValidPrefix(fieldNames, prefix + '_'); - } + std::string newPrefix = prefix.substr(0, newPrefixLength); + return createValidPrefix(fieldNames, newPrefix); + } } - return prefix; + bool allMembersStartWithUnderscore = true; + for (const std::string& field : fieldNames) { + if (field[prefix.size()] != '_') { + allMembersStartWithUnderscore = false; + break; + } + } + if (allMembersStartWithUnderscore) { + return createValidPrefix(fieldNames, prefix + '_'); + } + } + + return prefix; } -std::string Utils::calculateEnumFieldsPrefix(const std::string& enumName, const std::vector& fields) -{ - for (size_t prefixLength = 0; prefixLength < enumName.size(); prefixLength++) { - char c = enumName[prefixLength]; - for (size_t i = 0; i < fields.size(); i++) { - if (prefixLength >= fields[i].size() || fields[i][prefixLength] != c) { - while (prefixLength > 0 && (!std::isupper(fields[i][prefixLength]) || !isalpha(fields, prefixLength))) { - prefixLength--; - } - return createValidPrefix(fields, fields[i].substr(0, prefixLength)); - } +std::string Utils::calculateEnumFieldsPrefix( + const std::string& enumName, const std::vector& fields) { + for (size_t prefixLength = 0; prefixLength < enumName.size(); + prefixLength++) { + char c = enumName[prefixLength]; + for (size_t i = 0; i < fields.size(); i++) { + if (prefixLength >= fields[i].size() || fields[i][prefixLength] != c) { + while (prefixLength > 0 && (!std::isupper(fields[i][prefixLength]) || + !isalpha(fields, prefixLength))) { + prefixLength--; } + return createValidPrefix(fields, fields[i].substr(0, prefixLength)); + } } + } - return createValidPrefix(fields, enumName); + return createValidPrefix(fields, enumName); } -void Utils::getAllLinkLibraries(clang::Module* module, std::vector& result) -{ - for (clang::Module::LinkLibrary lib : module->LinkLibraries) - result.push_back(lib); - for (clang::Module::submodule_const_iterator it = module->submodules().begin(); - it != module->submodules().end(); ++it) - getAllLinkLibraries(*it, result); -} +void Utils::getAllLinkLibraries( + clang::Module* module, std::vector& result) { + for (clang::Module::LinkLibrary lib : module->LinkLibraries) + result.push_back(lib); + for (clang::Module::submodule_const_iterator it = + module->submodules().begin(); + it != module->submodules().end(); ++it) + getAllLinkLibraries(*it, result); } +} // namespace Meta diff --git a/metadata-generator/src/TypeScript/DefinitionWriter.cpp b/metadata-generator/src/TypeScript/DefinitionWriter.cpp index 766b43bc..5a62003d 100644 --- a/metadata-generator/src/TypeScript/DefinitionWriter.cpp +++ b/metadata-generator/src/TypeScript/DefinitionWriter.cpp @@ -140,7 +140,7 @@ void DefinitionWriter::visit(InterfaceMeta* meta) { nullptr); for (auto& methodPair : inheritedStaticMethods) { MethodMeta* method = methodPair.second.second; - if (!method->signature[0]->is(TypeInstancetype)) { + if (!method->signature[0]->stripNullability()->is(TypeInstancetype)) { continue; } if (compoundStaticMethods.find(method->jsName) != @@ -561,7 +561,8 @@ std::string DefinitionWriter::writeConstructor( return output.str(); } -void getClosedGenericsIfAny(Type& type, std::vector& params) { +void getClosedGenericsIfAny(Type& typeIn, std::vector& params) { + Type& type = *typeIn.stripNullability(); if (type.is(TypeInterface)) { const InterfaceType& interfaceType = type.as(); for (size_t i = 0; i < interfaceType.typeArguments.size(); i++) { @@ -634,7 +635,7 @@ std::string DefinitionWriter::writeMethod(MethodMeta* meta, const Type* retType = meta->signature[0]; if (!methodDecl.isInstanceMethod() && owner->is(MetaType::Interface)) { - if ((retType->is(TypeInstancetype) || + if ((retType->stripNullability()->is(TypeInstancetype) || DefinitionWriter::hasClosedGenerics(*retType)) && !skipGenerics) { output << getTypeParametersStringOrEmpty( @@ -726,7 +727,8 @@ std::string DefinitionWriter::writeMethod( bool implementsProtocol = protocols.find(static_cast(memberOwner)) != protocols.end(); - bool returnsInstanceType = method->signature[0]->is(TypeInstancetype); + bool returnsInstanceType = + method->signature[0]->stripNullability()->is(TypeInstancetype); if (isOwnMethod || implementsProtocol || returnsInstanceType) { output << writeMethod(method, owner, canUseThisType); @@ -939,7 +941,8 @@ std::string DefinitionWriter::localizeReference(const ::Meta::Meta& meta) { return localizeReference(meta.jsName, meta.module->getFullModuleName()); } -bool DefinitionWriter::hasClosedGenerics(const Type& type) { +bool DefinitionWriter::hasClosedGenerics(const Type& typeIn) { + const Type& type = *typeIn.stripNullability(); if (type.is(TypeInterface)) { const InterfaceType& interfaceType = type.as(); return interfaceType.typeArguments.size(); @@ -1005,10 +1008,11 @@ std::string DefinitionWriter::tsifyType(const Type& type, tsifyType(*type.as().innerType) + ">"; case TypePointer: { const PointerType& pointerType = type.as(); - std::string result = (pointerType.innerType->is(TypeVoid)) - ? "interop.Pointer | interop.Reference" - : "interop.Pointer | interop.Reference<" + - tsifyType(*pointerType.innerType) + ">"; + std::string result = + (pointerType.innerType->stripNullability()->is(TypeVoid)) + ? "interop.Pointer | interop.Reference" + : "interop.Pointer | interop.Reference<" + + tsifyType(*pointerType.innerType) + ">"; if (isFuncParam) { result += " | ArrayBufferLike | ArrayBufferView"; } @@ -1134,7 +1138,7 @@ std::string DefinitionWriter::tsifyType(const Type& type, return type.as().name; case TypeNullable: { const NullableType& nullableType = type.as(); - if (nullableType.innerType->is(TypePointer)) { + if (nullableType.innerType->stripNullability()->is(TypePointer)) { return tsifyType(*nullableType.innerType, isFuncParam); } return tsifyType(*nullableType.innerType, isFuncParam) + " | null"; @@ -1157,7 +1161,7 @@ std::string DefinitionWriter::tsifyType(const Type& type, std::string DefinitionWriter::computeMethodReturnType( const Type* retType, const BaseClassMeta* owner, bool instanceMember) { std::ostringstream output; - if (retType->is(TypeInstancetype)) { + if (retType->stripNullability()->is(TypeInstancetype)) { if (instanceMember) { output << "this"; } else { diff --git a/metadata-generator/src/Yaml/MetaYamlTraits.h b/metadata-generator/src/Yaml/MetaYamlTraits.h index 698cc183..fdc44fd1 100644 --- a/metadata-generator/src/Yaml/MetaYamlTraits.h +++ b/metadata-generator/src/Yaml/MetaYamlTraits.h @@ -5,17 +5,15 @@ namespace llvm { namespace yaml { - bool operator==(Meta::Version& x, const Meta::Version& y) - { - return x.Major == y.Major && x.Minor == y.Minor && x.SubMinor == y.SubMinor; - } - - Meta::MetaFlags operator|(Meta::MetaFlags& value1, Meta::MetaFlags value2) - { - return (Meta::MetaFlags)((uint32_t)value1 | (uint32_t)value2); - } +bool operator==(Meta::Version& x, const Meta::Version& y) { + return x.Major == y.Major && x.Minor == y.Minor && x.SubMinor == y.SubMinor; } + +Meta::MetaFlags operator|(Meta::MetaFlags& value1, Meta::MetaFlags value2) { + return (Meta::MetaFlags)((uint32_t)value1 | (uint32_t)value2); } +} // namespace yaml +} // namespace llvm #include @@ -31,561 +29,557 @@ LLVM_YAML_IS_SEQUENCE_VECTOR(Meta::EnumField) namespace llvm { namespace yaml { - // Version - template <> - struct ScalarTraits { - static void output(const Meta::Version& value, void* context, raw_ostream& out) - { - if (value.Major >= 0) { - out << value.Major; - if (value.Minor >= 0) { - out << "." << value.Minor; - if (value.SubMinor >= 0) { - out << "." << value.SubMinor; - } - } - } - } - - static StringRef input(StringRef stringValue, void* context, Meta::Version& value) - { - value = UNKNOWN_VERSION; - if (stringValue.size() == 0) { - return StringRef(); - } - std::string version = stringValue.str(); - - unsigned long firstDotIndex = version.find("."); - value.Major = (firstDotIndex != std::string::npos) ? std::stoi(version.substr(0, firstDotIndex)) : std::stoi(version); - if (firstDotIndex != std::string::npos) { - unsigned long secondDotIndex = version.find(".", firstDotIndex + 1); - value.Minor = std::stoi(version.substr(firstDotIndex + 1, (secondDotIndex != std::string::npos) ? secondDotIndex - firstDotIndex - 1 : std::string::npos)); - if (secondDotIndex != std::string::npos) { - value.SubMinor = std::stoi(version.substr(secondDotIndex + 1, std::string::npos)); - } - } - - // TODO: We can validate the version and return non-empty string if the yaml format of the version is invalid - return StringRef(); - } - // Determine if this scalar needs quotes. - static QuotingType mustQuote(StringRef) - { - return QuotingType::None; - } - }; - - // MetaFlags - template <> - struct ScalarBitSetTraits { - - static void bitset(IO& io, Meta::MetaFlags& value) - { - io.bitSetCase(value, "IsIosAppExtensionAvailable", Meta::MetaFlags::IsIosAppExtensionAvailable); - io.bitSetCase(value, "MemberIsOptional", Meta::MetaFlags::MemberIsOptional); - //io.bitSetCase(value, "HasName", Meta::MetaFlags::HasName); - - io.bitSetCase(value, "FunctionIsVariadic", Meta::MetaFlags::FunctionIsVariadic); - io.bitSetCase(value, "FunctionOwnsReturnedCocoaObject", Meta::MetaFlags::FunctionOwnsReturnedCocoaObject); - io.bitSetCase(value, "FunctionReturnsUnmanaged", Meta::MetaFlags::FunctionReturnsUnmanaged); - - io.bitSetCase(value, "MethodIsVariadic", Meta::MetaFlags::MethodIsVariadic); - io.bitSetCase(value, "MethodIsNullTerminatedVariadic", Meta::MetaFlags::MethodIsNullTerminatedVariadic); - io.bitSetCase(value, "MethodOwnsReturnedCocoaObject", Meta::MetaFlags::MethodOwnsReturnedCocoaObject); - io.bitSetCase(value, "MethodHasErrorOutParameter", Meta::MetaFlags::MethodHasErrorOutParameter); - io.bitSetCase(value, "MethodIsInitializer", Meta::MetaFlags::MethodIsInitializer); - } - }; - - // MetaType - template <> - struct ScalarEnumerationTraits { - static void enumeration(IO& io, Meta::MetaType& value) - { - io.enumCase(value, "Undefined", Meta::MetaType::Undefined); - io.enumCase(value, "Struct", Meta::MetaType::Struct); - io.enumCase(value, "Union", Meta::MetaType::Union); - io.enumCase(value, "Function", Meta::MetaType::Function); - io.enumCase(value, "Enum", Meta::MetaType::Enum); - io.enumCase(value, "EnumConstant", Meta::MetaType::EnumConstant); - io.enumCase(value, "Var", Meta::MetaType::Var); - io.enumCase(value, "Interface", Meta::MetaType::Interface); - io.enumCase(value, "Protocol", Meta::MetaType::Protocol); - io.enumCase(value, "Category", Meta::MetaType::Category); - io.enumCase(value, "Method", Meta::MetaType::Method); - io.enumCase(value, "Property", Meta::MetaType::Property); +// Version +template <> +struct ScalarTraits { + static void output(const Meta::Version& value, void* context, + raw_ostream& out) { + if (value.Major >= 0) { + out << value.Major; + if (value.Minor >= 0) { + out << "." << value.Minor; + if (value.SubMinor >= 0) { + out << "." << value.SubMinor; } - }; - - // TypeType - template <> - struct ScalarEnumerationTraits { - static void enumeration(IO& io, Meta::TypeType& value) - { - io.enumCase(value, "Void", Meta::TypeType::TypeVoid); - io.enumCase(value, "Bool", Meta::TypeType::TypeBool); - io.enumCase(value, "Short", Meta::TypeType::TypeShort); - io.enumCase(value, "Ushort", Meta::TypeType::TypeUShort); - io.enumCase(value, "Int", Meta::TypeType::TypeInt); - io.enumCase(value, "UInt", Meta::TypeType::TypeUInt); - io.enumCase(value, "Long", Meta::TypeType::TypeLong); - io.enumCase(value, "ULong", Meta::TypeType::TypeULong); - io.enumCase(value, "LongLong", Meta::TypeType::TypeLongLong); - io.enumCase(value, "ULongLong", Meta::TypeType::TypeULongLong); - io.enumCase(value, "Char", Meta::TypeType::TypeSignedChar); - io.enumCase(value, "UChar", Meta::TypeType::TypeUnsignedChar); - io.enumCase(value, "Unichar", Meta::TypeType::TypeUnichar); - io.enumCase(value, "CString", Meta::TypeType::TypeCString); - io.enumCase(value, "Float", Meta::TypeType::TypeFloat); - io.enumCase(value, "Double", Meta::TypeType::TypeDouble); - io.enumCase(value, "Selector", Meta::TypeType::TypeSelector); - io.enumCase(value, "Class", Meta::TypeType::TypeClass); - io.enumCase(value, "Instancetype", Meta::TypeType::TypeInstancetype); - io.enumCase(value, "Id", Meta::TypeType::TypeId); - io.enumCase(value, "ConstantArray", Meta::TypeType::TypeConstantArray); - io.enumCase(value, "IncompleteArray", Meta::TypeType::TypeIncompleteArray); - io.enumCase(value, "Interface", Meta::TypeType::TypeInterface); - io.enumCase(value, "BridgedInterface", Meta::TypeType::TypeBridgedInterface); - io.enumCase(value, "Pointer", Meta::TypeType::TypePointer); - io.enumCase(value, "FunctionPointer", Meta::TypeType::TypeFunctionPointer); - io.enumCase(value, "Block", Meta::TypeType::TypeBlock); - io.enumCase(value, "Struct", Meta::TypeType::TypeStruct); - io.enumCase(value, "Union", Meta::TypeType::TypeUnion); - io.enumCase(value, "AnonymousStruct", Meta::TypeType::TypeAnonymousStruct); - io.enumCase(value, "AnonymousUnion", Meta::TypeType::TypeAnonymousUnion); - io.enumCase(value, "Enum", Meta::TypeType::TypeEnum); - io.enumCase(value, "VaList", Meta::TypeType::TypeVaList); - io.enumCase(value, "Protocol", Meta::TypeType::TypeProtocol); - io.enumCase(value, "TypeArgument", Meta::TypeType::TypeTypeArgument); - } - }; - - // clang::Module::LinkLibrary - template <> - struct MappingTraits { - - static void mapping(IO& io, clang::Module::LinkLibrary& lib) - { - io.mapRequired("Library", lib.Library); - io.mapRequired("IsFramework", lib.IsFramework); - } - }; - - // clang::Module * - template <> - struct MappingTraits { - - static void mapping(IO& io, clang::Module*& module) - { - std::string fullModuleName = module->getFullModuleName(); - bool isPartOfFramework = module->isPartOfFramework(); - bool isSystem = module->IsSystem; - std::vector libs; - - Meta::Utils::getAllLinkLibraries(module, libs); - - io.mapRequired("FullName", fullModuleName); - io.mapRequired("IsPartOfFramework", isPartOfFramework); - io.mapRequired("IsSystemModule", isSystem); - io.mapRequired("Libraries", libs); - } - }; - - // std::pair> - template <> - struct MappingTraits > > { - - static void mapping(IO& io, std::pair >& module) - { - io.mapRequired("Module", module.first); - io.mapRequired("Items", module.second); - } - }; - - // Type * - template <> - struct MappingTraits { - - static void mapping(IO& io, Meta::Type*& type) - { - Meta::TypeType typeType = type->getType(); - io.mapRequired("Type", typeType); - - switch (typeType) { - case Meta::TypeType::TypeId: { - Meta::IdType& concreteType = type->as(); - std::vector protocols; - for (Meta::ProtocolMeta* protocol : concreteType.protocols) { - protocols.push_back(protocol->jsName); - } - io.mapRequired("WithProtocols", protocols); - break; - } - case Meta::TypeType::TypeConstantArray: { - Meta::ConstantArrayType& concreteType = type->as(); - io.mapRequired("ArrayType", concreteType.innerType); - io.mapRequired("Size", concreteType.size); - break; - } - case Meta::TypeType::TypeIncompleteArray: { - Meta::IncompleteArrayType& concreteType = type->as(); - io.mapRequired("ArrayType", concreteType.innerType); - break; - } - case Meta::TypeType::TypeInterface: { - Meta::InterfaceType& concreteType = type->as(); - io.mapRequired("Name", concreteType.interface->name); - if (concreteType.typeArguments.size() > 0) { - std::vector typeArguments; - for (Meta::Type* type : concreteType.typeArguments) { - typeArguments.push_back(type); - } - io.mapRequired("TypeParameters", typeArguments); - } - std::vector protocols; - for (Meta::ProtocolMeta* protocol : concreteType.protocols) { - protocols.push_back(protocol->jsName); - } - io.mapRequired("WithProtocols", protocols); - break; - } - case Meta::TypeType::TypeBridgedInterface: { - Meta::BridgedInterfaceType& concreteType = type->as(); - io.mapRequired("Name", concreteType.name); - std::string bridgedTo = concreteType.isId() ? "id" : (concreteType.bridgedInterface == nullptr ? "[None]" : concreteType.bridgedInterface->jsName); - io.mapRequired("BridgedTo", bridgedTo); - break; - } - case Meta::TypeType::TypePointer: { - Meta::PointerType& concreteType = type->as(); - io.mapRequired("PointerType", concreteType.innerType); - break; - } - case Meta::TypeType::TypeFunctionPointer: { - Meta::FunctionPointerType& concreteType = type->as(); - io.mapRequired("Signature", concreteType.signature); - break; - } - case Meta::TypeType::TypeBlock: { - Meta::BlockType& concreteType = type->as(); - io.mapRequired("Signature", concreteType.signature); - break; - } - case Meta::TypeType::TypeStruct: { - Meta::StructType& concreteType = type->as(); - std::string fullModuleName = concreteType.structMeta->module->getFullModuleName(); - io.mapRequired("Module", fullModuleName); - io.mapRequired("Name", concreteType.structMeta->name); - break; - } - case Meta::TypeType::TypeUnion: { - Meta::UnionType& concreteType = type->as(); - std::string fullModuleName = concreteType.unionMeta->module->getFullModuleName(); - io.mapRequired("Module", fullModuleName); - io.mapRequired("Name", concreteType.unionMeta->name); - break; - } - case Meta::TypeType::TypeAnonymousStruct: { - Meta::AnonymousStructType& concreteType = type->as(); - io.mapRequired("Fields", concreteType.fields); - break; - } - case Meta::TypeType::TypeAnonymousUnion: { - Meta::AnonymousUnionType& concreteType = type->as(); - io.mapRequired("Fields", concreteType.fields); - break; - } - case Meta::TypeType::TypeEnum: { - Meta::EnumType& concreteType = type->as(); - io.mapRequired("Name", concreteType.enumMeta->jsName); - break; - } - case Meta::TypeType::TypeTypeArgument: { - Meta::TypeArgumentType& concreteType = type->as(); - io.mapRequired("Name", concreteType.name); - io.mapRequired("UnderlyingType", concreteType.underlyingType); - std::vector protocols; - for (Meta::ProtocolMeta* protocol : concreteType.protocols) { - protocols.push_back(protocol->jsName); - } - if (protocols.size() > 0) { - io.mapRequired("WithProtocols", protocols); - } - break; - } - default: { - } - } - } - }; - - static void mapBaseMeta(IO& io, Meta::Meta* meta) - { - io.mapRequired("Name", meta->name); - io.mapRequired("JsName", meta->jsName); - if (!meta->demangledName.empty()) { - io.mapRequired("DemangledName", meta->demangledName); - } - io.mapRequired("Filename", meta->fileName); - io.mapRequired("Module", meta->module); - io.mapOptional("IntroducedIn", meta->introducedIn, (Meta::Version) UNKNOWN_VERSION); - io.mapRequired("Flags", meta->flags); - io.mapRequired("Type", meta->type); + } } + } - // MethodMeta * - template <> - struct MappingTraits { - - static void mapping(IO& io, Meta::MethodMeta*& meta) - { - mapBaseMeta(io, meta); - io.mapRequired("Signature", meta->signature); - } - }; - - // PropertyMeta * - template <> - struct MappingTraits { - - static void mapping(IO& io, Meta::PropertyMeta*& meta) - { - mapBaseMeta(io, meta); - - if (meta->getter) - io.mapRequired("Getter", meta->getter); - if (meta->setter) - io.mapRequired("Setter", meta->setter); - } - }; - - // BaseClassMeta * - template <> - struct MappingTraits { - - static void mapping(IO& io, Meta::BaseClassMeta*& meta) - { - mapBaseMeta(io, meta); - io.mapRequired("InstanceMethods", meta->instanceMethods); - io.mapRequired("StaticMethods", meta->staticMethods); - io.mapRequired("InstanceProperties", meta->instanceProperties); - io.mapRequired("StaticProperties", meta->staticProperties); - std::vector protocols; - for (Meta::ProtocolMeta* protocol : meta->protocols) { - protocols.push_back(protocol->jsName); - } - io.mapRequired("Protocols", protocols); - } - }; - - // FunctionMeta * - template <> - struct MappingTraits { - - static void mapping(IO& io, Meta::FunctionMeta*& meta) - { - mapBaseMeta(io, meta); - io.mapRequired("Signature", meta->signature); - } - }; - - // RecordField - template <> - struct MappingTraits { - - static void mapping(IO& io, Meta::RecordField& field) - { - io.mapRequired("Name", field.name); - io.mapRequired("Signature", field.encoding); - } - }; - - // RecordMeta * - template <> - struct MappingTraits { - - static void mapping(IO& io, Meta::RecordMeta*& meta) - { - mapBaseMeta(io, meta); - io.mapRequired("Fields", meta->fields); - } - }; - - // StructMeta * - template <> - struct MappingTraits { - - static void mapping(IO& io, Meta::StructMeta*& meta) - { - Meta::RecordMeta* recordMeta = &meta->as(); - MappingTraits::mapping(io, recordMeta); - } - }; - - // UnionMeta * - template <> - struct MappingTraits { - - static void mapping(IO& io, Meta::UnionMeta*& meta) - { - Meta::RecordMeta* recordMeta = &meta->as(); - MappingTraits::mapping(io, recordMeta); - } - }; - - // VarMeta * - template <> - struct MappingTraits { - - static void mapping(IO& io, Meta::VarMeta*& meta) - { - mapBaseMeta(io, meta); - io.mapRequired("Signature", meta->signature); - if (meta->hasValue) { - io.mapRequired("Value", meta->value); - } - } - }; - - // EnumField - template <> - struct MappingTraits { - - static void mapping(IO& io, Meta::EnumField& field) - { - io.mapRequired(field.name.c_str(), field.value); - } - }; - - // EnumMeta * - template <> - struct MappingTraits { - - static void mapping(IO& io, Meta::EnumMeta*& meta) - { - mapBaseMeta(io, meta); - io.mapRequired("FullNameFields", meta->fullNameFields); - io.mapRequired("SwiftNameFields", meta->swiftNameFields); - } - }; - - // EnumConstantMeta * - template <> - struct MappingTraits { + static StringRef input(StringRef stringValue, void* context, + Meta::Version& value) { + value = UNKNOWN_VERSION; + if (stringValue.size() == 0) { + return StringRef(); + } + std::string version = stringValue.str(); + + unsigned long firstDotIndex = version.find("."); + value.Major = (firstDotIndex != std::string::npos) + ? std::stoi(version.substr(0, firstDotIndex)) + : std::stoi(version); + if (firstDotIndex != std::string::npos) { + unsigned long secondDotIndex = version.find(".", firstDotIndex + 1); + value.Minor = std::stoi(version.substr( + firstDotIndex + 1, (secondDotIndex != std::string::npos) + ? secondDotIndex - firstDotIndex - 1 + : std::string::npos)); + if (secondDotIndex != std::string::npos) { + value.SubMinor = + std::stoi(version.substr(secondDotIndex + 1, std::string::npos)); + } + } - static void mapping(IO& io, Meta::EnumConstantMeta*& meta) - { - mapBaseMeta(io, meta); - io.mapRequired("Value", meta->value); + // TODO: We can validate the version and return non-empty string if the yaml + // format of the version is invalid + return StringRef(); + } + // Determine if this scalar needs quotes. + static QuotingType mustQuote(StringRef) { return QuotingType::None; } +}; + +// MetaFlags +template <> +struct ScalarBitSetTraits { + static void bitset(IO& io, Meta::MetaFlags& value) { + io.bitSetCase(value, "IsIosAppExtensionAvailable", + Meta::MetaFlags::IsIosAppExtensionAvailable); + io.bitSetCase(value, "MemberIsOptional", Meta::MetaFlags::MemberIsOptional); + // io.bitSetCase(value, "HasName", Meta::MetaFlags::HasName); + + io.bitSetCase(value, "FunctionIsVariadic", + Meta::MetaFlags::FunctionIsVariadic); + io.bitSetCase(value, "FunctionOwnsReturnedCocoaObject", + Meta::MetaFlags::FunctionOwnsReturnedCocoaObject); + io.bitSetCase(value, "FunctionReturnsUnmanaged", + Meta::MetaFlags::FunctionReturnsUnmanaged); + + io.bitSetCase(value, "MethodIsVariadic", Meta::MetaFlags::MethodIsVariadic); + io.bitSetCase(value, "MethodIsNullTerminatedVariadic", + Meta::MetaFlags::MethodIsNullTerminatedVariadic); + io.bitSetCase(value, "MethodOwnsReturnedCocoaObject", + Meta::MetaFlags::MethodOwnsReturnedCocoaObject); + io.bitSetCase(value, "MethodHasErrorOutParameter", + Meta::MetaFlags::MethodHasErrorOutParameter); + io.bitSetCase(value, "MethodIsInitializer", + Meta::MetaFlags::MethodIsInitializer); + } +}; + +// MetaType +template <> +struct ScalarEnumerationTraits { + static void enumeration(IO& io, Meta::MetaType& value) { + io.enumCase(value, "Undefined", Meta::MetaType::Undefined); + io.enumCase(value, "Struct", Meta::MetaType::Struct); + io.enumCase(value, "Union", Meta::MetaType::Union); + io.enumCase(value, "Function", Meta::MetaType::Function); + io.enumCase(value, "Enum", Meta::MetaType::Enum); + io.enumCase(value, "EnumConstant", Meta::MetaType::EnumConstant); + io.enumCase(value, "Var", Meta::MetaType::Var); + io.enumCase(value, "Interface", Meta::MetaType::Interface); + io.enumCase(value, "Protocol", Meta::MetaType::Protocol); + io.enumCase(value, "Category", Meta::MetaType::Category); + io.enumCase(value, "Method", Meta::MetaType::Method); + io.enumCase(value, "Property", Meta::MetaType::Property); + } +}; + +// TypeType +template <> +struct ScalarEnumerationTraits { + static void enumeration(IO& io, Meta::TypeType& value) { + io.enumCase(value, "Void", Meta::TypeType::TypeVoid); + io.enumCase(value, "Bool", Meta::TypeType::TypeBool); + io.enumCase(value, "Short", Meta::TypeType::TypeShort); + io.enumCase(value, "Ushort", Meta::TypeType::TypeUShort); + io.enumCase(value, "Int", Meta::TypeType::TypeInt); + io.enumCase(value, "UInt", Meta::TypeType::TypeUInt); + io.enumCase(value, "Long", Meta::TypeType::TypeLong); + io.enumCase(value, "ULong", Meta::TypeType::TypeULong); + io.enumCase(value, "LongLong", Meta::TypeType::TypeLongLong); + io.enumCase(value, "ULongLong", Meta::TypeType::TypeULongLong); + io.enumCase(value, "Char", Meta::TypeType::TypeSignedChar); + io.enumCase(value, "UChar", Meta::TypeType::TypeUnsignedChar); + io.enumCase(value, "Unichar", Meta::TypeType::TypeUnichar); + io.enumCase(value, "CString", Meta::TypeType::TypeCString); + io.enumCase(value, "Float", Meta::TypeType::TypeFloat); + io.enumCase(value, "Double", Meta::TypeType::TypeDouble); + io.enumCase(value, "Selector", Meta::TypeType::TypeSelector); + io.enumCase(value, "Class", Meta::TypeType::TypeClass); + io.enumCase(value, "Instancetype", Meta::TypeType::TypeInstancetype); + io.enumCase(value, "Id", Meta::TypeType::TypeId); + io.enumCase(value, "ConstantArray", Meta::TypeType::TypeConstantArray); + io.enumCase(value, "IncompleteArray", Meta::TypeType::TypeIncompleteArray); + io.enumCase(value, "Interface", Meta::TypeType::TypeInterface); + io.enumCase(value, "BridgedInterface", + Meta::TypeType::TypeBridgedInterface); + io.enumCase(value, "Pointer", Meta::TypeType::TypePointer); + io.enumCase(value, "FunctionPointer", Meta::TypeType::TypeFunctionPointer); + io.enumCase(value, "Block", Meta::TypeType::TypeBlock); + io.enumCase(value, "Struct", Meta::TypeType::TypeStruct); + io.enumCase(value, "Union", Meta::TypeType::TypeUnion); + io.enumCase(value, "AnonymousStruct", Meta::TypeType::TypeAnonymousStruct); + io.enumCase(value, "AnonymousUnion", Meta::TypeType::TypeAnonymousUnion); + io.enumCase(value, "Enum", Meta::TypeType::TypeEnum); + io.enumCase(value, "VaList", Meta::TypeType::TypeVaList); + io.enumCase(value, "Protocol", Meta::TypeType::TypeProtocol); + io.enumCase(value, "TypeArgument", Meta::TypeType::TypeTypeArgument); + io.enumCase(value, "ExtVector", Meta::TypeType::TypeExtVector); + io.enumCase(value, "Nullable", Meta::TypeType::TypeNullable); + io.enumCase(value, "NonNullable", Meta::TypeType::TypeNonNullable); + } +}; + +// clang::Module::LinkLibrary +template <> +struct MappingTraits { + static void mapping(IO& io, clang::Module::LinkLibrary& lib) { + io.mapRequired("Library", lib.Library); + io.mapRequired("IsFramework", lib.IsFramework); + } +}; + +// clang::Module * +template <> +struct MappingTraits { + static void mapping(IO& io, clang::Module*& module) { + std::string fullModuleName = module->getFullModuleName(); + bool isPartOfFramework = module->isPartOfFramework(); + bool isSystem = module->IsSystem; + std::vector libs; + + Meta::Utils::getAllLinkLibraries(module, libs); + + io.mapRequired("FullName", fullModuleName); + io.mapRequired("IsPartOfFramework", isPartOfFramework); + io.mapRequired("IsSystemModule", isSystem); + io.mapRequired("Libraries", libs); + } +}; + +// std::pair> +template <> +struct MappingTraits > > { + static void mapping( + IO& io, std::pair >& module) { + io.mapRequired("Module", module.first); + io.mapRequired("Items", module.second); + } +}; + +// Type * +template <> +struct MappingTraits { + static void mapping(IO& io, Meta::Type*& type) { + Meta::TypeType typeType = type->getType(); + io.mapRequired("Type", typeType); + + switch (typeType) { + case Meta::TypeType::TypeId: { + Meta::IdType& concreteType = type->as(); + std::vector protocols; + for (Meta::ProtocolMeta* protocol : concreteType.protocols) { + protocols.push_back(protocol->jsName); } - }; - - // InterfaceMeta * - template <> - struct MappingTraits { - - static void mapping(IO& io, Meta::InterfaceMeta*& meta) - { - Meta::BaseClassMeta* baseClassMeta = &meta->as(); - MappingTraits::mapping(io, baseClassMeta); - if (meta->base != nullptr) { - io.mapRequired("Base", meta->base->jsName); - } + io.mapRequired("WithProtocols", protocols); + break; + } + case Meta::TypeType::TypeConstantArray: { + Meta::ConstantArrayType& concreteType = + type->as(); + io.mapRequired("ArrayType", concreteType.innerType); + io.mapRequired("Size", concreteType.size); + break; + } + case Meta::TypeType::TypeIncompleteArray: { + Meta::IncompleteArrayType& concreteType = + type->as(); + io.mapRequired("ArrayType", concreteType.innerType); + break; + } + case Meta::TypeType::TypeInterface: { + Meta::InterfaceType& concreteType = type->as(); + io.mapRequired("Name", concreteType.interface->name); + if (concreteType.typeArguments.size() > 0) { + std::vector typeArguments; + for (Meta::Type* type : concreteType.typeArguments) { + typeArguments.push_back(type); + } + io.mapRequired("TypeParameters", typeArguments); } - }; - - // ProtocolMeta * - template <> - struct MappingTraits { - - static void mapping(IO& io, Meta::ProtocolMeta*& meta) - { - Meta::BaseClassMeta* baseClassMeta = &meta->as(); - MappingTraits::mapping(io, baseClassMeta); + std::vector protocols; + for (Meta::ProtocolMeta* protocol : concreteType.protocols) { + protocols.push_back(protocol->jsName); } - }; - - // CategoryMeta * - template <> - struct MappingTraits { - - static void mapping(IO& io, Meta::CategoryMeta*& meta) - { - Meta::BaseClassMeta* baseClassMeta = &meta->as(); - MappingTraits::mapping(io, baseClassMeta); - io.mapRequired("ExtendedInterface", meta->extendedInterface); + io.mapRequired("WithProtocols", protocols); + break; + } + case Meta::TypeType::TypeBridgedInterface: { + Meta::BridgedInterfaceType& concreteType = + type->as(); + io.mapRequired("Name", concreteType.name); + std::string bridgedTo = + concreteType.isId() ? "id" + : (concreteType.bridgedInterface == nullptr + ? "[None]" + : concreteType.bridgedInterface->jsName); + io.mapRequired("BridgedTo", bridgedTo); + break; + } + case Meta::TypeType::TypePointer: { + Meta::PointerType& concreteType = type->as(); + io.mapRequired("PointerType", concreteType.innerType); + break; + } + case Meta::TypeType::TypeFunctionPointer: { + Meta::FunctionPointerType& concreteType = + type->as(); + io.mapRequired("Signature", concreteType.signature); + break; + } + case Meta::TypeType::TypeBlock: { + Meta::BlockType& concreteType = type->as(); + io.mapRequired("Signature", concreteType.signature); + break; + } + case Meta::TypeType::TypeStruct: { + Meta::StructType& concreteType = type->as(); + std::string fullModuleName = + concreteType.structMeta->module->getFullModuleName(); + io.mapRequired("Module", fullModuleName); + io.mapRequired("Name", concreteType.structMeta->name); + break; + } + case Meta::TypeType::TypeUnion: { + Meta::UnionType& concreteType = type->as(); + std::string fullModuleName = + concreteType.unionMeta->module->getFullModuleName(); + io.mapRequired("Module", fullModuleName); + io.mapRequired("Name", concreteType.unionMeta->name); + break; + } + case Meta::TypeType::TypeAnonymousStruct: { + Meta::AnonymousStructType& concreteType = + type->as(); + io.mapRequired("Fields", concreteType.fields); + break; + } + case Meta::TypeType::TypeAnonymousUnion: { + Meta::AnonymousUnionType& concreteType = + type->as(); + io.mapRequired("Fields", concreteType.fields); + break; + } + case Meta::TypeType::TypeEnum: { + Meta::EnumType& concreteType = type->as(); + io.mapRequired("Name", concreteType.enumMeta->jsName); + break; + } + case Meta::TypeType::TypeTypeArgument: { + Meta::TypeArgumentType& concreteType = + type->as(); + io.mapRequired("Name", concreteType.name); + io.mapRequired("UnderlyingType", concreteType.underlyingType); + std::vector protocols; + for (Meta::ProtocolMeta* protocol : concreteType.protocols) { + protocols.push_back(protocol->jsName); } - }; - - // Meta * - // These traits check which is the actual run-time type of the meta and forward to the corresponding traits. - template <> - struct MappingTraits { - - static void mapping(IO& io, Meta::Meta*& meta) - { - switch (meta->type) { - case Meta::MetaType::Function: { - Meta::FunctionMeta* functionMeta = &meta->as(); - MappingTraits::mapping(io, functionMeta); - break; - } - case Meta::MetaType::Struct: { - Meta::StructMeta* structMeta = &meta->as(); - MappingTraits::mapping(io, structMeta); - break; - } - case Meta::MetaType::Union: { - Meta::UnionMeta* unionMeta = &meta->as(); - MappingTraits::mapping(io, unionMeta); - break; - } - case Meta::MetaType::Var: { - Meta::VarMeta* varMeta = &meta->as(); - MappingTraits::mapping(io, varMeta); - break; - } - case Meta::MetaType::Enum: { - Meta::EnumMeta* enumMeta = &meta->as(); - MappingTraits::mapping(io, enumMeta); - break; - } - case Meta::MetaType::EnumConstant: { - Meta::EnumConstantMeta* enumConstantMeta = &meta->as(); - MappingTraits::mapping(io, enumConstantMeta); - break; - } - case Meta::MetaType::Interface: { - Meta::InterfaceMeta* interfaceMeta = &meta->as(); - MappingTraits::mapping(io, interfaceMeta); - break; - } - case Meta::MetaType::Protocol: { - Meta::ProtocolMeta* protocolMeta = &meta->as(); - MappingTraits::mapping(io, protocolMeta); - break; - } - case Meta::MetaType::Category: { - Meta::CategoryMeta* categoryMeta = &meta->as(); - MappingTraits::mapping(io, categoryMeta); - break; - } - case Meta::MetaType::Method: { - Meta::MethodMeta* methodMeta = &meta->as(); - MappingTraits::mapping(io, methodMeta); - break; - } - case Meta::MetaType::Property: { - Meta::PropertyMeta* propertyMeta = &meta->as(); - MappingTraits::mapping(io, propertyMeta); - break; - } - case Meta::MetaType::Undefined: - default: { - throw std::runtime_error("Unknown type of meta object."); - } - } + if (protocols.size() > 0) { + io.mapRequired("WithProtocols", protocols); } - }; -} + break; + } + case Meta::TypeType::TypeNullable: { + Meta::NullableType& concreteType = type->as(); + io.mapRequired("InnerType", concreteType.innerType); + break; + } + case Meta::TypeType::TypeNonNullable: { + Meta::NonNullableType& concreteType = type->as(); + io.mapRequired("InnerType", concreteType.innerType); + break; + } + default: { + } + } + } +}; + +static void mapBaseMeta(IO& io, Meta::Meta* meta) { + io.mapRequired("Name", meta->name); + io.mapRequired("JsName", meta->jsName); + if (!meta->demangledName.empty()) { + io.mapRequired("DemangledName", meta->demangledName); + } + io.mapRequired("Filename", meta->fileName); + io.mapRequired("Module", meta->module); + io.mapOptional("IntroducedIn", meta->introducedIn, + (Meta::Version)UNKNOWN_VERSION); + io.mapRequired("Flags", meta->flags); + io.mapRequired("Type", meta->type); } + +// MethodMeta * +template <> +struct MappingTraits { + static void mapping(IO& io, Meta::MethodMeta*& meta) { + mapBaseMeta(io, meta); + io.mapRequired("Signature", meta->signature); + } +}; + +// PropertyMeta * +template <> +struct MappingTraits { + static void mapping(IO& io, Meta::PropertyMeta*& meta) { + mapBaseMeta(io, meta); + + if (meta->getter) io.mapRequired("Getter", meta->getter); + if (meta->setter) io.mapRequired("Setter", meta->setter); + } +}; + +// BaseClassMeta * +template <> +struct MappingTraits { + static void mapping(IO& io, Meta::BaseClassMeta*& meta) { + mapBaseMeta(io, meta); + io.mapRequired("InstanceMethods", meta->instanceMethods); + io.mapRequired("StaticMethods", meta->staticMethods); + io.mapRequired("InstanceProperties", meta->instanceProperties); + io.mapRequired("StaticProperties", meta->staticProperties); + std::vector protocols; + for (Meta::ProtocolMeta* protocol : meta->protocols) { + protocols.push_back(protocol->jsName); + } + io.mapRequired("Protocols", protocols); + } +}; + +// FunctionMeta * +template <> +struct MappingTraits { + static void mapping(IO& io, Meta::FunctionMeta*& meta) { + mapBaseMeta(io, meta); + io.mapRequired("Signature", meta->signature); + } +}; + +// RecordField +template <> +struct MappingTraits { + static void mapping(IO& io, Meta::RecordField& field) { + io.mapRequired("Name", field.name); + io.mapRequired("Signature", field.encoding); + } +}; + +// RecordMeta * +template <> +struct MappingTraits { + static void mapping(IO& io, Meta::RecordMeta*& meta) { + mapBaseMeta(io, meta); + io.mapRequired("Fields", meta->fields); + } +}; + +// StructMeta * +template <> +struct MappingTraits { + static void mapping(IO& io, Meta::StructMeta*& meta) { + Meta::RecordMeta* recordMeta = &meta->as(); + MappingTraits::mapping(io, recordMeta); + } +}; + +// UnionMeta * +template <> +struct MappingTraits { + static void mapping(IO& io, Meta::UnionMeta*& meta) { + Meta::RecordMeta* recordMeta = &meta->as(); + MappingTraits::mapping(io, recordMeta); + } +}; + +// VarMeta * +template <> +struct MappingTraits { + static void mapping(IO& io, Meta::VarMeta*& meta) { + mapBaseMeta(io, meta); + io.mapRequired("Signature", meta->signature); + if (meta->hasValue) { + io.mapRequired("Value", meta->value); + } + } +}; + +// EnumField +template <> +struct MappingTraits { + static void mapping(IO& io, Meta::EnumField& field) { + io.mapRequired(field.name.c_str(), field.value); + } +}; + +// EnumMeta * +template <> +struct MappingTraits { + static void mapping(IO& io, Meta::EnumMeta*& meta) { + mapBaseMeta(io, meta); + io.mapRequired("FullNameFields", meta->fullNameFields); + io.mapRequired("SwiftNameFields", meta->swiftNameFields); + } +}; + +// EnumConstantMeta * +template <> +struct MappingTraits { + static void mapping(IO& io, Meta::EnumConstantMeta*& meta) { + mapBaseMeta(io, meta); + io.mapRequired("Value", meta->value); + } +}; + +// InterfaceMeta * +template <> +struct MappingTraits { + static void mapping(IO& io, Meta::InterfaceMeta*& meta) { + Meta::BaseClassMeta* baseClassMeta = &meta->as(); + MappingTraits::mapping(io, baseClassMeta); + if (meta->base != nullptr) { + io.mapRequired("Base", meta->base->jsName); + } + } +}; + +// ProtocolMeta * +template <> +struct MappingTraits { + static void mapping(IO& io, Meta::ProtocolMeta*& meta) { + Meta::BaseClassMeta* baseClassMeta = &meta->as(); + MappingTraits::mapping(io, baseClassMeta); + } +}; + +// CategoryMeta * +template <> +struct MappingTraits { + static void mapping(IO& io, Meta::CategoryMeta*& meta) { + Meta::BaseClassMeta* baseClassMeta = &meta->as(); + MappingTraits::mapping(io, baseClassMeta); + io.mapRequired("ExtendedInterface", meta->extendedInterface); + } +}; + +// Meta * +// These traits check which is the actual run-time type of the meta and forward +// to the corresponding traits. +template <> +struct MappingTraits { + static void mapping(IO& io, Meta::Meta*& meta) { + switch (meta->type) { + case Meta::MetaType::Function: { + Meta::FunctionMeta* functionMeta = &meta->as(); + MappingTraits::mapping(io, functionMeta); + break; + } + case Meta::MetaType::Struct: { + Meta::StructMeta* structMeta = &meta->as(); + MappingTraits::mapping(io, structMeta); + break; + } + case Meta::MetaType::Union: { + Meta::UnionMeta* unionMeta = &meta->as(); + MappingTraits::mapping(io, unionMeta); + break; + } + case Meta::MetaType::Var: { + Meta::VarMeta* varMeta = &meta->as(); + MappingTraits::mapping(io, varMeta); + break; + } + case Meta::MetaType::Enum: { + Meta::EnumMeta* enumMeta = &meta->as(); + MappingTraits::mapping(io, enumMeta); + break; + } + case Meta::MetaType::EnumConstant: { + Meta::EnumConstantMeta* enumConstantMeta = + &meta->as(); + MappingTraits::mapping(io, enumConstantMeta); + break; + } + case Meta::MetaType::Interface: { + Meta::InterfaceMeta* interfaceMeta = &meta->as(); + MappingTraits::mapping(io, interfaceMeta); + break; + } + case Meta::MetaType::Protocol: { + Meta::ProtocolMeta* protocolMeta = &meta->as(); + MappingTraits::mapping(io, protocolMeta); + break; + } + case Meta::MetaType::Category: { + Meta::CategoryMeta* categoryMeta = &meta->as(); + MappingTraits::mapping(io, categoryMeta); + break; + } + case Meta::MetaType::Method: { + Meta::MethodMeta* methodMeta = &meta->as(); + MappingTraits::mapping(io, methodMeta); + break; + } + case Meta::MetaType::Property: { + Meta::PropertyMeta* propertyMeta = &meta->as(); + MappingTraits::mapping(io, propertyMeta); + break; + } + case Meta::MetaType::Undefined: + default: { + throw std::runtime_error("Unknown type of meta object."); + } + } + } +}; +} // namespace yaml +} // namespace llvm From 885797cddd83d558373ca906d0b32089cb57071e Mon Sep 17 00:00:00 2001 From: Eduardo Speroni Date: Fri, 12 Jun 2026 15:21:39 -0300 Subject: [PATCH 2/2] fix(metadata-generator): serialize ExtVector inner type and size to YAML The previous commit added ExtVector to the TypeType enum mapping but not to the structural switch in MappingTraits, so ext-vector types emitted only 'Type: ExtVector' with their element type and size dropped. Mirror the TypeConstantArray style (ArrayType + Size). Addresses PR review feedback on #389. --- metadata-generator/src/Yaml/MetaYamlTraits.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/metadata-generator/src/Yaml/MetaYamlTraits.h b/metadata-generator/src/Yaml/MetaYamlTraits.h index fdc44fd1..e9d07383 100644 --- a/metadata-generator/src/Yaml/MetaYamlTraits.h +++ b/metadata-generator/src/Yaml/MetaYamlTraits.h @@ -329,6 +329,12 @@ struct MappingTraits { } break; } + case Meta::TypeType::TypeExtVector: { + Meta::ExtVectorType& concreteType = type->as(); + io.mapRequired("ArrayType", concreteType.innerType); + io.mapRequired("Size", concreteType.size); + break; + } case Meta::TypeType::TypeNullable: { Meta::NullableType& concreteType = type->as(); io.mapRequired("InnerType", concreteType.innerType);