Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[swift2objc] Filtering Support #1730

Merged
merged 11 commits into from
Dec 16, 2024
23 changes: 17 additions & 6 deletions pkgs/swift2objc/lib/src/config.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import 'package:path/path.dart' as path;

import 'ast/_core/interfaces/declaration.dart';

const defaultTempDirPrefix = 'swift2objc_temp_';
const symbolgraphFileSuffix = '.symbols.json';

Expand Down Expand Up @@ -32,12 +34,21 @@ class Config {
/// intermediate files after generating the wrapper
final Uri? tempDir;

const Config({
required this.input,
required this.outputFile,
this.tempDir,
this.preamble,
});
/// Filter function to filter APIs
///
/// APIs can be filtered by name
///
/// Includes all declarations by default
final bool Function(Declaration declaration) include;

static bool _defaultInclude(_) => true;

const Config(
{required this.input,
required this.outputFile,
this.tempDir,
this.preamble,
this.include = Config._defaultInclude});
}

/// Used to specify the inputs in the `config` object.
Expand Down
4 changes: 2 additions & 2 deletions pkgs/swift2objc/lib/src/generate_wrapper.dart
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ Future<void> generateWrapper(Config config) async {
};

final declarations = parseAst(symbolgraphJson);
final transformedDeclarations = transform(declarations);

