diff --git a/pkgs/swift2objc/lib/src/ast/_core/interfaces/compound_memeber.dart b/pkgs/swift2objc/lib/src/ast/_core/interfaces/compound_memeber.dart new file mode 100644 index 000000000..e45740a5d --- /dev/null +++ b/pkgs/swift2objc/lib/src/ast/_core/interfaces/compound_memeber.dart @@ -0,0 +1,8 @@ +// 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. + +/// An interface to describe a Swift entity's compound membership +abstract interface class CompoundMemeber { + abstract final bool isStatic; +} diff --git a/pkgs/swift2objc/lib/src/ast/_core/interfaces/function_declaration.dart b/pkgs/swift2objc/lib/src/ast/_core/interfaces/function_declaration.dart new file mode 100644 index 000000000..140ed9695 --- /dev/null +++ b/pkgs/swift2objc/lib/src/ast/_core/interfaces/function_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 '../shared/referred_type.dart'; +import 'declaration.dart'; +import 'executable.dart'; +import 'parameterizable.dart'; +import 'type_parameterizable.dart'; + +/// Describes a function-like entity. +abstract interface class FunctionDeclaration + implements Declaration, Parameterizable, Executable, TypeParameterizable { + abstract final ReferredType? returnType; +} diff --git a/pkgs/swift2objc/lib/src/ast/_core/interfaces/variable_declaration.dart b/pkgs/swift2objc/lib/src/ast/_core/interfaces/variable_declaration.dart new file mode 100644 index 000000000..1f024735b --- /dev/null +++ b/pkgs/swift2objc/lib/src/ast/_core/interfaces/variable_declaration.dart @@ -0,0 +1,12 @@ +// 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 '../shared/referred_type.dart'; +import 'declaration.dart'; + +/// Describes a variable-like entity. +abstract interface class VariableDeclaration implements Declaration { + abstract final bool isConstant; + abstract final ReferredType type; +} 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 c16a59139..ac4298ff9 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 @@ -2,23 +2,14 @@ // 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 '../../../_core/interfaces/declaration.dart'; -import '../../../_core/interfaces/executable.dart'; +import '../../../_core/interfaces/function_declaration.dart'; import '../../../_core/interfaces/objc_annotatable.dart'; -import '../../../_core/interfaces/parameterizable.dart'; -import '../../../_core/interfaces/type_parameterizable.dart'; import '../../../_core/shared/parameter.dart'; import '../../../_core/shared/referred_type.dart'; /// Describes a method declaration for a Swift compound entity /// (e.g, class, structs) -class MethodDeclaration - implements - Declaration, - TypeParameterizable, - Executable, - Parameterizable, - ObjCAnnotatable { +class MethodDeclaration implements FunctionDeclaration, ObjCAnnotatable { @override String id; @@ -37,6 +28,7 @@ class MethodDeclaration @override List statements; + @override ReferredType? returnType; bool isStatic; diff --git a/pkgs/swift2objc/lib/src/ast/declarations/compounds/members/property_declaration.dart b/pkgs/swift2objc/lib/src/ast/declarations/compounds/members/property_declaration.dart index e14dfbd9f..536edf414 100644 --- a/pkgs/swift2objc/lib/src/ast/declarations/compounds/members/property_declaration.dart +++ b/pkgs/swift2objc/lib/src/ast/declarations/compounds/members/property_declaration.dart @@ -2,14 +2,14 @@ // 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 '../../../_core/interfaces/declaration.dart'; import '../../../_core/interfaces/executable.dart'; import '../../../_core/interfaces/objc_annotatable.dart'; +import '../../../_core/interfaces/variable_declaration.dart'; import '../../../_core/shared/referred_type.dart'; /// Describes a property declaration for a Swift compound entity /// (e.g, class, structs) -class PropertyDeclaration implements Declaration, ObjCAnnotatable { +class PropertyDeclaration implements VariableDeclaration, ObjCAnnotatable { @override String id; @@ -19,13 +19,17 @@ class PropertyDeclaration implements Declaration, ObjCAnnotatable { @override bool hasObjCAnnotation; + @override + ReferredType type; + + @override + bool isConstant; + bool hasSetter; PropertyStatements? getter; PropertyStatements? setter; - ReferredType type; - bool isStatic; PropertyDeclaration({ @@ -33,11 +37,12 @@ class PropertyDeclaration implements Declaration, ObjCAnnotatable { required this.name, required this.type, this.hasSetter = false, + this.isConstant = false, this.hasObjCAnnotation = false, this.getter, this.setter, this.isStatic = false, - }); + }) : assert(!isConstant || !hasSetter); } class PropertyStatements implements Executable { diff --git a/pkgs/swift2objc/lib/src/ast/declarations/globals/globals.dart b/pkgs/swift2objc/lib/src/ast/declarations/globals/globals.dart index 3bbb6dab4..774e35ffb 100644 --- a/pkgs/swift2objc/lib/src/ast/declarations/globals/globals.dart +++ b/pkgs/swift2objc/lib/src/ast/declarations/globals/globals.dart @@ -2,9 +2,8 @@ // 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 '../../_core/interfaces/declaration.dart'; -import '../../_core/interfaces/parameterizable.dart'; -import '../../_core/interfaces/type_parameterizable.dart'; +import '../../_core/interfaces/function_declaration.dart'; +import '../../_core/interfaces/variable_declaration.dart'; import '../../_core/shared/parameter.dart'; import '../../_core/shared/referred_type.dart'; @@ -12,17 +11,16 @@ import '../../_core/shared/referred_type.dart'; /// and functions. class Globals { List functions; - List values; + List variables; Globals({ required this.functions, - required this.values, + required this.variables, }); } /// Describes a globally defined function. -class GlobalFunctionDeclaration - implements Declaration, Parameterizable, TypeParameterizable { +class GlobalFunctionDeclaration implements FunctionDeclaration { @override String id; @@ -35,30 +33,37 @@ class GlobalFunctionDeclaration @override List typeParams; - ReferredType returnType; + @override + ReferredType? returnType; + + @override + List statements; GlobalFunctionDeclaration({ required this.id, required this.name, required this.params, - required this.typeParams, required this.returnType, + this.typeParams = const [], + this.statements = const [], }); } /// Describes a globally defined values (i.e variable/constant). -class GlobalValueDeclaration implements Declaration { +class GlobalVariableDeclaration implements VariableDeclaration { @override String id; @override String name; - DeclaredType type; + @override + ReferredType type; + @override bool isConstant; - GlobalValueDeclaration({ + GlobalVariableDeclaration({ required this.id, required this.name, required this.type, diff --git a/pkgs/swift2objc/lib/src/generator/generators/class_generator.dart b/pkgs/swift2objc/lib/src/generator/generators/class_generator.dart index da822b9b9..274cc170c 100644 --- a/pkgs/swift2objc/lib/src/generator/generators/class_generator.dart +++ b/pkgs/swift2objc/lib/src/generator/generators/class_generator.dart @@ -104,10 +104,15 @@ List _generateClassProperties(ClassDeclaration declaration) { return declaration.properties.map( (property) { final header = StringBuffer(); + if (property.hasObjCAnnotation) { header.write('@objc '); } + if (property.isStatic) { + header.write('static '); + } + header.write('public var ${property.name}: ${property.type.name} {'); final getterLines = [ diff --git a/pkgs/swift2objc/lib/src/parser/_core/utils.dart b/pkgs/swift2objc/lib/src/parser/_core/utils.dart index 97f285136..e61eb8059 100644 --- a/pkgs/swift2objc/lib/src/parser/_core/utils.dart +++ b/pkgs/swift2objc/lib/src/parser/_core/utils.dart @@ -33,7 +33,7 @@ extension TopLevelOnly on List { (declaration) => declaration is CompoundDeclaration || declaration is EnumDeclaration || - declaration is GlobalValueDeclaration || + declaration is GlobalVariableDeclaration || declaration is GlobalFunctionDeclaration, ).toList(); } @@ -55,7 +55,7 @@ String parseSymbolName(Json symbolJson) { .get(); } -bool symbolHasObjcAnnotation(Json symbolJson) { +bool parseSymbolHasObjcAnnotation(Json symbolJson) { return symbolJson['declarationFragments'].any( (json) => json['kind'].exists && diff --git a/pkgs/swift2objc/lib/src/parser/parsers/declaration_parsers/parse_initializer_declaration.dart b/pkgs/swift2objc/lib/src/parser/parsers/declaration_parsers/parse_initializer_declaration.dart index 690d6050f..11c0904c0 100644 --- a/pkgs/swift2objc/lib/src/parser/parsers/declaration_parsers/parse_initializer_declaration.dart +++ b/pkgs/swift2objc/lib/src/parser/parsers/declaration_parsers/parse_initializer_declaration.dart @@ -11,7 +11,7 @@ InitializerDeclaration parseInitializerDeclaration( return InitializerDeclaration( id: parseSymbolId(initializerSymbolJson), params: parseInitializerParams(initializerSymbolJson, symbolgraph), - hasObjCAnnotation: symbolHasObjcAnnotation(initializerSymbolJson), + hasObjCAnnotation: parseSymbolHasObjcAnnotation(initializerSymbolJson), ); } diff --git a/pkgs/swift2objc/lib/src/parser/parsers/declaration_parsers/parse_property_declaration.dart b/pkgs/swift2objc/lib/src/parser/parsers/declaration_parsers/parse_variable_declaration.dart similarity index 51% rename from pkgs/swift2objc/lib/src/parser/parsers/declaration_parsers/parse_property_declaration.dart rename to pkgs/swift2objc/lib/src/parser/parsers/declaration_parsers/parse_variable_declaration.dart index f1315e296..783c6bb48 100644 --- a/pkgs/swift2objc/lib/src/parser/parsers/declaration_parsers/parse_property_declaration.dart +++ b/pkgs/swift2objc/lib/src/parser/parsers/declaration_parsers/parse_variable_declaration.dart @@ -1,6 +1,7 @@ import '../../../ast/_core/interfaces/declaration.dart'; import '../../../ast/_core/shared/referred_type.dart'; import '../../../ast/declarations/compounds/members/property_declaration.dart'; +import '../../../ast/declarations/globals/globals.dart'; import '../../_core/json.dart'; import '../../_core/parsed_symbolgraph.dart'; import '../../_core/utils.dart'; @@ -11,17 +12,32 @@ PropertyDeclaration parsePropertyDeclaration( ParsedSymbolgraph symbolgraph, { bool isStatic = false, }) { + final isConstant = _parseVariableIsConstant(propertySymbolJson); return PropertyDeclaration( id: parseSymbolId(propertySymbolJson), name: parseSymbolName(propertySymbolJson), - type: _parsePropertyType(propertySymbolJson, symbolgraph), - hasObjCAnnotation: symbolHasObjcAnnotation(propertySymbolJson), - hasSetter: _propertyHasSetter(propertySymbolJson), + type: _parseVariableType(propertySymbolJson, symbolgraph), + hasObjCAnnotation: parseSymbolHasObjcAnnotation(propertySymbolJson), + isConstant: isConstant, + hasSetter: isConstant ? false : _parsePropertyHasSetter(propertySymbolJson), isStatic: isStatic, ); } -ReferredType _parsePropertyType( +GlobalVariableDeclaration parseGlobalVariableDeclaration( + Json variableSymbolJson, + ParsedSymbolgraph symbolgraph, { + bool isStatic = false, +}) { + return GlobalVariableDeclaration( + id: parseSymbolId(variableSymbolJson), + name: parseSymbolName(variableSymbolJson), + type: _parseVariableType(variableSymbolJson, symbolgraph), + isConstant: _parseVariableIsConstant(variableSymbolJson), + ); +} + +ReferredType _parseVariableType( Json propertySymbolJson, ParsedSymbolgraph symbolgraph, ) { @@ -47,8 +63,8 @@ ReferredType _parsePropertyType( return typeDeclaration.asDeclaredType; } -bool _propertyHasSetter(Json propertySymbolJson) { - final fragmentsJson = propertySymbolJson['declarationFragments']; +bool _parseVariableIsConstant(Json variableSymbolJson) { + final fragmentsJson = variableSymbolJson['declarationFragments']; final declarationKeywordJson = fragmentsJson.firstWhere( (json) { @@ -67,25 +83,38 @@ bool _propertyHasSetter(Json propertySymbolJson) { final declarationKeyword = declarationKeywordJson['spelling'].get(); - if (declarationKeyword == 'var') { - final hasExplicitSetter = fragmentsJson.any( - (json) => json['spelling'].get() == 'set', - ); + return declarationKeyword == 'let'; +} - final hasExplicitGetter = fragmentsJson.any( - (json) => json['spelling'].get() == 'get', - ); +bool _parsePropertyHasSetter(Json propertySymbolJson) { + final fragmentsJson = propertySymbolJson['declarationFragments']; - if (hasExplicitGetter) { - if (hasExplicitSetter) { - return true; - } else { - return false; - } - } + final hasExplicitSetter = fragmentsJson.any( + (json) => json['spelling'].get() == 'set', + ); - return true; - } + final hasExplicitGetter = fragmentsJson.any( + (json) => json['spelling'].get() == 'get', + ); - return false; + if (hasExplicitGetter) { + if (hasExplicitSetter) { + // has explicit getter and has explicit setter + return true; + } else { + // has explicit getter and no explicit setter + return false; + } + } else { + if (hasExplicitSetter) { + // has no explicit getter and but has explicit setter + throw Exception( + 'Invalid property at ${propertySymbolJson.path}. ' + 'Properties can not have a setter without a getter', + ); + } else { + // has no explicit getter and no explicit setter + return true; + } + } } diff --git a/pkgs/swift2objc/lib/src/parser/parsers/declaration_parsers/parse_method_declaration.dart b/pkgs/swift2objc/lib/src/parser/parsers/declaration_parsers/pase_function_declaration.dart similarity index 75% rename from pkgs/swift2objc/lib/src/parser/parsers/declaration_parsers/parse_method_declaration.dart rename to pkgs/swift2objc/lib/src/parser/parsers/declaration_parsers/pase_function_declaration.dart index 2d88aa475..bdff14992 100644 --- a/pkgs/swift2objc/lib/src/parser/parsers/declaration_parsers/parse_method_declaration.dart +++ b/pkgs/swift2objc/lib/src/parser/parsers/declaration_parsers/pase_function_declaration.dart @@ -6,11 +6,24 @@ import '../../../ast/_core/interfaces/declaration.dart'; import '../../../ast/_core/shared/parameter.dart'; import '../../../ast/_core/shared/referred_type.dart'; import '../../../ast/declarations/compounds/members/method_declaration.dart'; +import '../../../ast/declarations/globals/globals.dart'; import '../../_core/json.dart'; import '../../_core/parsed_symbolgraph.dart'; import '../../_core/utils.dart'; import '../parse_declarations.dart'; +GlobalFunctionDeclaration parseGlobalFunctionDeclaration( + Json globalFunctionSymbolJson, + ParsedSymbolgraph symbolgraph, +) { + return GlobalFunctionDeclaration( + id: parseSymbolId(globalFunctionSymbolJson), + name: parseSymbolName(globalFunctionSymbolJson), + returnType: _parseFunctionReturnType(globalFunctionSymbolJson, symbolgraph), + params: _parseFunctionParams(globalFunctionSymbolJson, symbolgraph), + ); +} + MethodDeclaration parseMethodDeclaration( Json methodSymbolJson, ParsedSymbolgraph symbolgraph, { @@ -19,14 +32,14 @@ MethodDeclaration parseMethodDeclaration( return MethodDeclaration( id: parseSymbolId(methodSymbolJson), name: parseSymbolName(methodSymbolJson), - returnType: _parseMethodReturnType(methodSymbolJson, symbolgraph), - params: _parseMethodParams(methodSymbolJson, symbolgraph), - hasObjCAnnotation: symbolHasObjcAnnotation(methodSymbolJson), + returnType: _parseFunctionReturnType(methodSymbolJson, symbolgraph), + params: _parseFunctionParams(methodSymbolJson, symbolgraph), + hasObjCAnnotation: parseSymbolHasObjcAnnotation(methodSymbolJson), isStatic: isStatic, ); } -ReferredType? _parseMethodReturnType( +ReferredType? _parseFunctionReturnType( Json methodSymbolJson, ParsedSymbolgraph symbolgraph, ) { @@ -56,7 +69,7 @@ ReferredType? _parseMethodReturnType( return returnTypeDeclaration.asDeclaredType; } -List _parseMethodParams( +List _parseFunctionParams( Json methodSymbolJson, ParsedSymbolgraph symbolgraph, ) { diff --git a/pkgs/swift2objc/lib/src/parser/parsers/parse_declarations.dart b/pkgs/swift2objc/lib/src/parser/parsers/parse_declarations.dart index 9c9237f57..48d800901 100644 --- a/pkgs/swift2objc/lib/src/parser/parsers/parse_declarations.dart +++ b/pkgs/swift2objc/lib/src/parser/parsers/parse_declarations.dart @@ -9,8 +9,8 @@ import '../_core/parsed_symbolgraph.dart'; import '../_core/utils.dart'; import 'declaration_parsers/parse_compound_declaration.dart'; import 'declaration_parsers/parse_initializer_declaration.dart'; -import 'declaration_parsers/parse_method_declaration.dart'; -import 'declaration_parsers/parse_property_declaration.dart'; +import 'declaration_parsers/parse_variable_declaration.dart'; +import 'declaration_parsers/pase_function_declaration.dart'; List parseDeclarations(ParsedSymbolgraph symbolgraph) { final declarations = []; @@ -51,6 +51,8 @@ Declaration parseDeclaration( 'swift.type.property' => parsePropertyDeclaration(symbolJson, symbolgraph, isStatic: true), 'swift.init' => parseInitializerDeclaration(symbolJson, symbolgraph), + 'swift.func' => parseGlobalFunctionDeclaration(symbolJson, symbolgraph), + 'swift.var' => parseGlobalVariableDeclaration(symbolJson, symbolgraph), _ => throw Exception( 'Symbol of type $symbolType is not implemented yet.', ), diff --git a/pkgs/swift2objc/lib/src/transformer/transform.dart b/pkgs/swift2objc/lib/src/transformer/transform.dart index 876c01c91..e7d4618c5 100644 --- a/pkgs/swift2objc/lib/src/transformer/transform.dart +++ b/pkgs/swift2objc/lib/src/transformer/transform.dart @@ -6,8 +6,10 @@ import '../ast/_core/interfaces/compound_declaration.dart'; import '../ast/_core/interfaces/declaration.dart'; import '../ast/declarations/compounds/class_declaration.dart'; import '../ast/declarations/compounds/struct_declaration.dart'; +import '../ast/declarations/globals/globals.dart'; import '_core/unique_namer.dart'; import 'transformers/transform_compound.dart'; +import 'transformers/transform_globals.dart'; typedef TransformationMap = Map; @@ -20,9 +22,27 @@ List transform(List declarations) { declarations.map((declaration) => declaration.name), ); - return declarations - .map((decl) => transformDeclaration(decl, globalNamer, transformationMap)) - .toList() + final globals = Globals( + functions: declarations.whereType().toList(), + variables: declarations.whereType().toList(), + ); + final nonGlobals = declarations + .where( + (declaration) => + declaration is! GlobalFunctionDeclaration && + declaration is! GlobalVariableDeclaration, + ) + .toList(); + + final transformedDeclarations = [ + ...nonGlobals.map( + (decl) => transformDeclaration(decl, globalNamer, transformationMap), + ), + if (globals.functions.isNotEmpty || globals.variables.isNotEmpty) + transformGlobals(globals, globalNamer, transformationMap), + ]; + + return transformedDeclarations ..sort((Declaration a, Declaration b) => a.id.compareTo(b.id)); } diff --git a/pkgs/swift2objc/lib/src/transformer/transformers/transform_compound.dart b/pkgs/swift2objc/lib/src/transformer/transformers/transform_compound.dart index acab198a8..1647997d5 100644 --- a/pkgs/swift2objc/lib/src/transformer/transformers/transform_compound.dart +++ b/pkgs/swift2objc/lib/src/transformer/transformers/transform_compound.dart @@ -12,9 +12,9 @@ import '../../ast/declarations/compounds/members/property_declaration.dart'; import '../../parser/_core/utils.dart'; import '../_core/unique_namer.dart'; import '../transform.dart'; +import 'transform_function.dart'; import 'transform_initializer.dart'; -import 'transform_method.dart'; -import 'transform_property.dart'; +import 'transform_variable.dart'; ClassDeclaration transformCompound( CompoundDeclaration originalCompound, diff --git a/pkgs/swift2objc/lib/src/transformer/transformers/transform_method.dart b/pkgs/swift2objc/lib/src/transformer/transformers/transform_function.dart similarity index 50% rename from pkgs/swift2objc/lib/src/transformer/transformers/transform_method.dart rename to pkgs/swift2objc/lib/src/transformer/transformers/transform_function.dart index 6eec1bac7..e0b42daaf 100644 --- a/pkgs/swift2objc/lib/src/transformer/transformers/transform_method.dart +++ b/pkgs/swift2objc/lib/src/transformer/transformers/transform_function.dart @@ -2,22 +2,70 @@ // 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/_core/interfaces/function_declaration.dart'; import '../../ast/_core/shared/parameter.dart'; import '../../ast/_core/shared/referred_type.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'; import '../_core/utils.dart'; import '../transform.dart'; import 'transform_referred_type.dart'; +// The main difference between generating a wrapper method for a global function +// and a compound method is the way the original function/method is referenced. +// In compound method case, the original method is referenced through the +// wrapped class instance in the wrapper class. In global function case, +// it can be referenced directly since it's not a member of any entity. + MethodDeclaration transformMethod( MethodDeclaration originalMethod, PropertyDeclaration wrappedClassInstance, UniqueNamer globalNamer, TransformationMap transformationMap, ) { - final transformedParams = originalMethod.params + return _transformFunction( + originalMethod, + globalNamer, + transformationMap, + wrapperMethodName: originalMethod.name, + originalCallStatementGenerator: (arguments) { + final methodSource = originalMethod.isStatic + ? wrappedClassInstance.type.name + : wrappedClassInstance.name; + return '$methodSource.${originalMethod.name}($arguments)'; + }, + ); +} + +MethodDeclaration transformGlobalFunction( + GlobalFunctionDeclaration globalFunction, + UniqueNamer globalNamer, + TransformationMap transformationMap, +) { + return _transformFunction( + globalFunction, + globalNamer, + transformationMap, + wrapperMethodName: globalNamer.makeUnique( + '${globalFunction.name}Wrapper', + ), + originalCallStatementGenerator: (arguments) => + '${globalFunction.name}($arguments)', + ); +} + +// -------------------------- Core Implementation -------------------------- + +MethodDeclaration _transformFunction( + FunctionDeclaration originalFunction, + UniqueNamer globalNamer, + TransformationMap transformationMap, { + required String wrapperMethodName, + required String Function(String arguments) originalCallStatementGenerator, +}) { + final transformedParams = originalFunction.params .map( (param) => Parameter( name: param.name, @@ -33,47 +81,49 @@ MethodDeclaration transformMethod( final ReferredType? transformedReturnType; - if (originalMethod.returnType == null) { + if (originalFunction.returnType == null) { transformedReturnType = null; } else { transformedReturnType = transformReferredType( - originalMethod.returnType!, + originalFunction.returnType!, globalNamer, transformationMap, ); } final transformedMethod = MethodDeclaration( - id: originalMethod.id, - name: originalMethod.name, + id: originalFunction.id, + name: wrapperMethodName, returnType: transformedReturnType, params: transformedParams, hasObjCAnnotation: true, - isStatic: originalMethod.isStatic, + isStatic: originalFunction is MethodDeclaration + ? originalFunction.isStatic + : true, ); - transformedMethod.statements = _generateMethodStatements( - originalMethod, - wrappedClassInstance, + transformedMethod.statements = _generateStatements( + originalFunction, transformedMethod, globalNamer, transformationMap, + originalCallGenerator: originalCallStatementGenerator, ); return transformedMethod; } -List _generateMethodStatements( - MethodDeclaration originalMethod, - PropertyDeclaration wrappedClassInstance, +List _generateStatements( + FunctionDeclaration originalFunction, MethodDeclaration transformedMethod, UniqueNamer globalNamer, - TransformationMap transformationMap, -) { + TransformationMap transformationMap, { + required String Function(String arguments) originalCallGenerator, +}) { final argumentsList = []; - for (var i = 0; i < originalMethod.params.length; i++) { - final originalParam = originalMethod.params[i]; + for (var i = 0; i < originalFunction.params.length; i++) { + final originalParam = originalFunction.params[i]; final transformedParam = transformedMethod.params[i]; final transformedParamName = @@ -93,27 +143,24 @@ List _generateMethodStatements( final arguments = argumentsList.join(', '); - final methodSource = originalMethod.isStatic - ? wrappedClassInstance.type.name - : wrappedClassInstance.name; - final originalMethodCall = '$methodSource.${originalMethod.name}($arguments)'; + final originalMethodCall = originalCallGenerator(arguments); - if (originalMethod.returnType == null) { + if (originalFunction.returnType == null) { return [originalMethodCall]; } - if (originalMethod.returnType!.id == transformedMethod.returnType?.id) { + if (originalFunction.returnType!.id == transformedMethod.returnType?.id) { return ['return $originalMethodCall']; } - if (originalMethod.returnType is GenericType) { + if (originalFunction.returnType is GenericType) { throw UnimplementedError('Generic types are not implemented yet'); } final methodCallStmt = 'let result = $originalMethodCall'; final (wrappedResult, wrapperType) = maybeWrapValue( - originalMethod.returnType!, + originalFunction.returnType!, 'result', globalNamer, transformationMap, diff --git a/pkgs/swift2objc/lib/src/transformer/transformers/transform_globals.dart b/pkgs/swift2objc/lib/src/transformer/transformers/transform_globals.dart new file mode 100644 index 000000000..47f08a263 --- /dev/null +++ b/pkgs/swift2objc/lib/src/transformer/transformers/transform_globals.dart @@ -0,0 +1,43 @@ +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/globals/globals.dart'; +import '../../parser/_core/utils.dart'; +import '../_core/unique_namer.dart'; +import '../transform.dart'; +import 'transform_function.dart'; +import 'transform_variable.dart'; + +ClassDeclaration transformGlobals( + Globals globals, + UniqueNamer globalNamer, + TransformationMap transformationMap, +) { + final transformedGlobals = ClassDeclaration( + id: 'globals'.addIdSuffix('wrapper'), + name: globalNamer.makeUnique('GlobalsWrapper'), + hasObjCAnnotation: true, + superClass: BuiltInDeclaration.swiftNSObject.asDeclaredType, + isWrapper: true, + ); + + transformedGlobals.properties = globals.variables + .map((variable) => transformGlobalVariable( + variable, + globalNamer, + transformationMap, + )) + .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)); + + return transformedGlobals; +} diff --git a/pkgs/swift2objc/lib/src/transformer/transformers/transform_property.dart b/pkgs/swift2objc/lib/src/transformer/transformers/transform_property.dart deleted file mode 100644 index 6655eeb37..000000000 --- a/pkgs/swift2objc/lib/src/transformer/transformers/transform_property.dart +++ /dev/null @@ -1,95 +0,0 @@ -import '../../ast/declarations/compounds/members/property_declaration.dart'; -import '../_core/unique_namer.dart'; -import '../_core/utils.dart'; -import '../transform.dart'; -import 'transform_referred_type.dart'; - -PropertyDeclaration transformProperty( - PropertyDeclaration originalProperty, - PropertyDeclaration wrappedClassInstance, - UniqueNamer globalNamer, - TransformationMap transformationMap, -) { - final transformedType = transformReferredType( - originalProperty.type, - globalNamer, - transformationMap, - ); - - final transformedProperty = PropertyDeclaration( - id: originalProperty.id, - name: originalProperty.name, - hasSetter: originalProperty.hasSetter, - type: transformedType, - hasObjCAnnotation: true, - isStatic: originalProperty.isStatic, - ); - - final getterStatements = _generateGetterStatements( - originalProperty, - wrappedClassInstance, - transformedProperty, - globalNamer, - transformationMap, - ); - transformedProperty.getter = PropertyStatements(getterStatements); - - if (originalProperty.hasSetter) { - final setterStatements = _generateSetterStatements( - originalProperty, - wrappedClassInstance, - transformedProperty, - globalNamer, - transformationMap, - ); - transformedProperty.setter = PropertyStatements(setterStatements); - } - - return transformedProperty; -} - -List _generateGetterStatements( - PropertyDeclaration originalProperty, - PropertyDeclaration wrappedClassInstance, - PropertyDeclaration transformedProperty, - UniqueNamer globalNamer, - TransformationMap transformationMap, -) { - final propertySource = originalProperty.isStatic - ? wrappedClassInstance.type.name - : wrappedClassInstance.name; - final wrappedProperty = '$propertySource.${originalProperty.name}'; - - final (wrappedValue, wrapperType) = maybeWrapValue( - originalProperty.type, - wrappedProperty, - globalNamer, - transformationMap, - ); - - assert(wrapperType.id == transformedProperty.type.id); - - return [wrappedValue]; -} - -List _generateSetterStatements( - PropertyDeclaration originalProperty, - PropertyDeclaration wrappedClassInstance, - PropertyDeclaration transformedProperty, - UniqueNamer globalNamer, - TransformationMap transformationMap, -) { - final propertySource = originalProperty.isStatic - ? wrappedClassInstance.type.name - : wrappedClassInstance.name; - final wrappedProperty = '$propertySource.${originalProperty.name}'; - - final (unwrappedValue, unwrappedType) = maybeUnwrapValue( - transformedProperty.type, - 'newValue', - ); - - assert(unwrappedType.id == originalProperty.type.id); - - return ['$wrappedProperty = $unwrappedValue']; -} diff --git a/pkgs/swift2objc/lib/src/transformer/transformers/transform_variable.dart b/pkgs/swift2objc/lib/src/transformer/transformers/transform_variable.dart new file mode 100644 index 000000000..75382bfdd --- /dev/null +++ b/pkgs/swift2objc/lib/src/transformer/transformers/transform_variable.dart @@ -0,0 +1,138 @@ +import '../../ast/_core/interfaces/variable_declaration.dart'; +import '../../ast/declarations/compounds/members/property_declaration.dart'; +import '../../ast/declarations/globals/globals.dart'; +import '../_core/unique_namer.dart'; +import '../_core/utils.dart'; +import '../transform.dart'; +import 'transform_referred_type.dart'; + +// The main difference between generating a wrapper property for a global +// variable and a compound property is the way the original variable/property +// is referenced. In compound property case, the original property is referenced +// 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( + PropertyDeclaration originalProperty, + PropertyDeclaration wrappedClassInstance, + UniqueNamer globalNamer, + TransformationMap transformationMap, +) { + final propertySource = originalProperty.isStatic + ? wrappedClassInstance.type.name + : wrappedClassInstance.name; + + return _transformVariable( + originalProperty, + globalNamer, + transformationMap, + wrapperPropertyName: originalProperty.name, + variableReferenceExpression: '$propertySource.${originalProperty.name}', + ); +} + +PropertyDeclaration transformGlobalVariable( + GlobalVariableDeclaration globalVariable, + UniqueNamer globalNamer, + TransformationMap transformationMap, +) { + return _transformVariable( + globalVariable, + globalNamer, + transformationMap, + wrapperPropertyName: globalNamer.makeUnique( + '${globalVariable.name}Wrapper', + ), + variableReferenceExpression: globalVariable.name, + ); +} + +// -------------------------- Core Implementation -------------------------- + +PropertyDeclaration _transformVariable( + VariableDeclaration originalVariable, + UniqueNamer globalNamer, + TransformationMap transformationMap, { + required String wrapperPropertyName, + required String variableReferenceExpression, +}) { + final transformedType = transformReferredType( + originalVariable.type, + globalNamer, + transformationMap, + ); + + final shouldGenerateSetter = originalVariable is PropertyDeclaration + ? originalVariable.hasSetter + : !originalVariable.isConstant; + + final transformedProperty = PropertyDeclaration( + id: originalVariable.id, + name: wrapperPropertyName, + type: transformedType, + hasObjCAnnotation: true, + hasSetter: shouldGenerateSetter, + isStatic: originalVariable is PropertyDeclaration + ? originalVariable.isStatic + : true, + isConstant: originalVariable.isConstant, + ); + + final getterStatements = _generateGetterStatements( + originalVariable, + variableReferenceExpression, + transformedProperty, + globalNamer, + transformationMap, + ); + transformedProperty.getter = PropertyStatements(getterStatements); + + if (shouldGenerateSetter) { + final setterStatements = _generateSetterStatements( + originalVariable, + variableReferenceExpression, + transformedProperty, + globalNamer, + transformationMap, + ); + transformedProperty.setter = PropertyStatements(setterStatements); + } + + return transformedProperty; +} + +List _generateGetterStatements( + VariableDeclaration originalVariable, + String variableReferenceExpression, + PropertyDeclaration transformedProperty, + UniqueNamer globalNamer, + TransformationMap transformationMap, +) { + final (wrappedValue, wrapperType) = maybeWrapValue( + originalVariable.type, + variableReferenceExpression, + globalNamer, + transformationMap, + ); + + assert(wrapperType.id == transformedProperty.type.id); + + return [wrappedValue]; +} + +List _generateSetterStatements( + VariableDeclaration originalVariable, + String variableReference, + PropertyDeclaration transformedProperty, + UniqueNamer globalNamer, + TransformationMap transformationMap, +) { + final (unwrappedValue, unwrappedType) = maybeUnwrapValue( + transformedProperty.type, + 'newValue', + ); + + assert(unwrappedType.id == originalVariable.type.id); + + return ['$variableReference = $unwrappedValue']; +} diff --git a/pkgs/swift2objc/test/integration/classes_and_properties_input.swift b/pkgs/swift2objc/test/integration/classes_and_properties_input.swift index 98cb1bb61..6e24e10b4 100644 --- a/pkgs/swift2objc/test/integration/classes_and_properties_input.swift +++ b/pkgs/swift2objc/test/integration/classes_and_properties_input.swift @@ -1,12 +1,12 @@ import Foundation public class MyClass { - public var customSetterVariable: MyOtherClass { + public var customSetterProperty: MyOtherClass { get { MyOtherClass() } set { } } - public var customGetterVariable: MyOtherClass { + public var customGetterProperty: MyOtherClass { get { MyOtherClass() } } @@ -14,15 +14,17 @@ public class MyClass { public let customConstantProperty: MyOtherClass - public var representableSetterVariable: Int { + public var representableSetterProperty: Int { get { 1 } set { } } - public var representableGetterVariable: Int { + public var representableGetterProperty: Int { get { 1 } } + public var implicitGetterProperty: Int { 1 } + public var representableVariableProperty: Int public let representableConstantProperty: Int diff --git a/pkgs/swift2objc/test/integration/classes_and_properties_output.swift b/pkgs/swift2objc/test/integration/classes_and_properties_output.swift index ec56565a6..828c5eb6d 100644 --- a/pkgs/swift2objc/test/integration/classes_and_properties_output.swift +++ b/pkgs/swift2objc/test/integration/classes_and_properties_output.swift @@ -13,18 +13,18 @@ import Foundation @objc public class MyClassWrapper: NSObject { var wrappedInstance: MyClass - @objc public var customGetterVariable: MyOtherClassWrapper { + @objc public var customGetterProperty: MyOtherClassWrapper { get { - MyOtherClassWrapper(wrappedInstance.customGetterVariable) + MyOtherClassWrapper(wrappedInstance.customGetterProperty) } } - @objc public var customSetterVariable: MyOtherClassWrapper { + @objc public var customSetterProperty: MyOtherClassWrapper { get { - MyOtherClassWrapper(wrappedInstance.customSetterVariable) + MyOtherClassWrapper(wrappedInstance.customSetterProperty) } set { - wrappedInstance.customSetterVariable = newValue.wrappedInstance + wrappedInstance.customSetterProperty = newValue.wrappedInstance } } @@ -43,18 +43,24 @@ import Foundation } } - @objc public var representableGetterVariable: Int { + @objc public var implicitGetterProperty: Int { get { - wrappedInstance.representableGetterVariable + wrappedInstance.implicitGetterProperty } } - @objc public var representableSetterVariable: Int { + @objc public var representableGetterProperty: Int { get { - wrappedInstance.representableSetterVariable + wrappedInstance.representableGetterProperty + } + } + + @objc public var representableSetterProperty: Int { + get { + wrappedInstance.representableSetterProperty } set { - wrappedInstance.representableSetterVariable = newValue + wrappedInstance.representableSetterProperty = newValue } } diff --git a/pkgs/swift2objc/test/integration/classes_and_static_properties_output.swift b/pkgs/swift2objc/test/integration/classes_and_static_properties_output.swift index cb6dd16c6..bb6a4d13f 100644 --- a/pkgs/swift2objc/test/integration/classes_and_static_properties_output.swift +++ b/pkgs/swift2objc/test/integration/classes_and_static_properties_output.swift @@ -13,13 +13,13 @@ import Foundation @objc public class MyClassWrapper: NSObject { var wrappedInstance: MyClass - @objc public var customGetterVariable: MyOtherClassWrapper { + @objc static public var customGetterVariable: MyOtherClassWrapper { get { MyOtherClassWrapper(MyClass.customGetterVariable) } } - @objc public var customSetterVariable: MyOtherClassWrapper { + @objc static public var customSetterVariable: MyOtherClassWrapper { get { MyOtherClassWrapper(MyClass.customSetterVariable) } @@ -28,13 +28,13 @@ import Foundation } } - @objc public var customConstantProperty: MyOtherClassWrapper { + @objc static public var customConstantProperty: MyOtherClassWrapper { get { MyOtherClassWrapper(MyClass.customConstantProperty) } } - @objc public var customVariableProperty: MyOtherClassWrapper { + @objc static public var customVariableProperty: MyOtherClassWrapper { get { MyOtherClassWrapper(MyClass.customVariableProperty) } @@ -43,13 +43,13 @@ import Foundation } } - @objc public var representableGetterVariable: Int { + @objc static public var representableGetterVariable: Int { get { MyClass.representableGetterVariable } } - @objc public var representableSetterVariable: Int { + @objc static public var representableSetterVariable: Int { get { MyClass.representableSetterVariable } @@ -58,13 +58,13 @@ import Foundation } } - @objc public var representableConstantProperty: Int { + @objc static public var representableConstantProperty: Int { get { MyClass.representableConstantProperty } } - @objc public var representableVariableProperty: Int { + @objc static public var representableVariableProperty: Int { get { MyClass.representableVariableProperty } diff --git a/pkgs/swift2objc/test/integration/global_variables_and_functions_input.swift b/pkgs/swift2objc/test/integration/global_variables_and_functions_input.swift new file mode 100644 index 000000000..cd838a0df --- /dev/null +++ b/pkgs/swift2objc/test/integration/global_variables_and_functions_input.swift @@ -0,0 +1,21 @@ +import Foundation + +public var globalRepresentableVariable = 1 +public let globalRepresentableConstant = 1 + +public var globalCustomVariable = MyOtherClass() +public let globalCustomConstant = MyOtherClass() + +public func globalCustomFunction(label1 param1: Int, param2: MyOtherClass) -> MyOtherClass { + return MyOtherClass() +} + +public func globalRepresentableFunction() -> Void { + 1 + 1 +} + +public func globalRepresentableFunctionWrapper() { + globalRepresentableFunction() +} + +public class MyOtherClass {} diff --git a/pkgs/swift2objc/test/integration/global_variables_and_functions_output.swift b/pkgs/swift2objc/test/integration/global_variables_and_functions_output.swift new file mode 100644 index 000000000..79e680af0 --- /dev/null +++ b/pkgs/swift2objc/test/integration/global_variables_and_functions_output.swift @@ -0,0 +1,56 @@ +// Test preamble text + +import Foundation + +@objc public class GlobalsWrapper: NSObject { + @objc static public var globalCustomConstantWrapper: MyOtherClassWrapper { + get { + MyOtherClassWrapper(globalCustomConstant) + } + } + + @objc static public var globalCustomVariableWrapper: MyOtherClassWrapper { + get { + MyOtherClassWrapper(globalCustomVariable) + } + set { + globalCustomVariable = newValue.wrappedInstance + } + } + + @objc static public var globalRepresentableConstantWrapper: Int { + get { + globalRepresentableConstant + } + } + + @objc static public var globalRepresentableVariableWrapper: Int { + get { + globalRepresentableVariable + } + set { + globalRepresentableVariable = newValue + } + } + + @objc static public func globalCustomFunctionWrapper(label1 param1: Int, param2: MyOtherClassWrapper) -> MyOtherClassWrapper { + let result = globalCustomFunction(label1: param1, param2: param2.wrappedInstance) + return MyOtherClassWrapper(result) + } + + @objc static public func globalRepresentableFunctionWrapper1() -> Void { + return globalRepresentableFunction() + } + + @objc static public func globalRepresentableFunctionWrapperWrapper() { + globalRepresentableFunctionWrapper() + } +} + +@objc public class MyOtherClassWrapper: NSObject { + var wrappedInstance: MyOtherClass + + init(_ wrappedInstance: MyOtherClass) { + self.wrappedInstance = wrappedInstance + } +} diff --git a/pkgs/swift2objc/test/integration/structs_and_properties_input.swift b/pkgs/swift2objc/test/integration/structs_and_properties_input.swift index a48fb9090..d1793f9be 100644 --- a/pkgs/swift2objc/test/integration/structs_and_properties_input.swift +++ b/pkgs/swift2objc/test/integration/structs_and_properties_input.swift @@ -1,12 +1,12 @@ import Foundation public struct MyStruct { - public var customSetterVariable: MyOtherStruct { + public var customSetterProperty: MyOtherStruct { get { MyOtherStruct() } set { } } - public var customGetterVariable: MyOtherStruct { + public var customGetterProperty: MyOtherStruct { get { MyOtherStruct() } } @@ -14,15 +14,17 @@ public struct MyStruct { public let customConstantProperty: MyOtherStruct - public var representableSetterVariable: Int { + public var representableSetterProperty: Int { get { 1 } set { } } - public var representableGetterVariable: Int { + public var representableGetterProperty: Int { get { 1 } } + public var implicitGetterProperty: Int { 1 } + public var representableVariableProperty: Int public let representableConstantProperty: Int diff --git a/pkgs/swift2objc/test/integration/structs_and_properties_output.swift b/pkgs/swift2objc/test/integration/structs_and_properties_output.swift index c98bff86e..535abb0cd 100644 --- a/pkgs/swift2objc/test/integration/structs_and_properties_output.swift +++ b/pkgs/swift2objc/test/integration/structs_and_properties_output.swift @@ -13,18 +13,18 @@ import Foundation @objc public class MyStructWrapper: NSObject { var wrappedInstance: MyStruct - @objc public var customGetterVariable: MyOtherStructWrapper { + @objc public var customGetterProperty: MyOtherStructWrapper { get { - MyOtherStructWrapper(wrappedInstance.customGetterVariable) + MyOtherStructWrapper(wrappedInstance.customGetterProperty) } } - @objc public var customSetterVariable: MyOtherStructWrapper { + @objc public var customSetterProperty: MyOtherStructWrapper { get { - MyOtherStructWrapper(wrappedInstance.customSetterVariable) + MyOtherStructWrapper(wrappedInstance.customSetterProperty) } set { - wrappedInstance.customSetterVariable = newValue.wrappedInstance + wrappedInstance.customSetterProperty = newValue.wrappedInstance } } @@ -43,18 +43,24 @@ import Foundation } } - @objc public var representableGetterVariable: Int { + @objc public var implicitGetterProperty: Int { get { - wrappedInstance.representableGetterVariable + wrappedInstance.implicitGetterProperty } } - @objc public var representableSetterVariable: Int { + @objc public var representableGetterProperty: Int { get { - wrappedInstance.representableSetterVariable + wrappedInstance.representableGetterProperty + } + } + + @objc public var representableSetterProperty: Int { + get { + wrappedInstance.representableSetterProperty } set { - wrappedInstance.representableSetterVariable = newValue + wrappedInstance.representableSetterProperty = newValue } } diff --git a/pkgs/swift2objc/test/integration/structs_and_static_properties_output.swift b/pkgs/swift2objc/test/integration/structs_and_static_properties_output.swift index ed417f169..7633573ed 100644 --- a/pkgs/swift2objc/test/integration/structs_and_static_properties_output.swift +++ b/pkgs/swift2objc/test/integration/structs_and_static_properties_output.swift @@ -13,13 +13,13 @@ import Foundation @objc public class MyStructWrapper: NSObject { var wrappedInstance: MyStruct - @objc public var customGetterVariable: MyOtherStructWrapper { + @objc static public var customGetterVariable: MyOtherStructWrapper { get { MyOtherStructWrapper(MyStruct.customGetterVariable) } } - @objc public var customSetterVariable: MyOtherStructWrapper { + @objc static public var customSetterVariable: MyOtherStructWrapper { get { MyOtherStructWrapper(MyStruct.customSetterVariable) } @@ -28,13 +28,13 @@ import Foundation } } - @objc public var customConstantProperty: MyOtherStructWrapper { + @objc static public var customConstantProperty: MyOtherStructWrapper { get { MyOtherStructWrapper(MyStruct.customConstantProperty) } } - @objc public var customVariableProperty: MyOtherStructWrapper { + @objc static public var customVariableProperty: MyOtherStructWrapper { get { MyOtherStructWrapper(MyStruct.customVariableProperty) } @@ -43,13 +43,13 @@ import Foundation } } - @objc public var representableGetterVariable: Int { + @objc static public var representableGetterVariable: Int { get { MyStruct.representableGetterVariable } } - @objc public var representableSetterVariable: Int { + @objc static public var representableSetterVariable: Int { get { MyStruct.representableSetterVariable } @@ -58,13 +58,13 @@ import Foundation } } - @objc public var representableConstantProperty: Int { + @objc static public var representableConstantProperty: Int { get { MyStruct.representableConstantProperty } } - @objc public var representableVariableProperty: Int { + @objc static public var representableVariableProperty: Int { get { MyStruct.representableVariableProperty }