From b0c806fdfb97f8a2217ce9b51eb30a8136710af6 Mon Sep 17 00:00:00 2001 From: Nike Date: Wed, 18 Dec 2024 14:32:09 -0500 Subject: [PATCH 1/3] Initial commit for branch --- .../parse_compound_declaration.dart | 10 ++++++++++ .../lib/src/parser/parsers/parse_declarations.dart | 1 + 2 files changed, 11 insertions(+) diff --git a/pkgs/swift2objc/lib/src/parser/parsers/declaration_parsers/parse_compound_declaration.dart b/pkgs/swift2objc/lib/src/parser/parsers/declaration_parsers/parse_compound_declaration.dart index cf3e9a27a..0940833db 100644 --- a/pkgs/swift2objc/lib/src/parser/parsers/declaration_parsers/parse_compound_declaration.dart +++ b/pkgs/swift2objc/lib/src/parser/parsers/declaration_parsers/parse_compound_declaration.dart @@ -8,6 +8,7 @@ import '../../../ast/declarations/compounds/class_declaration.dart'; import '../../../ast/declarations/compounds/members/initializer_declaration.dart'; import '../../../ast/declarations/compounds/members/method_declaration.dart'; import '../../../ast/declarations/compounds/members/property_declaration.dart'; +import '../../../ast/declarations/compounds/protocol_declaration.dart'; import '../../../ast/declarations/compounds/struct_declaration.dart'; import '../../_core/parsed_symbolgraph.dart'; import '../../_core/utils.dart'; @@ -107,3 +108,12 @@ StructDeclaration parseStructDeclaration( symbolgraph, ); } + +// This won't work as there's more for a protocol declaration +// Placing this here as placeholder declaration +ProtocolDeclaration parseProtocolDeclaration( + ParsedSymbol protocolSymbol, + ParsedSymbolgraph symbolgraph +) { + return _parseCompoundDeclaration(protocolSymbol, ProtocolDeclaration.new, symbolgraph); +} \ No newline at end of file diff --git a/pkgs/swift2objc/lib/src/parser/parsers/parse_declarations.dart b/pkgs/swift2objc/lib/src/parser/parsers/parse_declarations.dart index 36b74c4f4..ff2bf50eb 100644 --- a/pkgs/swift2objc/lib/src/parser/parsers/parse_declarations.dart +++ b/pkgs/swift2objc/lib/src/parser/parsers/parse_declarations.dart @@ -56,6 +56,7 @@ Declaration parseDeclaration( 'swift.init' => parseInitializerDeclaration(symbolJson, symbolgraph), 'swift.func' => parseGlobalFunctionDeclaration(symbolJson, symbolgraph), 'swift.var' => parseGlobalVariableDeclaration(symbolJson, symbolgraph), + 'swift.protocol' => parseProtocolDeclaration(parsedSymbol, symbolgraph), _ => throw Exception( 'Symbol of type $symbolType is not implemented yet.', ), From 6f6d50f56df6c50fc7cf68b65dfb942cac8255ef Mon Sep 17 00:00:00 2001 From: Nike Date: Wed, 18 Dec 2024 15:21:29 -0500 Subject: [PATCH 2/3] implemented parsing basic protocols with inheritance (conformance) --- .../compounds/protocol_declaration.dart | 17 ++- .../src/parser/_core/parsed_symbolgraph.dart | 4 + .../parse_compound_declaration.dart | 119 +++++++++++++++++- 3 files changed, 138 insertions(+), 2 deletions(-) diff --git a/pkgs/swift2objc/lib/src/ast/declarations/compounds/protocol_declaration.dart b/pkgs/swift2objc/lib/src/ast/declarations/compounds/protocol_declaration.dart index 197b5ee6d..28e6c3da5 100644 --- a/pkgs/swift2objc/lib/src/ast/declarations/compounds/protocol_declaration.dart +++ b/pkgs/swift2objc/lib/src/ast/declarations/compounds/protocol_declaration.dart @@ -4,13 +4,14 @@ import '../../_core/interfaces/compound_declaration.dart'; import '../../_core/interfaces/nestable_declaration.dart'; +import '../../_core/interfaces/objc_annotatable.dart'; import '../../_core/shared/referred_type.dart'; import 'members/initializer_declaration.dart'; import 'members/method_declaration.dart'; import 'members/property_declaration.dart'; /// Describes the declaration of a Swift protocol. -class ProtocolDeclaration implements CompoundDeclaration { +class ProtocolDeclaration implements CompoundDeclaration, ObjCAnnotatable { @override String id; @@ -23,9 +24,20 @@ class ProtocolDeclaration implements CompoundDeclaration { @override covariant List methods; + /// Only present if indicated with `@objc` + @override + List optionalProperties; + + /// Only present if indicated with `@objc` + @override + List optionalMethods; + @override List> conformedProtocols; + @override + bool hasObjCAnnotation; + @override List typeParams; @@ -46,7 +58,10 @@ class ProtocolDeclaration implements CompoundDeclaration { required this.initializers, required this.conformedProtocols, required this.typeParams, + this.hasObjCAnnotation = false, this.nestingParent, this.nestedDeclarations = const [], + this.optionalMethods = const [], + this.optionalProperties = const [], }); } diff --git a/pkgs/swift2objc/lib/src/parser/_core/parsed_symbolgraph.dart b/pkgs/swift2objc/lib/src/parser/_core/parsed_symbolgraph.dart index e98010719..88684dc66 100644 --- a/pkgs/swift2objc/lib/src/parser/_core/parsed_symbolgraph.dart +++ b/pkgs/swift2objc/lib/src/parser/_core/parsed_symbolgraph.dart @@ -35,6 +35,10 @@ class ParsedRelation { } enum ParsedRelationKind { + requirementOf, + defaultImplementationOf, + optionalRequirementOf, + conformsTo, memberOf; static final _supportedRelationKindsMap = { diff --git a/pkgs/swift2objc/lib/src/parser/parsers/declaration_parsers/parse_compound_declaration.dart b/pkgs/swift2objc/lib/src/parser/parsers/declaration_parsers/parse_compound_declaration.dart index 0940833db..27ecfa3f4 100644 --- a/pkgs/swift2objc/lib/src/parser/parsers/declaration_parsers/parse_compound_declaration.dart +++ b/pkgs/swift2objc/lib/src/parser/parsers/declaration_parsers/parse_compound_declaration.dart @@ -3,6 +3,7 @@ // BSD-style license that can be found in the LICENSE file. import '../../../ast/_core/interfaces/compound_declaration.dart'; +import '../../../ast/_core/interfaces/declaration.dart'; import '../../../ast/_core/interfaces/nestable_declaration.dart'; import '../../../ast/declarations/compounds/class_declaration.dart'; import '../../../ast/declarations/compounds/members/initializer_declaration.dart'; @@ -111,9 +112,125 @@ StructDeclaration parseStructDeclaration( // This won't work as there's more for a protocol declaration // Placing this here as placeholder declaration +// TODO: Implement extensions before adding support for default implementations + // 5. protocol func with reimpl in extension -> requirementOf + // and defaultImplementationOf, swift.method + // 6. protocol var with reimpl in extension -> requirementOf + // and defaultImplementationOf, swift.property, +// TODO: Replace generics to associatedType and implement ProtocolDeclaration parseProtocolDeclaration( ParsedSymbol protocolSymbol, ParsedSymbolgraph symbolgraph ) { - return _parseCompoundDeclaration(protocolSymbol, ProtocolDeclaration.new, symbolgraph); + final compoundId = parseSymbolId(protocolSymbol.json); + final compoundRelations = symbolgraph.relations[compoundId] ?? []; + + // construct protocol + final protocol = ProtocolDeclaration( + id: compoundId, name: parseSymbolName(protocolSymbol.json), + properties: [], + methods: [], + initializers: [], + conformedProtocols: [], + typeParams: [] + ); + + // get optional member declarations if any + final optionalMemberDeclarations = compoundRelations.where((relation) { + final isOptionalRequirementRelation = + relation.kind == ParsedRelationKind.optionalRequirementOf; + final isMemberOfProtocol = relation.targetId == compoundId; + return isMemberOfProtocol && isOptionalRequirementRelation; + }).map((relation) { + final memberSymbol = symbolgraph.symbols[relation.sourceId]; + if (memberSymbol == null) { + return null; + } + return tryParseDeclaration(memberSymbol, symbolgraph); + }) + .nonNulls + .dedupeBy((decl) => decl.id) + .toList() + ; + + // get normal member declarations + final memberDeclarations = compoundRelations.where((relation) { + final isOptionalRequirementRelation = + relation.kind == ParsedRelationKind.requirementOf + && relation.kind != ParsedRelationKind.optionalRequirementOf; + final isMemberOfProtocol = relation.targetId == compoundId; + return isMemberOfProtocol && isOptionalRequirementRelation; + }).map((relation) { + final memberSymbol = symbolgraph.symbols[relation.sourceId]; + if (memberSymbol == null) { + return null; + } + return tryParseDeclaration(memberSymbol, symbolgraph); + }) + .nonNulls + .dedupeBy((decl) => decl.id) + .toList() + ; + + // get conformed protocols + final conformedProtocolDeclarations = compoundRelations.where((relation) { + final isOptionalRequirementRelation = + relation.kind == ParsedRelationKind.conformsTo; + final isMemberOfProtocol = relation.targetId == compoundId; + return isMemberOfProtocol && isOptionalRequirementRelation; + }).map((relation) { + final memberSymbol = symbolgraph.symbols[relation.sourceId]; + if (memberSymbol == null) { + return null; + } + var conformedDecl = tryParseDeclaration(memberSymbol, symbolgraph) as ProtocolDeclaration; + return conformedDecl.asDeclaredType; + }) + .nonNulls + .dedupeBy((decl) => decl.id) + .toList() + ; + + // If the protocol has optional members, it must be annotated with `@objc` + if (optionalMemberDeclarations.isNotEmpty) { + protocol.hasObjCAnnotation = true; + } + + protocol.methods.addAll( + memberDeclarations + .whereType() + .dedupeBy((m) => m.fullName), + ); + + protocol.properties.addAll( + memberDeclarations.whereType(), + ); + + protocol.optionalMethods.addAll( + optionalMemberDeclarations + .whereType() + .dedupeBy((m) => m.fullName), + ); + + protocol.optionalProperties.addAll( + optionalMemberDeclarations.whereType(), + ); + + protocol.conformedProtocols.addAll( + conformedProtocolDeclarations + ); + + protocol.initializers.addAll( + memberDeclarations + .whereType() + .dedupeBy((m) => m.fullName), + ); + + protocol.nestedDeclarations.addAll( + memberDeclarations.whereType(), + ); + + protocol.nestedDeclarations.fillNestingParents(protocol); + + return protocol; } \ No newline at end of file From fa00ec49c089c38fe92f8a360703b7c0017ebd7b Mon Sep 17 00:00:00 2001 From: Nike Okoronkwo Date: Sat, 18 Jan 2025 22:16:45 -0600 Subject: [PATCH 3/3] added support for associated type --- .../src/ast/_core/shared/referred_type.dart | 27 +++++++++++++++++++ .../members/associated_type_declaration.dart | 25 +++++++++++++++++ .../compounds/protocol_declaration.dart | 7 +++++ .../parse_associated_type_declaration.dart | 15 +++++++++++ .../parse_compound_declaration.dart | 10 +++++++ .../parser/parsers/parse_declarations.dart | 5 ++++ .../lib/src/parser/parsers/parse_type.dart | 1 + 7 files changed, 90 insertions(+) create mode 100644 pkgs/swift2objc/lib/src/ast/declarations/compounds/members/associated_type_declaration.dart create mode 100644 pkgs/swift2objc/lib/src/parser/parsers/declaration_parsers/parse_associated_type_declaration.dart diff --git a/pkgs/swift2objc/lib/src/ast/_core/shared/referred_type.dart b/pkgs/swift2objc/lib/src/ast/_core/shared/referred_type.dart index 9bbd4080b..9cedb8f4a 100644 --- a/pkgs/swift2objc/lib/src/ast/_core/shared/referred_type.dart +++ b/pkgs/swift2objc/lib/src/ast/_core/shared/referred_type.dart @@ -3,6 +3,7 @@ // BSD-style license that can be found in the LICENSE file. import '../../ast_node.dart'; +import '../../declarations/compounds/protocol_declaration.dart'; import '../interfaces/declaration.dart'; import '../interfaces/nestable_declaration.dart'; import '../interfaces/objc_annotatable.dart'; @@ -71,6 +72,7 @@ class DeclaredType extends AstNode /// Describes a reference of a generic type /// (e.g a method return type `T` within a generic class). +/// TODO(): Add Type Constrains and extend to support associated types class GenericType extends AstNode implements ReferredType { final String id; @@ -97,6 +99,31 @@ class GenericType extends AstNode implements ReferredType { void visit(Visitation visitation) => visitation.visitGenericType(this); } +class AssociatedType extends AstNode implements ReferredType { + final String id; + + final String name; + + + @override + bool get isObjCRepresentable => false; + + @override + String get swiftType => name; + + @override + bool sameAs(ReferredType other) => other is AssociatedType && other.id == id; + + AssociatedType({ + required this.id, + required this.name, + }); + + @override + String toString() => name; +} + + /// An optional type, like Dart's nullable types. Eg `String?`. class OptionalType extends AstNode implements ReferredType { final ReferredType child; diff --git a/pkgs/swift2objc/lib/src/ast/declarations/compounds/members/associated_type_declaration.dart b/pkgs/swift2objc/lib/src/ast/declarations/compounds/members/associated_type_declaration.dart new file mode 100644 index 000000000..58cf08d72 --- /dev/null +++ b/pkgs/swift2objc/lib/src/ast/declarations/compounds/members/associated_type_declaration.dart @@ -0,0 +1,25 @@ + +import '../../../_core/interfaces/declaration.dart'; +import '../../../_core/shared/referred_type.dart'; +import '../../../ast_node.dart'; +import '../protocol_declaration.dart'; + + +class AssociatedTypeDeclaration extends AstNode implements Declaration { + @override + String id; + + @override + String name; + + AssociatedTypeDeclaration({ + required this.id, + required this.name, + }); +} + +extension AsAssociatedType on T { + AssociatedType asType() => AssociatedType( + id: id, name: name + ); +} \ No newline at end of file diff --git a/pkgs/swift2objc/lib/src/ast/declarations/compounds/protocol_declaration.dart b/pkgs/swift2objc/lib/src/ast/declarations/compounds/protocol_declaration.dart index ce2b16d16..5276d3385 100644 --- a/pkgs/swift2objc/lib/src/ast/declarations/compounds/protocol_declaration.dart +++ b/pkgs/swift2objc/lib/src/ast/declarations/compounds/protocol_declaration.dart @@ -7,6 +7,7 @@ import '../../_core/interfaces/nestable_declaration.dart'; import '../../_core/interfaces/objc_annotatable.dart'; import '../../_core/shared/referred_type.dart'; import '../../ast_node.dart'; +import 'members/associated_type_declaration.dart'; import 'members/initializer_declaration.dart'; import 'members/method_declaration.dart'; import 'members/property_declaration.dart'; @@ -29,6 +30,11 @@ class ProtocolDeclaration extends AstNode implements CompoundDeclaration, ObjCAn @override List optionalProperties; + /// Associated types used with this declaration + /// They are similar to generic types + /// but only designated for protocol declarations + List associatedTypes; + /// Only present if indicated with `@objc` @override List optionalMethods; @@ -59,6 +65,7 @@ class ProtocolDeclaration extends AstNode implements CompoundDeclaration, ObjCAn required this.initializers, required this.conformedProtocols, required this.typeParams, + this.associatedTypes = const [], this.hasObjCAnnotation = false, this.nestingParent, this.nestedDeclarations = const [], diff --git a/pkgs/swift2objc/lib/src/parser/parsers/declaration_parsers/parse_associated_type_declaration.dart b/pkgs/swift2objc/lib/src/parser/parsers/declaration_parsers/parse_associated_type_declaration.dart new file mode 100644 index 000000000..8b191af36 --- /dev/null +++ b/pkgs/swift2objc/lib/src/parser/parsers/declaration_parsers/parse_associated_type_declaration.dart @@ -0,0 +1,15 @@ +// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import '../../../ast/declarations/compounds/members/associated_type_declaration.dart'; +import '../../_core/json.dart'; +import '../../_core/parsed_symbolgraph.dart'; +import '../../_core/utils.dart'; + +AssociatedTypeDeclaration parseAssociatedTypeDeclaration(Json symbolJson, ParsedSymbolgraph symbolGraph) { + final id = parseSymbolId(symbolJson); + final name = parseSymbolName(symbolJson); + + return AssociatedTypeDeclaration(id: id, name: name); +} diff --git a/pkgs/swift2objc/lib/src/parser/parsers/declaration_parsers/parse_compound_declaration.dart b/pkgs/swift2objc/lib/src/parser/parsers/declaration_parsers/parse_compound_declaration.dart index 27ecfa3f4..bb5de2745 100644 --- a/pkgs/swift2objc/lib/src/parser/parsers/declaration_parsers/parse_compound_declaration.dart +++ b/pkgs/swift2objc/lib/src/parser/parsers/declaration_parsers/parse_compound_declaration.dart @@ -6,6 +6,7 @@ import '../../../ast/_core/interfaces/compound_declaration.dart'; import '../../../ast/_core/interfaces/declaration.dart'; import '../../../ast/_core/interfaces/nestable_declaration.dart'; import '../../../ast/declarations/compounds/class_declaration.dart'; +import '../../../ast/declarations/compounds/members/associated_type_declaration.dart'; import '../../../ast/declarations/compounds/members/initializer_declaration.dart'; import '../../../ast/declarations/compounds/members/method_declaration.dart'; import '../../../ast/declarations/compounds/members/property_declaration.dart'; @@ -196,6 +197,13 @@ ProtocolDeclaration parseProtocolDeclaration( protocol.hasObjCAnnotation = true; } + protocol.associatedTypes.addAll( + memberDeclarations + .whereType() + .map((decl) => decl.asType()) + .dedupeBy((m) => m.name) + ); + protocol.methods.addAll( memberDeclarations .whereType() @@ -232,5 +240,7 @@ ProtocolDeclaration parseProtocolDeclaration( protocol.nestedDeclarations.fillNestingParents(protocol); + + return protocol; } \ No newline at end of file diff --git a/pkgs/swift2objc/lib/src/parser/parsers/parse_declarations.dart b/pkgs/swift2objc/lib/src/parser/parsers/parse_declarations.dart index 6a7bbca3e..6a9d268f6 100644 --- a/pkgs/swift2objc/lib/src/parser/parsers/parse_declarations.dart +++ b/pkgs/swift2objc/lib/src/parser/parsers/parse_declarations.dart @@ -5,8 +5,12 @@ import 'package:logging/logging.dart'; import '../../ast/_core/interfaces/declaration.dart'; +import '../../ast/_core/shared/referred_type.dart'; +import '../../ast/declarations/compounds/members/associated_type_declaration.dart'; +import '../_core/json.dart'; import '../_core/parsed_symbolgraph.dart'; import '../_core/utils.dart'; +import 'declaration_parsers/parse_associated_type_declaration.dart'; import 'declaration_parsers/parse_compound_declaration.dart'; import 'declaration_parsers/parse_function_declaration.dart'; import 'declaration_parsers/parse_initializer_declaration.dart'; @@ -57,6 +61,7 @@ Declaration parseDeclaration( 'swift.func' => parseGlobalFunctionDeclaration(symbolJson, symbolgraph), 'swift.var' => parseGlobalVariableDeclaration(symbolJson, symbolgraph), 'swift.protocol' => parseProtocolDeclaration(parsedSymbol, symbolgraph), + 'swift.associatedtype' => parseAssociatedTypeDeclaration(symbolJson, symbolgraph), _ => throw Exception( 'Symbol of type $symbolType is not implemented yet.', ), diff --git a/pkgs/swift2objc/lib/src/parser/parsers/parse_type.dart b/pkgs/swift2objc/lib/src/parser/parsers/parse_type.dart index 6e7a4231f..1efa4b3cc 100644 --- a/pkgs/swift2objc/lib/src/parser/parsers/parse_type.dart +++ b/pkgs/swift2objc/lib/src/parser/parsers/parse_type.dart @@ -10,6 +10,7 @@ import '../_core/parsed_symbolgraph.dart'; import '../_core/token_list.dart'; import 'parse_declarations.dart'; + /// Parse a type from a list of Json fragments. /// /// Returns the parsed type, and a Json slice of the remaining fragments that