Skip to content

Commit

Permalink
[swift2objc] Filtering Support (#1730)
Browse files Browse the repository at this point in the history
  • Loading branch information
nikeokoronkwo authored Dec 16, 2024
1 parent a70c1a2 commit f2a0e28
Show file tree
Hide file tree
Showing 11 changed files with 826 additions and 13 deletions.
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

0 comments on commit f2a0e28

Please sign in to comment.