diff --git a/pkgs/ffigen/lib/src/header_parser/parser.dart b/pkgs/ffigen/lib/src/header_parser/parser.dart index 1f88645835..a5f1cbff65 100644 --- a/pkgs/ffigen/lib/src/header_parser/parser.dart +++ b/pkgs/ffigen/lib/src/header_parser/parser.dart @@ -175,39 +175,33 @@ List transformBindings(List bindings, Context context) { applyConfigFiltersVisitation.indirectlyIncluded, ); - final findByValueCompoundsVisitation = FindByValueCompoundsVisitation(); - visit( + final byValueCompounds = visit( context, - findByValueCompoundsVisitation, + FindByValueCompoundsVisitation(), FindByValueCompoundsVisitation.rootNodes(included), - ); - final byValueCompounds = findByValueCompoundsVisitation.byValueCompounds; + ).byValueCompounds; visit( context, ClearOpaqueCompoundMembersVisitation(config, byValueCompounds, included), bindings, ); - final findTransitiveDepsVisitation = FindTransitiveDepsVisitation(); - visit(context, findTransitiveDepsVisitation, included); - final transitives = findTransitiveDepsVisitation.transitives; - final findDirectTransitiveDepsVisitation = FindDirectTransitiveDepsVisitation( - config, + final transitives = visit( + context, + FindTransitiveDepsVisitation(), included, - directlyIncluded, - ); - visit(context, findDirectTransitiveDepsVisitation, included); - final directTransitives = - findDirectTransitiveDepsVisitation.directTransitives; - - final listBindingsVisitation = ListBindingsVisitation( - config, + ).transitives; + final directTransitives = visit( + context, + FindDirectTransitiveDepsVisitation(config, included, directlyIncluded), included, - transitives, - directTransitives, - ); - visit(context, listBindingsVisitation, bindings); - final finalBindings = listBindingsVisitation.bindings; + ).directTransitives; + + final finalBindings = visit( + context, + ListBindingsVisitation(config, included, transitives, directTransitives), + bindings, + ).bindings; visit(context, MarkBindingsVisitation(finalBindings), bindings); final finalBindingsList = finalBindings.toList(); diff --git a/pkgs/swift2objc/lib/src/ast/declarations/compounds/class_declaration.dart b/pkgs/swift2objc/lib/src/ast/declarations/compounds/class_declaration.dart index b031c3574f..3f8b05bdea 100644 --- a/pkgs/swift2objc/lib/src/ast/declarations/compounds/class_declaration.dart +++ b/pkgs/swift2objc/lib/src/ast/declarations/compounds/class_declaration.dart @@ -49,6 +49,9 @@ class ClassDeclaration extends AstNode /// If this class is a wrapper for another entity (class, struct, etc) bool isWrapper; + // Whether this is a stub wrapper. + bool isStub; + /// An instance of the original entity that this class is wraping PropertyDeclaration? wrappedInstance; @@ -77,6 +80,7 @@ class ClassDeclaration extends AstNode this.hasObjCAnnotation = false, this.superClass, this.isWrapper = false, + this.isStub = false, this.wrappedInstance, this.wrapperInitializer, this.initializers = const [], diff --git a/pkgs/swift2objc/lib/src/ast/declarations/globals/globals.dart b/pkgs/swift2objc/lib/src/ast/declarations/globals/globals.dart index 1d548624d5..e0e2c54037 100644 --- a/pkgs/swift2objc/lib/src/ast/declarations/globals/globals.dart +++ b/pkgs/swift2objc/lib/src/ast/declarations/globals/globals.dart @@ -62,6 +62,10 @@ class GlobalFunctionDeclaration extends AstNode implements FunctionDeclaration { this.async = false, }); + @override + void visit(Visitation visitation) => + visitation.visitGlobalFunctionDeclaration(this); + @override void visitChildren(Visitor visitor) { super.visitChildren(visitor); @@ -104,6 +108,10 @@ class GlobalVariableDeclaration extends AstNode implements VariableDeclaration { required this.async, }) : assert(!(throws && !isConstant)); + @override + void visit(Visitation visitation) => + visitation.visitGlobalVariableDeclaration(this); + @override void visitChildren(Visitor visitor) { super.visitChildren(visitor); diff --git a/pkgs/swift2objc/lib/src/generator/generators/class_generator.dart b/pkgs/swift2objc/lib/src/generator/generators/class_generator.dart index ff65f8d595..47be9d25de 100644 --- a/pkgs/swift2objc/lib/src/generator/generators/class_generator.dart +++ b/pkgs/swift2objc/lib/src/generator/generators/class_generator.dart @@ -13,6 +13,7 @@ import '../generator.dart'; List generateClass(ClassDeclaration declaration) { return [ + if (declaration.isStub) ..._generateStubComment(declaration), ...generateAvailability(declaration), '${_generateClassHeader(declaration)} {', ...[ @@ -26,6 +27,14 @@ List generateClass(ClassDeclaration declaration) { ]; } +List _generateStubComment(ClassDeclaration declaration) { + final wrappedType = declaration.wrappedInstance!.type.swiftType; + return [ + '// This wrapper is a stub. To generate the full wrapper, add $wrappedType', + "// to your config's include function.", + ]; +} + String _generateClassHeader(ClassDeclaration declaration) { final header = StringBuffer(); diff --git a/pkgs/swift2objc/lib/src/transformer/_core/dependencies.dart b/pkgs/swift2objc/lib/src/transformer/_core/dependencies.dart index 032367ac8b..db72caceec 100644 --- a/pkgs/swift2objc/lib/src/transformer/_core/dependencies.dart +++ b/pkgs/swift2objc/lib/src/transformer/_core/dependencies.dart @@ -4,22 +4,45 @@ import '../../ast/_core/interfaces/compound_declaration.dart'; import '../../ast/_core/interfaces/declaration.dart'; +import '../../ast/declarations/compounds/class_declaration.dart'; +import '../../ast/declarations/compounds/protocol_declaration.dart'; +import '../../ast/declarations/compounds/struct_declaration.dart'; import '../../ast/declarations/globals/globals.dart'; import '../../ast/visitor.dart'; -class DependencyVisitation extends Visitation { - Set topLevelDeclarations = {}; +class FindIncludesVisitation extends Visitation { + final bool Function(Declaration) filter; + final includes = {}; + + FindIncludesVisitation(this.filter); + + @override + void visitDeclaration(Declaration node) { + if (filter(node)) { + includes.add(node); + node.visitChildren(visitor); + } + } +} + +class ListDeclsVisitation extends Visitation { + final Set includes; + final Set directTransitives; + final topLevelDecls = {}; + final stubDecls = {}; + + ListDeclsVisitation(this.includes, this.directTransitives); @override void visitGlobalFunctionDeclaration(GlobalFunctionDeclaration node) { node.visitChildren(visitor); - topLevelDeclarations.add(node); + topLevelDecls.add(node); } @override void visitGlobalVariableDeclaration(GlobalVariableDeclaration node) { node.visitChildren(visitor); - topLevelDeclarations.add(node); + topLevelDecls.add(node); } @override @@ -27,6 +50,58 @@ class DependencyVisitation extends Visitation { node.visitChildren(visitor); // Don't add nested classes etc to the top level declarations. - if (node.nestingParent == null) topLevelDeclarations.add(node); + if (node.nestingParent == null) topLevelDecls.add(node); + + if (!includes.contains(node) && directTransitives.contains(node)) { + stubDecls.add(node); + } + } +} + +class FindDirectTransitiveDepsVisitation extends Visitation { + final Set includes; + final directTransitives = {}; + + FindDirectTransitiveDepsVisitation(this.includes); + + void _visitImpl(Declaration node, bool forceVisitChildren) { + directTransitives.add(node); + if (forceVisitChildren || includes.contains(node)) { + node.visitChildren(visitor); + } + } + + @override + void visitDeclaration(Declaration node) => _visitImpl(node, true); + + @override + void visitClassDeclaration(ClassDeclaration node) { + _visitImpl(node, false); + + // Always visit the super type, protocols, and nesting parent, regardless of + // whether the node is directly included. This ensures that super types etc + // of stubs are also stubs, rather than being omitted like the rest of the + // stub's children. + visitor.visit(node.superClass); + visitor.visitAll(node.conformedProtocols); + visitor.visit(node.nestingParent); + } + + @override + void visitProtocolDeclaration(ProtocolDeclaration node) { + _visitImpl(node, false); + + // See visitClassDeclaration. + visitor.visitAll(node.conformedProtocols); + visitor.visit(node.nestingParent); + } + + @override + void visitStructDeclaration(StructDeclaration node) { + _visitImpl(node, false); + + // See visitClassDeclaration. + visitor.visitAll(node.conformedProtocols); + visitor.visit(node.nestingParent); } } diff --git a/pkgs/swift2objc/lib/src/transformer/_core/primitive_wrappers.dart b/pkgs/swift2objc/lib/src/transformer/_core/primitive_wrappers.dart index 685ddf4b74..97f3ea123d 100644 --- a/pkgs/swift2objc/lib/src/transformer/_core/primitive_wrappers.dart +++ b/pkgs/swift2objc/lib/src/transformer/_core/primitive_wrappers.dart @@ -46,7 +46,7 @@ ReferredType _createWrapperClass(DeclaredType primitiveType) { (ReferredType, bool) maybeGetPrimitiveWrapper( ReferredType type, bool shouldWrapPrimitives, - TransformationMap transformationMap, + TransformationState state, ) { if (type is! DeclaredType || !shouldWrapPrimitives) { return (type, false); @@ -57,7 +57,7 @@ ReferredType _createWrapperClass(DeclaredType primitiveType) { return (type, false); } - transformationMap[type.declaration] = (wrapper as DeclaredType).declaration; + state.map[type.declaration] = (wrapper as DeclaredType).declaration; return (wrapper, true); } diff --git a/pkgs/swift2objc/lib/src/transformer/_core/utils.dart b/pkgs/swift2objc/lib/src/transformer/_core/utils.dart index c37cd0b0dc..2caddc7f18 100644 --- a/pkgs/swift2objc/lib/src/transformer/_core/utils.dart +++ b/pkgs/swift2objc/lib/src/transformer/_core/utils.dart @@ -18,10 +18,10 @@ import 'unique_namer.dart'; // that weird. Refactor this as part of the transformer refactor. (String value, ReferredType type) maybeWrapValue(ReferredType type, - String value, UniqueNamer globalNamer, TransformationMap transformationMap, + String value, UniqueNamer globalNamer, TransformationState state, {bool shouldWrapPrimitives = false}) { final (wrappedPrimitiveType, returnsWrappedPrimitive) = - maybeGetPrimitiveWrapper(type, shouldWrapPrimitives, transformationMap); + maybeGetPrimitiveWrapper(type, shouldWrapPrimitives, state); if (returnsWrappedPrimitive) { return ( '${(wrappedPrimitiveType as DeclaredType).name}($value)', @@ -38,15 +38,14 @@ import 'unique_namer.dart'; } else if (type is DeclaredType) { final declaration = type.declaration; if (declaration is TypealiasDeclaration) { - return maybeWrapValue( - declaration.target, value, globalNamer, transformationMap, + return maybeWrapValue(declaration.target, value, globalNamer, state, shouldWrapPrimitives: shouldWrapPrimitives); } final transformedTypeDeclaration = transformDeclaration( declaration, globalNamer, - transformationMap, + state, ); return ( @@ -55,7 +54,7 @@ import 'unique_namer.dart'; ); } else if (type is OptionalType) { final (newValue, newType) = - maybeWrapValue(type.child, '$value!', globalNamer, transformationMap); + maybeWrapValue(type.child, '$value!', globalNamer, state); return ( '$value == nil ? nil : $newValue', OptionalType(newType), diff --git a/pkgs/swift2objc/lib/src/transformer/transform.dart b/pkgs/swift2objc/lib/src/transformer/transform.dart index aef6b5dd3b..4eb6cba365 100644 --- a/pkgs/swift2objc/lib/src/transformer/transform.dart +++ b/pkgs/swift2objc/lib/src/transformer/transform.dart @@ -16,28 +16,35 @@ import '_core/unique_namer.dart'; import 'transformers/transform_compound.dart'; import 'transformers/transform_globals.dart'; -typedef TransformationMap = Map; - -Set generateDependencies(Iterable decls) => - visit(DependencyVisitation(), decls).topLevelDeclarations; +class TransformationState { + final map = {}; + final stubs = {}; +} /// Transforms the given declarations into the desired ObjC wrapped declarations List transform(List declarations, {required bool Function(Declaration) filter}) { - final transformationMap = {}; + final state = TransformationState(); - final declarations0 = declarations.where(filter).toSet(); - declarations0.addAll(generateDependencies(declarations0)); + final includes = visit(FindIncludesVisitation(filter), declarations).includes; + final directTransitives = + visit(FindDirectTransitiveDepsVisitation(includes), includes) + .directTransitives; + final allBindings = includes.union(directTransitives); + final listDecls = + visit(ListDeclsVisitation(includes, directTransitives), allBindings); + final topLevelDecls = listDecls.topLevelDecls; + state.stubs.addAll(listDecls.stubDecls); final globalNamer = UniqueNamer( - declarations0.map((declaration) => declaration.name), + topLevelDecls.map((declaration) => declaration.name), ); final globals = Globals( - functions: declarations0.whereType().toList(), - variables: declarations0.whereType().toList(), + functions: topLevelDecls.whereType().toList(), + variables: topLevelDecls.whereType().toList(), ); - final nonGlobals = declarations0 + final nonGlobals = topLevelDecls .where( (declaration) => declaration is! GlobalFunctionDeclaration && @@ -46,38 +53,34 @@ List transform(List declarations, .toList(); final transformedDeclarations = [ - ...nonGlobals - .map( - (d) => maybeTransformDeclaration(d, globalNamer, transformationMap), - ) - .nonNulls, - if (globals.functions.isNotEmpty || globals.variables.isNotEmpty) - transformGlobals(globals, globalNamer, transformationMap), - ]; + ...nonGlobals.map( + (d) => maybeTransformDeclaration(d, globalNamer, state), + ), + transformGlobals(globals, globalNamer, state), + ].nonNulls.toList(); - return (transformedDeclarations + - _getPrimitiveWrapperClasses(transformationMap)) + return (transformedDeclarations + _getPrimitiveWrapperClasses(state)) ..sort((Declaration a, Declaration b) => a.id.compareTo(b.id)); } Declaration transformDeclaration( Declaration declaration, UniqueNamer parentNamer, - TransformationMap transformationMap, { + TransformationState state, { bool nested = false, }) => - maybeTransformDeclaration(declaration, parentNamer, transformationMap, + maybeTransformDeclaration(declaration, parentNamer, state, nested: nested) ?? declaration; Declaration? maybeTransformDeclaration( Declaration declaration, UniqueNamer parentNamer, - TransformationMap transformationMap, { + TransformationState state, { bool nested = false, }) { - if (transformationMap.containsKey(declaration)) { - return transformationMap[declaration]; + if (state.map.containsKey(declaration)) { + return state.map[declaration]; } if (declaration is InnerNestableDeclaration && @@ -91,16 +94,15 @@ Declaration? maybeTransformDeclaration( ClassDeclaration() || StructDeclaration() => transformCompound( declaration as CompoundDeclaration, parentNamer, - transformationMap, + state, ), TypealiasDeclaration() => null, _ => throw UnimplementedError(), }; } -List _getPrimitiveWrapperClasses( - TransformationMap transformationMap) { - return transformationMap.entries +List _getPrimitiveWrapperClasses(TransformationState state) { + return state.map.entries .where((entry) => entry.key is BuiltInDeclaration) .map((entry) => entry.value) .nonNulls diff --git a/pkgs/swift2objc/lib/src/transformer/transformers/transform_compound.dart b/pkgs/swift2objc/lib/src/transformer/transformers/transform_compound.dart index 997691896f..f713ea5a37 100644 --- a/pkgs/swift2objc/lib/src/transformer/transformers/transform_compound.dart +++ b/pkgs/swift2objc/lib/src/transformer/transformers/transform_compound.dart @@ -18,11 +18,9 @@ import 'transform_function.dart'; import 'transform_initializer.dart'; import 'transform_variable.dart'; -ClassDeclaration transformCompound( - CompoundDeclaration originalCompound, - UniqueNamer parentNamer, - TransformationMap transformationMap, -) { +ClassDeclaration transformCompound(CompoundDeclaration originalCompound, + UniqueNamer parentNamer, TransformationState state) { + final isStub = state.stubs.contains(originalCompound); final compoundNamer = UniqueNamer.inCompound(originalCompound); final wrappedCompoundInstance = PropertyDeclaration( @@ -39,65 +37,68 @@ ClassDeclaration transformCompound( hasObjCAnnotation: true, superClass: objectType, isWrapper: true, + isStub: isStub, wrappedInstance: wrappedCompoundInstance, wrapperInitializer: buildWrapperInitializer(wrappedCompoundInstance), ); - transformationMap[originalCompound] = transformedCompound; + state.map[originalCompound] = transformedCompound; transformedCompound.nestedDeclarations = originalCompound.nestedDeclarations - .map((nested) => maybeTransformDeclaration( - nested, compoundNamer, transformationMap, nested: true) - as InnerNestableDeclaration?) + .map((nested) => + maybeTransformDeclaration(nested, compoundNamer, state, nested: true) + as InnerNestableDeclaration?) .nonNulls .toList() ..sort((Declaration a, Declaration b) => a.id.compareTo(b.id)); transformedCompound.nestedDeclarations .fillNestingParents(transformedCompound); - final transformedProperties = originalCompound.properties - .map((property) => transformProperty( - property, - wrappedCompoundInstance, - parentNamer, - transformationMap, - )) - .nonNulls - .toList(); + if (!isStub) { + final transformedProperties = originalCompound.properties + .map((property) => transformProperty( + property, + wrappedCompoundInstance, + parentNamer, + state, + )) + .nonNulls + .toList(); - final transformedInitializers = originalCompound.initializers - .map((initializer) => transformInitializer( - initializer, - wrappedCompoundInstance, - parentNamer, - transformationMap, - )) - .toList(); + final transformedInitializers = originalCompound.initializers + .map((initializer) => transformInitializer( + initializer, + wrappedCompoundInstance, + parentNamer, + state, + )) + .toList(); - final transformedMethods = originalCompound.methods - .map((method) => transformMethod( - method, - wrappedCompoundInstance, - parentNamer, - transformationMap, - )) - .nonNulls - .toList(); + final transformedMethods = originalCompound.methods + .map((method) => transformMethod( + method, + wrappedCompoundInstance, + parentNamer, + state, + )) + .nonNulls + .toList(); - transformedCompound.properties = transformedProperties - .whereType() - .toList() - ..sort((Declaration a, Declaration b) => a.id.compareTo(b.id)); + transformedCompound.properties = transformedProperties + .whereType() + .toList() + ..sort((Declaration a, Declaration b) => a.id.compareTo(b.id)); - transformedCompound.initializers = transformedInitializers - .whereType() - .toList() - ..sort((Declaration a, Declaration b) => a.id.compareTo(b.id)); + transformedCompound.initializers = transformedInitializers + .whereType() + .toList() + ..sort((Declaration a, Declaration b) => a.id.compareTo(b.id)); - transformedCompound.methods = (transformedMethods + - transformedProperties.whereType().toList() + - transformedInitializers.whereType().toList()) - ..sort((Declaration a, Declaration b) => a.id.compareTo(b.id)); + transformedCompound.methods = (transformedMethods + + transformedProperties.whereType().toList() + + transformedInitializers.whereType().toList()) + ..sort((Declaration a, Declaration b) => a.id.compareTo(b.id)); + } return transformedCompound; } diff --git a/pkgs/swift2objc/lib/src/transformer/transformers/transform_function.dart b/pkgs/swift2objc/lib/src/transformer/transformers/transform_function.dart index 3471c69b8c..a9e4d8e9e4 100644 --- a/pkgs/swift2objc/lib/src/transformer/transformers/transform_function.dart +++ b/pkgs/swift2objc/lib/src/transformer/transformers/transform_function.dart @@ -24,7 +24,7 @@ MethodDeclaration? transformMethod( MethodDeclaration originalMethod, PropertyDeclaration wrappedClassInstance, UniqueNamer globalNamer, - TransformationMap transformationMap, + TransformationState state, ) { if (disallowedMethods.contains(originalMethod.name)) { return null; @@ -33,7 +33,7 @@ MethodDeclaration? transformMethod( return _transformFunction( originalMethod, globalNamer, - transformationMap, + state, wrapperMethodName: originalMethod.name, originalCallStatementGenerator: (arguments) { final methodSource = originalMethod.isStatic @@ -47,12 +47,12 @@ MethodDeclaration? transformMethod( MethodDeclaration transformGlobalFunction( GlobalFunctionDeclaration globalFunction, UniqueNamer globalNamer, - TransformationMap transformationMap, + TransformationState state, ) { return _transformFunction( globalFunction, globalNamer, - transformationMap, + state, wrapperMethodName: globalNamer.makeUnique( '${globalFunction.name}Wrapper', ), @@ -66,7 +66,7 @@ MethodDeclaration transformGlobalFunction( MethodDeclaration _transformFunction( FunctionDeclaration originalFunction, UniqueNamer globalNamer, - TransformationMap transformationMap, { + TransformationState state, { required String wrapperMethodName, required String Function(String arguments) originalCallStatementGenerator, }) { @@ -78,7 +78,7 @@ MethodDeclaration _transformFunction( type: transformReferredType( param.type, globalNamer, - transformationMap, + state, ), ), ) @@ -88,7 +88,7 @@ MethodDeclaration _transformFunction( final resultName = localNamer.makeUnique('result'); final (wrapperResult, type) = maybeWrapValue( - originalFunction.returnType, resultName, globalNamer, transformationMap, + originalFunction.returnType, resultName, globalNamer, state, shouldWrapPrimitives: originalFunction.throws); final transformedMethod = MethodDeclaration( @@ -112,7 +112,7 @@ MethodDeclaration _transformFunction( localNamer, resultName, wrapperResult, - transformationMap, + state, originalCallGenerator: originalCallStatementGenerator, ); @@ -152,7 +152,7 @@ List _generateStatements( UniqueNamer localNamer, String resultName, String wrappedResult, - TransformationMap transformationMap, { + TransformationState state, { required String Function(String arguments) originalCallGenerator, }) { final arguments = generateInvocationParams( diff --git a/pkgs/swift2objc/lib/src/transformer/transformers/transform_globals.dart b/pkgs/swift2objc/lib/src/transformer/transformers/transform_globals.dart index 8423fb73c6..08d8e73486 100644 --- a/pkgs/swift2objc/lib/src/transformer/transformers/transform_globals.dart +++ b/pkgs/swift2objc/lib/src/transformer/transformers/transform_globals.dart @@ -14,11 +14,13 @@ import '../transform.dart'; import 'transform_function.dart'; import 'transform_variable.dart'; -ClassDeclaration transformGlobals( +ClassDeclaration? transformGlobals( Globals globals, UniqueNamer globalNamer, - TransformationMap transformationMap, + TransformationState state, ) { + if (globals.variables.isEmpty && globals.functions.isEmpty) return null; + final transformedGlobals = ClassDeclaration( id: 'globals'.addIdSuffix('wrapper'), name: globalNamer.makeUnique('GlobalsWrapper'), @@ -32,7 +34,7 @@ ClassDeclaration transformGlobals( .map((variable) => transformGlobalVariable( variable, globalNamer, - transformationMap, + state, )) .toList(); @@ -40,7 +42,7 @@ ClassDeclaration transformGlobals( .map((function) => transformGlobalFunction( function, globalNamer, - transformationMap, + state, )) .toList(); diff --git a/pkgs/swift2objc/lib/src/transformer/transformers/transform_initializer.dart b/pkgs/swift2objc/lib/src/transformer/transformers/transform_initializer.dart index cffbbb149a..b742fe578e 100644 --- a/pkgs/swift2objc/lib/src/transformer/transformers/transform_initializer.dart +++ b/pkgs/swift2objc/lib/src/transformer/transformers/transform_initializer.dart @@ -17,7 +17,7 @@ Declaration transformInitializer( InitializerDeclaration originalInitializer, PropertyDeclaration wrappedClassInstance, UniqueNamer globalNamer, - TransformationMap transformationMap, + TransformationState state, ) { final transformedParams = originalInitializer.params .map( @@ -27,15 +27,15 @@ Declaration transformInitializer( type: transformReferredType( param.type, globalNamer, - transformationMap, + state, ), ), ) .toList(); if (originalInitializer.async) { - final methodReturnType = transformReferredType( - wrappedClassInstance.type, globalNamer, transformationMap); + final methodReturnType = + transformReferredType(wrappedClassInstance.type, globalNamer, state); return MethodDeclaration( id: originalInitializer.id, diff --git a/pkgs/swift2objc/lib/src/transformer/transformers/transform_referred_type.dart b/pkgs/swift2objc/lib/src/transformer/transformers/transform_referred_type.dart index ea5f01fa41..558ac89b86 100644 --- a/pkgs/swift2objc/lib/src/transformer/transformers/transform_referred_type.dart +++ b/pkgs/swift2objc/lib/src/transformer/transformers/transform_referred_type.dart @@ -14,7 +14,7 @@ import '../transform.dart'; ReferredType transformReferredType( ReferredType type, UniqueNamer globalNamer, - TransformationMap transformationMap, + TransformationState state, ) { if (type.isObjCRepresentable) return type; @@ -23,16 +23,15 @@ ReferredType transformReferredType( } else if (type is DeclaredType) { final decl = type.declaration; if (decl is TypealiasDeclaration) { - return transformReferredType(decl.target, globalNamer, transformationMap); + return transformReferredType(decl.target, globalNamer, state); } return transformDeclaration( decl, globalNamer, - transformationMap, + state, ).asDeclaredType; } else if (type is OptionalType) { - return OptionalType( - transformReferredType(type.child, globalNamer, transformationMap)); + return OptionalType(transformReferredType(type.child, globalNamer, state)); } else { throw UnimplementedError('Unknown type: $type'); } diff --git a/pkgs/swift2objc/lib/src/transformer/transformers/transform_variable.dart b/pkgs/swift2objc/lib/src/transformer/transformers/transform_variable.dart index 727067428e..a1932286e4 100644 --- a/pkgs/swift2objc/lib/src/transformer/transformers/transform_variable.dart +++ b/pkgs/swift2objc/lib/src/transformer/transformers/transform_variable.dart @@ -24,7 +24,7 @@ Declaration? transformProperty( PropertyDeclaration originalProperty, PropertyDeclaration wrappedClassInstance, UniqueNamer globalNamer, - TransformationMap transformationMap, + TransformationState state, ) { if (disallowedMethods.contains(originalProperty.name)) { return null; @@ -37,7 +37,7 @@ Declaration? transformProperty( return _transformVariable( originalProperty, globalNamer, - transformationMap, + state, property: true, wrapperPropertyName: originalProperty.name, variableReferenceExpression: '$propertySource.${originalProperty.name}', @@ -47,12 +47,12 @@ Declaration? transformProperty( Declaration transformGlobalVariable( GlobalVariableDeclaration globalVariable, UniqueNamer globalNamer, - TransformationMap transformationMap, + TransformationState state, ) { return _transformVariable( globalVariable, globalNamer, - transformationMap, + state, wrapperPropertyName: globalNamer.makeUnique( '${globalVariable.name}Wrapper', ), @@ -65,7 +65,7 @@ Declaration transformGlobalVariable( Declaration _transformVariable( VariableDeclaration originalVariable, UniqueNamer globalNamer, - TransformationMap transformationMap, { + TransformationState state, { bool property = false, required String wrapperPropertyName, required String variableReferenceExpression, @@ -73,7 +73,7 @@ Declaration _transformVariable( final transformedType = transformReferredType( originalVariable.type, globalNamer, - transformationMap, + state, ); final shouldGenerateSetter = originalVariable is PropertyDeclaration @@ -91,7 +91,7 @@ Declaration _transformVariable( final resultName = localNamer.makeUnique('result'); final (wrapperResult, type) = maybeWrapValue( - originalVariable.type, resultName, globalNamer, transformationMap, + originalVariable.type, resultName, globalNamer, state, shouldWrapPrimitives: originalVariable.throws); return MethodDeclaration( @@ -140,7 +140,7 @@ Declaration _transformVariable( variableReferenceExpression, transformedProperty, globalNamer, - transformationMap, + state, ); transformedProperty.getter = PropertyStatements(getterStatements); @@ -150,7 +150,7 @@ Declaration _transformVariable( variableReferenceExpression, transformedProperty, globalNamer, - transformationMap, + state, ); transformedProperty.setter = PropertyStatements(setterStatements); } @@ -163,13 +163,13 @@ List _generateGetterStatements( String variableReferenceExpression, PropertyDeclaration transformedProperty, UniqueNamer globalNamer, - TransformationMap transformationMap, + TransformationState state, ) { final (wrappedValue, wrapperType) = maybeWrapValue( originalVariable.type, variableReferenceExpression, globalNamer, - transformationMap, + state, ); assert(wrapperType.sameAs(transformedProperty.type)); @@ -182,7 +182,7 @@ List _generateSetterStatements( String variableReference, PropertyDeclaration transformedProperty, UniqueNamer globalNamer, - TransformationMap transformationMap, + TransformationState state, ) { final (unwrappedValue, unwrappedType) = maybeUnwrapValue( transformedProperty.type, diff --git a/pkgs/swift2objc/test/integration/integration_test.dart b/pkgs/swift2objc/test/integration/integration_test.dart index 7e95a3d386..9e6398eb84 100644 --- a/pkgs/swift2objc/test/integration/integration_test.dart +++ b/pkgs/swift2objc/test/integration/integration_test.dart @@ -77,25 +77,7 @@ void main([List? args]) { expect(actualOutput, expectedOutput); expect(loggedErrors, 0); - // Try generating symbolgraph for input & output files - // to make sure the result compiles. Input file must be included cause - // it contains the definition of the entities the output code wraps. - final symbolgraphCommand = FilesInputConfig( - files: [Uri.file(inputFile), Uri.file(actualOutputFile)], - generatedModuleName: 'output_file_symbolgraph', - ).symbolgraphCommand!; - - final processResult = await Process.run( - symbolgraphCommand.executable, - symbolgraphCommand.args, - workingDirectory: tempDir, - ); - - if (processResult.exitCode != 0) { - print(processResult.stdout); - print(processResult.stderr); - } - expect(processResult.exitCode, 0); + await expectValidSwift([inputFile, actualOutputFile]); }); } }); diff --git a/pkgs/swift2objc/test/unit/filter_test.dart b/pkgs/swift2objc/test/unit/filter_test.dart index 5fc4b06371..6ecb7908a9 100644 --- a/pkgs/swift2objc/test/unit/filter_test.dart +++ b/pkgs/swift2objc/test/unit/filter_test.dart @@ -5,6 +5,7 @@ import 'dart:io'; import 'package:path/path.dart' as p; +import 'package:swift2objc/src/ast/_core/interfaces/declaration.dart'; import 'package:swift2objc/src/ast/declarations/compounds/class_declaration.dart'; import 'package:swift2objc/swift2objc.dart'; import 'package:test/test.dart'; @@ -14,22 +15,23 @@ import '../utils.dart'; void main() { group('Unit test for filter', () { final thisDir = p.join(testDir, 'unit'); + final inputFile = p.join(thisDir, 'filter_test_input.swift'); - final file = p.join(thisDir, 'filter_test_input.swift'); - test('A: Specific Files', () async { - final output = p.join(thisDir, 'filter_test_output_a.swift'); + Future runTest(String expectedOutputFile, + bool Function(Declaration declaration) include) async { + final output = p.join(thisDir, expectedOutputFile); final actualOutputFile = p.join( thisDir, - '${p.basenameWithoutExtension(output)}.test${p.extension(output)}', + '${p.basenameWithoutExtension(output)}.g.swift', ); await generateWrapper( Config( - input: FilesInputConfig(files: [Uri.file(file)]), + input: FilesInputConfig(files: [Uri.file(inputFile)]), outputFile: Uri.file(actualOutputFile), tempDir: Directory(thisDir).uri, preamble: '// Test preamble text', - include: (declaration) => declaration.name == 'Engine', + include: include, ), ); @@ -37,91 +39,45 @@ void main() { final expectedOutput = File(output).readAsStringSync(); expectString(actualOutput, expectedOutput); - }); - - test('B: Declarations of a specific type', () async { - final output = p.join(thisDir, 'filter_test_output_b.swift'); - final actualOutputFile = p.join( - thisDir, - '${p.basenameWithoutExtension(output)}.test${p.extension(output)}', - ); - - await generateWrapper( - Config( - input: FilesInputConfig(files: [Uri.file(file)]), - outputFile: Uri.file(actualOutputFile), - tempDir: Directory(thisDir).uri, - preamble: '// Test preamble text', - include: (declaration) => declaration is ClassDeclaration, - ), - ); + await expectValidSwift([inputFile, actualOutputFile]); + } - final actualOutput = await File(actualOutputFile).readAsString(); - final expectedOutput = File(output).readAsStringSync(); + test('A: Filtering by name', () async { + await runTest('filter_test_output_a.swift', + (declaration) => declaration.name == 'Engine'); + }); - expectString(actualOutput, expectedOutput); + test('B: Filtering by type', () async { + await runTest('filter_test_output_b.swift', + (declaration) => declaration is ClassDeclaration); }); test('C: Nonexistent declaration', () async { - final output = p.join(thisDir, 'filter_test_output_c.swift'); - final actualOutputFile = p.join( - thisDir, - '${p.basenameWithoutExtension(output)}.test${p.extension(output)}', - ); - - await generateWrapper( - Config( - input: FilesInputConfig(files: [Uri.file(file)]), - outputFile: Uri.file(actualOutputFile), - tempDir: Directory(thisDir).uri, - preamble: '// Test preamble text', - // The following declaration does not exist, - // so none are produced in output - include: (declaration) => declaration.name == 'Ship', - ), - ); - - final actualOutput = await File(actualOutputFile).readAsString(); - final expectedOutput = File(output).readAsStringSync(); + await runTest('filter_test_output_c.swift', + (declaration) => declaration.name == 'Ship'); + }); - expectString(actualOutput, expectedOutput); + test('D: Stubbed declarations', () async { + await runTest('filter_test_output_d.swift', + (declaration) => declaration.name == 'Vehicle'); }); tearDown(() { - if (File(p.join(thisDir, 'symbolgraph_module.abi.json')).existsSync()) { - File(p.join(thisDir, 'symbolgraph_module.abi.json')).deleteSync(); - } - if (File(p.join(thisDir, 'symbolgraph_module.swiftdoc')).existsSync()) { - File(p.join(thisDir, 'symbolgraph_module.swiftdoc')).deleteSync(); - } - if (File( - p.join(thisDir, 'symbolgraph_module.swiftmodule'), - ).existsSync()) { - File(p.join(thisDir, 'symbolgraph_module.swiftmodule')).deleteSync(); - } - if (File( - p.join(thisDir, 'symbolgraph_module.swiftsource'), - ).existsSync()) { - File(p.join(thisDir, 'symbolgraph_module.swiftsource')).deleteSync(); - } - if (File( - p.join(thisDir, 'symbolgraph_module.symbols.json'), - ).existsSync()) { - File(p.join(thisDir, 'symbolgraph_module.symbols.json')).deleteSync(); - } - if (File( - p.join(thisDir, 'symbolgraph_module.swiftsourceinfo'), - ).existsSync()) { - File( - p.join(thisDir, 'symbolgraph_module.swiftsourceinfo'), - ).deleteSync(); + void tryDelete(FileSystemEntity file) { + if (file is File && file.existsSync()) file.deleteSync(); } - for (final file in Directory( - thisDir, - ).listSync().where((t) => p.extension(t.path, 2) == '.test.swift')) { - if (file is File) file.deleteSync(); - } + tryDelete(File(p.join(thisDir, 'symbolgraph_module.abi.json'))); + tryDelete(File(p.join(thisDir, 'symbolgraph_module.swiftdoc'))); + tryDelete(File(p.join(thisDir, 'symbolgraph_module.swiftmodule'))); + tryDelete(File(p.join(thisDir, 'symbolgraph_module.swiftsource'))); + tryDelete(File(p.join(thisDir, 'symbolgraph_module.symbols.json'))); + tryDelete(File(p.join(thisDir, 'symbolgraph_module.swiftsourceinfo'))); + + Directory(thisDir) + .listSync() + .where((t) => p.extension(t.path, 2) == '.g.swift') + .forEach(tryDelete); }); }); } diff --git a/pkgs/swift2objc/test/unit/filter_test_output_b.swift b/pkgs/swift2objc/test/unit/filter_test_output_b.swift index 1d02519274..e797064f74 100644 --- a/pkgs/swift2objc/test/unit/filter_test_output_b.swift +++ b/pkgs/swift2objc/test/unit/filter_test_output_b.swift @@ -2,39 +2,15 @@ import Foundation +// This wrapper is a stub. To generate the full wrapper, add Dimensions +// to your config's include function. @objc public class DimensionsWrapper: NSObject { var wrappedInstance: Dimensions - @objc public var width: Double { - get { - wrappedInstance.width - } - } - - @objc public var height: Double { - get { - wrappedInstance.height - } - } - - @objc public var length: Double { - get { - wrappedInstance.length - } - } - init(_ wrappedInstance: Dimensions) { self.wrappedInstance = wrappedInstance } - @objc init(length: Double, width: Double, height: Double) { - wrappedInstance = Dimensions(length: length, width: width, height: height) - } - - @objc public func displayDimensions() { - return wrappedInstance.displayDimensions() - } - } @objc public class ElectricCarWrapper: NSObject { @@ -81,33 +57,15 @@ import Foundation } +// This wrapper is a stub. To generate the full wrapper, add Engine +// to your config's include function. @objc public class EngineWrapper: NSObject { var wrappedInstance: Engine - @objc public var horsepower: Int { - get { - wrappedInstance.horsepower - } - } - - @objc public var type: String { - get { - wrappedInstance.type - } - } - init(_ wrappedInstance: Engine) { self.wrappedInstance = wrappedInstance } - @objc init(type: String, horsepower: Int) { - wrappedInstance = Engine(type: type, horsepower: horsepower) - } - - @objc public func displaySpecs() { - return wrappedInstance.displaySpecs() - } - } @objc public class GarageWrapper: NSObject { diff --git a/pkgs/swift2objc/test/unit/filter_test_output_d.swift b/pkgs/swift2objc/test/unit/filter_test_output_d.swift new file mode 100644 index 0000000000..e15353db34 --- /dev/null +++ b/pkgs/swift2objc/test/unit/filter_test_output_d.swift @@ -0,0 +1,79 @@ +// Test preamble text + +import Foundation + +// This wrapper is a stub. To generate the full wrapper, add Dimensions +// to your config's include function. +@objc public class DimensionsWrapper: NSObject { + var wrappedInstance: Dimensions + + init(_ wrappedInstance: Dimensions) { + self.wrappedInstance = wrappedInstance + } + +} + +// This wrapper is a stub. To generate the full wrapper, add Engine +// to your config's include function. +@objc public class EngineWrapper: NSObject { + var wrappedInstance: Engine + + init(_ wrappedInstance: Engine) { + self.wrappedInstance = wrappedInstance + } + +} + +@objc public class VehicleWrapper: NSObject { + var wrappedInstance: Vehicle + + @objc public var dimensions: DimensionsWrapper { + get { + DimensionsWrapper(wrappedInstance.dimensions) + } + set { + wrappedInstance.dimensions = newValue.wrappedInstance + } + } + + @objc public var make: String { + get { + wrappedInstance.make + } + set { + wrappedInstance.make = newValue + } + } + + @objc public var model: String { + get { + wrappedInstance.model + } + set { + wrappedInstance.model = newValue + } + } + + @objc public var engine: EngineWrapper { + get { + EngineWrapper(wrappedInstance.engine) + } + set { + wrappedInstance.engine = newValue.wrappedInstance + } + } + + init(_ wrappedInstance: Vehicle) { + self.wrappedInstance = wrappedInstance + } + + @objc init(make: String, model: String, engine: EngineWrapper, dimensions: DimensionsWrapper) { + wrappedInstance = Vehicle(make: make, model: model, engine: engine.wrappedInstance, dimensions: dimensions.wrappedInstance) + } + + @objc public func displayInfo() { + return wrappedInstance.displayInfo() + } + +} + diff --git a/pkgs/swift2objc/test/utils.dart b/pkgs/swift2objc/test/utils.dart index c7994c5afd..233f8af954 100644 --- a/pkgs/swift2objc/test/utils.dart +++ b/pkgs/swift2objc/test/utils.dart @@ -2,8 +2,11 @@ // 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 'dart:io'; + import 'package:native_test_helpers/native_test_helpers.dart'; import 'package:path/path.dart' as p; +import 'package:swift2objc/swift2objc.dart'; import 'package:test/test.dart'; final _whitespace = RegExp(r'\s+'); @@ -21,3 +24,25 @@ String testDir = p.normalize( 'test', ), ); + +Future expectValidSwift(List files) async { + // Try generating symbolgraph for input & output files + // to make sure the result compiles. Input file must be included cause + // it contains the definition of the entities the output code wraps. + final symbolgraphCommand = FilesInputConfig( + files: files.map(Uri.file).toList(), + generatedModuleName: 'output_file_symbolgraph', + ).symbolgraphCommand!; + + final processResult = await Process.run( + symbolgraphCommand.executable, + symbolgraphCommand.args, + workingDirectory: Directory.systemTemp.createTempSync().absolute.path, + ); + + if (processResult.exitCode != 0) { + print(processResult.stdout); + print(processResult.stderr); + } + expect(processResult.exitCode, 0); +}