final transformedDeclarations =
transform(declarations, filter: config.include);
final wrapperCode = generate(
transformedDeclarations,
moduleName: sourceModule,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ List<Declaration> parseDeclarations(ParsedSymbolgraph symbolgraph) {
return declarations.topLevelOnly;
}

// TODO(https://github.com/dart-lang/native/issues/1815): Support for extensions
Declaration parseDeclaration(
ParsedSymbol parsedSymbol,
ParsedSymbolgraph symbolgraph,
Expand Down
259 changes: 259 additions & 0 deletions pkgs/swift2objc/lib/src/transformer/_core/dependencies.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,259 @@
import '../../ast/_core/interfaces/declaration.dart';
import '../../ast/_core/interfaces/enum_declaration.dart';
import '../../ast/_core/interfaces/function_declaration.dart';
import '../../ast/_core/interfaces/variable_declaration.dart';
import '../../ast/_core/shared/parameter.dart';
import '../../ast/_core/shared/referred_type.dart';
import '../../ast/declarations/compounds/class_declaration.dart';
import '../../ast/declarations/compounds/members/initializer_declaration.dart';
import '../../ast/declarations/compounds/protocol_declaration.dart';
import '../../ast/declarations/compounds/struct_declaration.dart';

// TODO(https://github.com/dart-lang/native/issues/1814): Type restrictions have not yet been implemented in system
class DependencyVisitor {
final Iterable<Declaration> declarations;
Set<Declaration> visitedDeclarations = {};

DependencyVisitor(this.declarations);

Set<Declaration> visit(Declaration dec) {
final dependencies = <Declaration>{};

Iterable<Declaration> d = [dec];

while (true) {
final deps = d.fold<Set<String>>(
{}, (previous, element) => previous.union(visitDeclaration(element)));
final depDecls = declarations.where((d) => deps.contains(d.id));
if (depDecls.isEmpty ||
(dependencies.union(depDecls.toSet()).length) ==
dependencies.length) {
break;
} else {
dependencies.addAll(depDecls);
d = depDecls;
}
}

visitedDeclarations.addAll(dependencies);

return dependencies;
}

Set<String> visitDeclaration(Declaration decl, [Set<String>? context]) {
final cont = context ??= {};

// switch between declarations
if (decl is ClassDeclaration) {
visitClass(decl, cont);
} else if (decl is ProtocolDeclaration) {
visitProtocol(decl, cont);
} else if (decl is StructDeclaration) {
visitStruct(decl, cont);
} else if (decl is FunctionDeclaration) {
visitFunction(decl, cont);
} else if (decl is VariableDeclaration) {
visitVariable(decl, cont);
} else if (decl is EnumDeclaration) {
visitEnum(decl, cont);
}

return cont;
}

Set<String> visitEnum(EnumDeclaration decl, [Set<String>? context]) {
final cont = context ??= {};

// visit nested declarations
for (var n in decl.nestedDeclarations) {
visitDeclaration(n, cont);
}

// visit protocols
for (var p in decl.conformedProtocols) {
visitProtocol(p.declaration, cont);
}

// ensure generic types do not enter
cont.removeWhere(
(t) => decl.typeParams.map((type) => type.name).contains(t));

return cont;
}

Set<String> visitStruct(StructDeclaration decl, [Set<String>? context]) {
final cont = context ??= {};

// visit variables
for (var d in decl.properties) {
visitVariable(d, cont);
}

// visit methods
for (var m in decl.methods) {
visitFunction(m, cont);
}

// visit initializers
for (var i in decl.initializers) {
visitInitializer(i, cont);
}

// visit nested declarations
for (var n in decl.nestedDeclarations) {
visitDeclaration(n, cont);
}

// visit protocols
for (var p in decl.conformedProtocols) {
visitProtocol(p.declaration, cont);
}

// ensure generic types do not enter
cont.removeWhere(
(t) => decl.typeParams.map((type) => type.name).contains(t));

return cont;
}

Set<String> visitClass(ClassDeclaration decl, [Set<String>? context]) {
final cont = context ??= {};

// visit variables
for (var d in decl.properties) {
visitVariable(d, cont);
}

// visit methods
for (var m in decl.methods) {
visitFunction(m, cont);
}

// visit initializers
for (var i in decl.initializers) {
visitInitializer(i, cont);
}

// visit super if any
if (decl.superClass != null) {
visitDeclaration(decl.superClass!.declaration, cont);
}

// visit nested declarations
for (var n in decl.nestedDeclarations) {
visitDeclaration(n, cont);
}

// visit protocols
for (var p in decl.conformedProtocols) {
visitProtocol(p.declaration, cont);
}

// ensure generic types do not enter
cont.removeWhere(
(t) => decl.typeParams.map((type) => type.name).contains(t));

return cont;
}

Set<String> visitProtocol(ProtocolDeclaration decl, [Set<String>? context]) {
final cont = context ??= {};

// visit variables
for (var d in decl.properties) {
visitVariable(d, cont);
}

// visit methods
for (var m in decl.methods) {
visitFunction(m, cont);
}

// visit initializers
for (var i in decl.initializers) {
visitInitializer(i, cont);
}

// visit nested declarations
for (var n in decl.nestedDeclarations) {
visitDeclaration(n, cont);
}

// visit protocols
for (var p in decl.conformedProtocols) {
visitProtocol(p.declaration, cont);
}

// ensure generic types do not enter
cont.removeWhere(
(t) => decl.typeParams.map((type) => type.name).contains(t));

return cont;
}

Set<String> visitInitializer(InitializerDeclaration decl,
[Set<String>? context]) {
final cont = context ??= {};

// similar to `visitMethod`, except no return type
for (var p in decl.params) {
visitParameter(p, cont);
}

return cont;
}

Set<String> visitFunction(FunctionDeclaration decl, [Set<String>? context]) {
final cont = context ??= {};

// visit parameters
for (var p in decl.params) {
visitParameter(p, cont);
}

// ensure generic types do not enter
cont.removeWhere(
(t) => decl.typeParams.map((type) => type.name).contains(t));

// visit return type
visitType(decl.returnType, cont);

return cont;
}

Set<String> visitParameter(Parameter decl, [Set<String>? context]) {
final cont = context ??= {};

// just visit type of parameter
visitType(decl.type, cont);

return cont;
}

Set<String> visitVariable(VariableDeclaration decl, [Set<String>? context]) {
final cont = context ??= {};

// just return property type
visitType(decl.type, cont);

return cont;
}

Set<String> visitType(ReferredType type, [Set<String>? context]) {
final cont = context ??= {};

// we need to confirm the types located
// check what kind of type [type] is
switch (type) {
case DeclaredType():
cont.add(type.id);
break;
case GenericType():
// do nothing
break;
case OptionalType():
visitType(type.child, cont);
}
return cont;
}
}
26 changes: 21 additions & 5 deletions pkgs/swift2objc/lib/src/transformer/transform.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,40 @@ import '../ast/_core/interfaces/nestable_declaration.dart';
import '../ast/declarations/compounds/class_declaration.dart';
import '../ast/declarations/compounds/struct_declaration.dart';
import '../ast/declarations/globals/globals.dart';
import '_core/dependencies.dart';
import '_core/unique_namer.dart';
import 'transformers/transform_compound.dart';
import 'transformers/transform_globals.dart';

typedef TransformationMap = Map<Declaration, Declaration>;

List<Declaration> transform(List<Declaration> declarations) {
Set<Declaration> generateDependencies(
Iterable<Declaration> decls, Iterable<Declaration> allDecls) {
final visitor = DependencyVisitor(allDecls);
for (final dec in decls) {
visitor.visit(dec);
}

return visitor.visitedDeclarations;
}

/// Transforms the given declarations into the desired ObjC wrapped declarations
List<Declaration> transform(List<Declaration> declarations,
{required bool Function(Declaration) filter}) {
final transformationMap = <Declaration, Declaration>{};

final declarations0 = declarations.where(filter).toSet();
declarations0.addAll(generateDependencies(declarations0, declarations));

final globalNamer = UniqueNamer(
declarations.map((declaration) => declaration.name),
declarations0.map((declaration) => declaration.name),
);

final globals = Globals(
functions: declarations.whereType<GlobalFunctionDeclaration>().toList(),
variables: declarations.whereType<GlobalVariableDeclaration>().toList(),
functions: declarations0.whereType<GlobalFunctionDeclaration>().toList(),
variables: declarations0.whereType<GlobalVariableDeclaration>().toList(),
);
final nonGlobals = declarations
final nonGlobals = declarations0
.where(
(declaration) =>
declaration is! GlobalFunctionDeclaration &&
Expand Down
Loading
Loading