From f5d1118fc2f1f64ecee7b7df9724ebb1be01ca1e Mon Sep 17 00:00:00 2001 From: Amr Ahmed Date: Fri, 24 Jan 2025 01:44:45 +0200 Subject: [PATCH 1/2] Support properties that throw --- .../interfaces/function_declaration.dart | 1 + .../compounds/members/method_declaration.dart | 4 ++ .../src/ast/declarations/globals/globals.dart | 4 ++ .../parse_variable_declaration.dart | 5 +- .../transformers/transform_compound.dart | 25 +++++++++- .../transformers/transform_function.dart | 7 +-- .../transformers/transform_globals.dart | 34 ++++++++++--- .../integration/throwing_getters_input.swift | 29 +++++++++++ .../integration/throwing_getters_output.swift | 49 +++++++++++++++++++ 9 files changed, 143 insertions(+), 15 deletions(-) create mode 100644 pkgs/swift2objc/test/integration/throwing_getters_input.swift create mode 100644 pkgs/swift2objc/test/integration/throwing_getters_output.swift diff --git a/pkgs/swift2objc/lib/src/ast/_core/interfaces/function_declaration.dart b/pkgs/swift2objc/lib/src/ast/_core/interfaces/function_declaration.dart index 5ee39604b..5bcad071d 100644 --- a/pkgs/swift2objc/lib/src/ast/_core/interfaces/function_declaration.dart +++ b/pkgs/swift2objc/lib/src/ast/_core/interfaces/function_declaration.dart @@ -20,4 +20,5 @@ abstract interface class FunctionDeclaration CanThrow, CanAsync { abstract final ReferredType returnType; + abstract final bool isCallingProperty; } diff --git a/pkgs/swift2objc/lib/src/ast/declarations/compounds/members/method_declaration.dart b/pkgs/swift2objc/lib/src/ast/declarations/compounds/members/method_declaration.dart index 604b31350..0200a02a2 100644 --- a/pkgs/swift2objc/lib/src/ast/declarations/compounds/members/method_declaration.dart +++ b/pkgs/swift2objc/lib/src/ast/declarations/compounds/members/method_declaration.dart @@ -43,6 +43,9 @@ class MethodDeclaration extends AstNode @override ReferredType returnType; + @override + bool isCallingProperty; + bool isStatic; String get fullName => [ @@ -62,6 +65,7 @@ class MethodDeclaration extends AstNode this.isOverriding = false, this.throws = false, this.async = false, + this.isCallingProperty = false, }) : assert(!isStatic || !isOverriding); @override diff --git a/pkgs/swift2objc/lib/src/ast/declarations/globals/globals.dart b/pkgs/swift2objc/lib/src/ast/declarations/globals/globals.dart index e8497bbd9..25a20e93b 100644 --- a/pkgs/swift2objc/lib/src/ast/declarations/globals/globals.dart +++ b/pkgs/swift2objc/lib/src/ast/declarations/globals/globals.dart @@ -46,6 +46,9 @@ class GlobalFunctionDeclaration extends AstNode implements FunctionDeclaration { @override List statements; + @override + bool isCallingProperty; + GlobalFunctionDeclaration({ required this.id, required this.name, @@ -55,6 +58,7 @@ class GlobalFunctionDeclaration extends AstNode implements FunctionDeclaration { this.statements = const [], this.throws = false, this.async = false, + this.isCallingProperty = false, }); @override diff --git a/pkgs/swift2objc/lib/src/parser/parsers/declaration_parsers/parse_variable_declaration.dart b/pkgs/swift2objc/lib/src/parser/parsers/declaration_parsers/parse_variable_declaration.dart index 1877af6c1..739724bf1 100644 --- a/pkgs/swift2objc/lib/src/parser/parsers/declaration_parsers/parse_variable_declaration.dart +++ b/pkgs/swift2objc/lib/src/parser/parsers/declaration_parsers/parse_variable_declaration.dart @@ -73,11 +73,10 @@ bool _parseVariableThrows(Json json) { final throws = json['declarationFragments'] .any((frag) => matchFragment(frag, 'keyword', 'throws')); if (throws) { - // TODO(https://github.com/dart-lang/native/issues/1765): Support throwing // getters. - throw Exception("Throwing getters aren't supported yet, at ${json.path}"); + return true; } - return throws; + return false; } bool _parseVariableAsync(Json json) { diff --git a/pkgs/swift2objc/lib/src/transformer/transformers/transform_compound.dart b/pkgs/swift2objc/lib/src/transformer/transformers/transform_compound.dart index 3e079f0f2..1509dd64e 100644 --- a/pkgs/swift2objc/lib/src/transformer/transformers/transform_compound.dart +++ b/pkgs/swift2objc/lib/src/transformer/transformers/transform_compound.dart @@ -9,6 +9,7 @@ import '../../ast/_core/shared/parameter.dart'; import '../../ast/declarations/built_in/built_in_declaration.dart'; 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 '../../parser/_core/utils.dart'; import '../_core/unique_namer.dart'; @@ -52,6 +53,7 @@ ClassDeclaration transformCompound( .fillNestingParents(transformedCompound); transformedCompound.properties = originalCompound.properties + .where((property) => !property.throws) .map((property) => transformProperty( property, wrappedCompoundInstance, @@ -72,7 +74,8 @@ ClassDeclaration transformCompound( .toList() ..sort((Declaration a, Declaration b) => a.id.compareTo(b.id)); - transformedCompound.methods = originalCompound.methods + transformedCompound.methods = (originalCompound.methods + + _convertPropertiesToMethods(originalCompound.properties)) .map((method) => transformMethod( method, wrappedCompoundInstance, @@ -106,3 +109,23 @@ InitializerDeclaration _buildWrapperInitializer( hasObjCAnnotation: wrappedClassInstance.hasObjCAnnotation, ); } + +List _convertPropertiesToMethods( + List properties, +) { + return properties + .where((property) => property.throws) + .map((property) => MethodDeclaration( + id: property.id, + name: property.name, + returnType: property.type, + params: [], + hasObjCAnnotation: true, + statements: property.getter?.statements ?? [], + isStatic: property.isStatic, + throws: property.throws, + async: property.async, + isCallingProperty: true, + )) + .toList(); +} diff --git a/pkgs/swift2objc/lib/src/transformer/transformers/transform_function.dart b/pkgs/swift2objc/lib/src/transformer/transformers/transform_function.dart index c5e375282..235b7c1ab 100644 --- a/pkgs/swift2objc/lib/src/transformer/transformers/transform_function.dart +++ b/pkgs/swift2objc/lib/src/transformer/transformers/transform_function.dart @@ -39,7 +39,8 @@ MethodDeclaration? transformMethod( final methodSource = originalMethod.isStatic ? wrappedClassInstance.type.swiftType : wrappedClassInstance.name; - return '$methodSource.${originalMethod.name}($arguments)'; + return '$methodSource.${originalMethod.name}' + '${originalMethod.isCallingProperty ? '' : '($arguments)'}'; }, ); } @@ -56,8 +57,8 @@ MethodDeclaration transformGlobalFunction( wrapperMethodName: globalNamer.makeUnique( '${globalFunction.name}Wrapper', ), - originalCallStatementGenerator: (arguments) => - '${globalFunction.name}($arguments)', + originalCallStatementGenerator: (arguments) => '${globalFunction.name}' + '${globalFunction.isCallingProperty ? '' : '($arguments)'}', ); } diff --git a/pkgs/swift2objc/lib/src/transformer/transformers/transform_globals.dart b/pkgs/swift2objc/lib/src/transformer/transformers/transform_globals.dart index b68843d58..88a396381 100644 --- a/pkgs/swift2objc/lib/src/transformer/transformers/transform_globals.dart +++ b/pkgs/swift2objc/lib/src/transformer/transformers/transform_globals.dart @@ -22,6 +22,7 @@ ClassDeclaration transformGlobals( ); transformedGlobals.properties = globals.variables + .where((variable) => !variable.throws) .map((variable) => transformGlobalVariable( variable, globalNamer, @@ -30,14 +31,31 @@ ClassDeclaration transformGlobals( .toList() ..sort((Declaration a, Declaration b) => a.id.compareTo(b.id)); - transformedGlobals.methods = globals.functions - .map((function) => transformGlobalFunction( - function, - globalNamer, - transformationMap, - )) - .toList() - ..sort((Declaration a, Declaration b) => a.id.compareTo(b.id)); + transformedGlobals.methods = + (globals.functions + _convertVariablesToFunctions(globals.variables)) + .map((function) => transformGlobalFunction( + function, + globalNamer, + transformationMap, + )) + .toList() + ..sort((Declaration a, Declaration b) => a.id.compareTo(b.id)); return transformedGlobals; } + +List _convertVariablesToFunctions( + List variables, +) { + return variables + .where((variable) => variable.throws) + .map((variable) => GlobalFunctionDeclaration( + id: variable.id, + name: variable.name, + params: [], + returnType: variable.type, + throws: variable.throws, + isCallingProperty: true, + )) + .toList(); +} diff --git a/pkgs/swift2objc/test/integration/throwing_getters_input.swift b/pkgs/swift2objc/test/integration/throwing_getters_input.swift new file mode 100644 index 000000000..535593bdc --- /dev/null +++ b/pkgs/swift2objc/test/integration/throwing_getters_input.swift @@ -0,0 +1,29 @@ +import Foundation + +public class MyClass { + public init(y: Int) throws {} + + public var classGetter: MyClass { + get throws { + try MyClass(y: 3) + } + } + public var otherClassGetter: OtherClass { + get throws { + OtherClass() + } + } +} + +public class OtherClass {} + +public var globalClassGetter: MyClass { + get throws { + try MyClass(y: 4) + } +} +public var globalOtherClassGetter: OtherClass { + get throws { + OtherClass() + } +} diff --git a/pkgs/swift2objc/test/integration/throwing_getters_output.swift b/pkgs/swift2objc/test/integration/throwing_getters_output.swift new file mode 100644 index 000000000..6c1cbfaa4 --- /dev/null +++ b/pkgs/swift2objc/test/integration/throwing_getters_output.swift @@ -0,0 +1,49 @@ +// Test preamble text + +import Foundation + +@objc public class GlobalsWrapper: NSObject { + @objc static public func globalClassGetterWrapper() throws -> MyClassWrapper { + let result = try globalClassGetter + return MyClassWrapper(result) + } + + @objc static public func globalOtherClassGetterWrapper() throws -> OtherClassWrapper { + let result = try globalOtherClassGetter + return OtherClassWrapper(result) + } + +} + +@objc public class OtherClassWrapper: NSObject { + var wrappedInstance: OtherClass + + init(_ wrappedInstance: OtherClass) { + self.wrappedInstance = wrappedInstance + } + +} + +@objc public class MyClassWrapper: NSObject { + var wrappedInstance: MyClass + + init(_ wrappedInstance: MyClass) { + self.wrappedInstance = wrappedInstance + } + + @objc init(y: Int) throws { + wrappedInstance = try MyClass(y: y) + } + + @objc public func otherClassGetter() throws -> OtherClassWrapper { + let result = try wrappedInstance.otherClassGetter + return OtherClassWrapper(result) + } + + @objc public func classGetter() throws -> MyClassWrapper { + let result = try wrappedInstance.classGetter + return MyClassWrapper(result) + } + +} + From d2745200aac296a5741af78b3711d536d3e17f32 Mon Sep 17 00:00:00 2001 From: Amr Ahmed Date: Tue, 28 Jan 2025 00:32:58 +0200 Subject: [PATCH 2/2] Implement transformation in transform_variable.dart --- .../interfaces/function_declaration.dart | 1 - .../compounds/members/method_declaration.dart | 4 -- .../src/ast/declarations/globals/globals.dart | 4 -- .../parse_variable_declaration.dart | 6 +-- .../transformers/transform_compound.dart | 37 +++++---------- .../transformers/transform_function.dart | 7 ++- .../transformers/transform_globals.dart | 45 ++++++++----------- .../transformers/transform_variable.dart | 27 +++++++++-- 8 files changed, 57 insertions(+), 74 deletions(-) diff --git a/pkgs/swift2objc/lib/src/ast/_core/interfaces/function_declaration.dart b/pkgs/swift2objc/lib/src/ast/_core/interfaces/function_declaration.dart index 5bcad071d..5ee39604b 100644 --- a/pkgs/swift2objc/lib/src/ast/_core/interfaces/function_declaration.dart +++ b/pkgs/swift2objc/lib/src/ast/_core/interfaces/function_declaration.dart @@ -20,5 +20,4 @@ abstract interface class FunctionDeclaration CanThrow, CanAsync { abstract final ReferredType returnType; - abstract final bool isCallingProperty; } diff --git a/pkgs/swift2objc/lib/src/ast/declarations/compounds/members/method_declaration.dart b/pkgs/swift2objc/lib/src/ast/declarations/compounds/members/method_declaration.dart index 0200a02a2..604b31350 100644 --- a/pkgs/swift2objc/lib/src/ast/declarations/compounds/members/method_declaration.dart +++ b/pkgs/swift2objc/lib/src/ast/declarations/compounds/members/method_declaration.dart @@ -43,9 +43,6 @@ class MethodDeclaration extends AstNode @override ReferredType returnType; - @override - bool isCallingProperty; - bool isStatic; String get fullName => [ @@ -65,7 +62,6 @@ class MethodDeclaration extends AstNode this.isOverriding = false, this.throws = false, this.async = false, - this.isCallingProperty = false, }) : assert(!isStatic || !isOverriding); @override diff --git a/pkgs/swift2objc/lib/src/ast/declarations/globals/globals.dart b/pkgs/swift2objc/lib/src/ast/declarations/globals/globals.dart index 25a20e93b..e8497bbd9 100644 --- a/pkgs/swift2objc/lib/src/ast/declarations/globals/globals.dart +++ b/pkgs/swift2objc/lib/src/ast/declarations/globals/globals.dart @@ -46,9 +46,6 @@ class GlobalFunctionDeclaration extends AstNode implements FunctionDeclaration { @override List statements; - @override - bool isCallingProperty; - GlobalFunctionDeclaration({ required this.id, required this.name, @@ -58,7 +55,6 @@ class GlobalFunctionDeclaration extends AstNode implements FunctionDeclaration { this.statements = const [], this.throws = false, this.async = false, - this.isCallingProperty = false, }); @override diff --git a/pkgs/swift2objc/lib/src/parser/parsers/declaration_parsers/parse_variable_declaration.dart b/pkgs/swift2objc/lib/src/parser/parsers/declaration_parsers/parse_variable_declaration.dart index 739724bf1..1d4d6195b 100644 --- a/pkgs/swift2objc/lib/src/parser/parsers/declaration_parsers/parse_variable_declaration.dart +++ b/pkgs/swift2objc/lib/src/parser/parsers/declaration_parsers/parse_variable_declaration.dart @@ -72,11 +72,7 @@ bool _parseVariableIsConstant(Json variableSymbolJson) { bool _parseVariableThrows(Json json) { final throws = json['declarationFragments'] .any((frag) => matchFragment(frag, 'keyword', 'throws')); - if (throws) { - // getters. - return true; - } - return false; + return throws; } bool _parseVariableAsync(Json json) { diff --git a/pkgs/swift2objc/lib/src/transformer/transformers/transform_compound.dart b/pkgs/swift2objc/lib/src/transformer/transformers/transform_compound.dart index 1509dd64e..0ec38fea5 100644 --- a/pkgs/swift2objc/lib/src/transformer/transformers/transform_compound.dart +++ b/pkgs/swift2objc/lib/src/transformer/transformers/transform_compound.dart @@ -52,8 +52,7 @@ ClassDeclaration transformCompound( transformedCompound.nestedDeclarations .fillNestingParents(transformedCompound); - transformedCompound.properties = originalCompound.properties - .where((property) => !property.throws) + final transformedProperties = originalCompound.properties .map((property) => transformProperty( property, wrappedCompoundInstance, @@ -61,8 +60,7 @@ ClassDeclaration transformCompound( transformationMap, )) .nonNulls - .toList() - ..sort((Declaration a, Declaration b) => a.id.compareTo(b.id)); + .toList(); transformedCompound.initializers = originalCompound.initializers .map((initializer) => transformInitializer( @@ -74,8 +72,7 @@ ClassDeclaration transformCompound( .toList() ..sort((Declaration a, Declaration b) => a.id.compareTo(b.id)); - transformedCompound.methods = (originalCompound.methods + - _convertPropertiesToMethods(originalCompound.properties)) + final transformedMethods = originalCompound.methods .map((method) => transformMethod( method, wrappedCompoundInstance, @@ -83,9 +80,17 @@ ClassDeclaration transformCompound( transformationMap, )) .nonNulls + .toList(); + + transformedCompound.properties = transformedProperties + .whereType() .toList() ..sort((Declaration a, Declaration b) => a.id.compareTo(b.id)); + transformedCompound.methods = (transformedMethods + + transformedProperties.whereType().toList()) + ..sort((Declaration a, Declaration b) => a.id.compareTo(b.id)); + return transformedCompound; } @@ -109,23 +114,3 @@ InitializerDeclaration _buildWrapperInitializer( hasObjCAnnotation: wrappedClassInstance.hasObjCAnnotation, ); } - -List _convertPropertiesToMethods( - List properties, -) { - return properties - .where((property) => property.throws) - .map((property) => MethodDeclaration( - id: property.id, - name: property.name, - returnType: property.type, - params: [], - hasObjCAnnotation: true, - statements: property.getter?.statements ?? [], - isStatic: property.isStatic, - throws: property.throws, - async: property.async, - isCallingProperty: true, - )) - .toList(); -} diff --git a/pkgs/swift2objc/lib/src/transformer/transformers/transform_function.dart b/pkgs/swift2objc/lib/src/transformer/transformers/transform_function.dart index 235b7c1ab..c5e375282 100644 --- a/pkgs/swift2objc/lib/src/transformer/transformers/transform_function.dart +++ b/pkgs/swift2objc/lib/src/transformer/transformers/transform_function.dart @@ -39,8 +39,7 @@ MethodDeclaration? transformMethod( final methodSource = originalMethod.isStatic ? wrappedClassInstance.type.swiftType : wrappedClassInstance.name; - return '$methodSource.${originalMethod.name}' - '${originalMethod.isCallingProperty ? '' : '($arguments)'}'; + return '$methodSource.${originalMethod.name}($arguments)'; }, ); } @@ -57,8 +56,8 @@ MethodDeclaration transformGlobalFunction( wrapperMethodName: globalNamer.makeUnique( '${globalFunction.name}Wrapper', ), - originalCallStatementGenerator: (arguments) => '${globalFunction.name}' - '${globalFunction.isCallingProperty ? '' : '($arguments)'}', + originalCallStatementGenerator: (arguments) => + '${globalFunction.name}($arguments)', ); } diff --git a/pkgs/swift2objc/lib/src/transformer/transformers/transform_globals.dart b/pkgs/swift2objc/lib/src/transformer/transformers/transform_globals.dart index 88a396381..bc2af787f 100644 --- a/pkgs/swift2objc/lib/src/transformer/transformers/transform_globals.dart +++ b/pkgs/swift2objc/lib/src/transformer/transformers/transform_globals.dart @@ -1,6 +1,8 @@ import '../../ast/_core/interfaces/declaration.dart'; import '../../ast/declarations/built_in/built_in_declaration.dart'; import '../../ast/declarations/compounds/class_declaration.dart'; +import '../../ast/declarations/compounds/members/method_declaration.dart'; +import '../../ast/declarations/compounds/members/property_declaration.dart'; import '../../ast/declarations/globals/globals.dart'; import '../../parser/_core/utils.dart'; import '../_core/unique_namer.dart'; @@ -21,41 +23,30 @@ ClassDeclaration transformGlobals( isWrapper: true, ); - transformedGlobals.properties = globals.variables - .where((variable) => !variable.throws) + final transformedProperties = globals.variables .map((variable) => transformGlobalVariable( variable, globalNamer, transformationMap, )) + .toList(); + + final transformedMethods = globals.functions + .map((function) => transformGlobalFunction( + function, + globalNamer, + transformationMap, + )) + .toList(); + + transformedGlobals.properties = transformedProperties + .whereType() .toList() ..sort((Declaration a, Declaration b) => a.id.compareTo(b.id)); - transformedGlobals.methods = - (globals.functions + _convertVariablesToFunctions(globals.variables)) - .map((function) => transformGlobalFunction( - function, - globalNamer, - transformationMap, - )) - .toList() - ..sort((Declaration a, Declaration b) => a.id.compareTo(b.id)); + transformedGlobals.methods = (transformedMethods + + transformedProperties.whereType().toList()) + ..sort((Declaration a, Declaration b) => a.id.compareTo(b.id)); return transformedGlobals; } - -List _convertVariablesToFunctions( - List variables, -) { - return variables - .where((variable) => variable.throws) - .map((variable) => GlobalFunctionDeclaration( - id: variable.id, - name: variable.name, - params: [], - returnType: variable.type, - throws: variable.throws, - isCallingProperty: true, - )) - .toList(); -} diff --git a/pkgs/swift2objc/lib/src/transformer/transformers/transform_variable.dart b/pkgs/swift2objc/lib/src/transformer/transformers/transform_variable.dart index c18e02a50..e60699499 100644 --- a/pkgs/swift2objc/lib/src/transformer/transformers/transform_variable.dart +++ b/pkgs/swift2objc/lib/src/transformer/transformers/transform_variable.dart @@ -1,4 +1,6 @@ +import '../../ast/_core/interfaces/declaration.dart'; import '../../ast/_core/interfaces/variable_declaration.dart'; +import '../../ast/declarations/compounds/members/method_declaration.dart'; import '../../ast/declarations/compounds/members/property_declaration.dart'; import '../../ast/declarations/globals/globals.dart'; import '../_core/unique_namer.dart'; @@ -13,7 +15,7 @@ import 'transform_referred_type.dart'; // through the wrapped class instance in the wrapper class. In global variable // case, it can be referenced directly since it's not a member of any entity. -PropertyDeclaration? transformProperty( +Declaration? transformProperty( PropertyDeclaration originalProperty, PropertyDeclaration wrappedClassInstance, UniqueNamer globalNamer, @@ -36,7 +38,7 @@ PropertyDeclaration? transformProperty( ); } -PropertyDeclaration transformGlobalVariable( +Declaration transformGlobalVariable( GlobalVariableDeclaration globalVariable, UniqueNamer globalNamer, TransformationMap transformationMap, @@ -54,7 +56,7 @@ PropertyDeclaration transformGlobalVariable( // -------------------------- Core Implementation -------------------------- -PropertyDeclaration _transformVariable( +Declaration _transformVariable( VariableDeclaration originalVariable, UniqueNamer globalNamer, TransformationMap transformationMap, { @@ -71,6 +73,25 @@ PropertyDeclaration _transformVariable( ? originalVariable.hasSetter : !originalVariable.isConstant; + if (originalVariable.throws) { + return MethodDeclaration( + id: originalVariable.id, + name: wrapperPropertyName, + returnType: transformedType, + params: [], + hasObjCAnnotation: true, + isStatic: originalVariable is PropertyDeclaration + ? originalVariable.isStatic + : true, + statements: [ + 'let result = try $variableReferenceExpression', + 'return $transformedType(result)', + ], + throws: true, + async: originalVariable.async, + ); + } + final transformedProperty = PropertyDeclaration( id: originalVariable.id, name: wrapperPropertyName,