-
Notifications
You must be signed in to change notification settings - Fork 68
[swift2objc] Filtering Support #1730
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
Changes from 9 commits
0c6ae4f
6b42b1c
d9a926d
2af7024
34d05ed
93c7879
dbe4ba4
38bbbbc
19e1504
4a7de6a
94926ae
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,198 @@ | ||
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'; | ||
|
||
/// Gets the type name from a string type by removing other characters like | ||
Set<String> _getTypeNames(String type) { | ||
// Remove optional markers (?) and square brackets ([]) | ||
type = type.replaceAll(RegExp(r'\?|!|[\[\]]'), ''); | ||
|
||
// Remove annotations (words starting with @) | ||
type = type.replaceAll(RegExp(r'@\w+'), ''); | ||
|
||
// Extract unique type names using regex | ||
final matches = RegExp(r'\b\w+\b').allMatches(type); | ||
|
||
// Return unique type names as a set | ||
return matches.map((match) => match.group(0)!).toSet(); | ||
} | ||
|
||
// TODO: Type restrictions have not yet been implemented in system | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. TODOs should link to a bug. If there isn't one, file one first, then link to the bug like Same below |
||
class DependencyVisitor { | ||
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 ??= {}; | ||
|
||
// TODO: what of raw values of enums? | ||
|
||
// visit nested declarations | ||
decl.nestedDeclarations.forEach((n) => visitDeclaration(n, cont)); | ||
|
||
// visit protocols | ||
decl.conformedProtocols.forEach((p) => 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 | ||
decl.properties.forEach((d) => visitVariable(d, cont)); | ||
|
||
// visit methods | ||
decl.methods.forEach((m) => visitFunction(m, cont)); | ||
|
||
// visit initializers | ||
decl.initializers.forEach((i) => visitInitializer(i, cont)); | ||
|
||
// visit nested declarations | ||
decl.nestedDeclarations.forEach((n) => visitDeclaration(n, cont)); | ||
|
||
// visit protocols | ||
decl.conformedProtocols.forEach((p) => 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 | ||
decl.properties.forEach((d) => visitVariable(d, cont)); | ||
|
||
// visit methods | ||
decl.methods.forEach((m) => visitFunction(m, cont)); | ||
|
||
// visit initializers | ||
decl.initializers.forEach((i) => visitInitializer(i, cont)); | ||
|
||
// visit super if any | ||
if (decl.superClass != null) | ||
visitDeclaration(decl.superClass!.declaration, cont); | ||
|
||
// visit nested declarations | ||
decl.nestedDeclarations.forEach((n) => visitDeclaration(n, cont)); | ||
|
||
// visit protocols | ||
decl.conformedProtocols.forEach((p) => 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 | ||
decl.properties.forEach((d) => visitVariable(d, cont)); | ||
|
||
// visit methods | ||
decl.methods.forEach((m) => visitFunction(m, cont)); | ||
|
||
// visit initializers | ||
decl.initializers.forEach((i) => visitInitializer(i, cont)); | ||
|
||
// visit nested declarations | ||
decl.nestedDeclarations.forEach((n) => visitDeclaration(n, cont)); | ||
|
||
// visit protocols | ||
decl.conformedProtocols.forEach((p) => 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 | ||
decl.params.forEach((p) => visitParameter(p, cont)); | ||
|
||
return cont; | ||
} | ||
|
||
Set<String> visitFunction(FunctionDeclaration decl, [Set<String>? context]) { | ||
final cont = context ??= {}; | ||
|
||
// visit parameters | ||
decl.params.forEach((p) => 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); | ||
|
||
// TODO: what of type restrictions (`... where T.Element: CustomStringConvertible`) | ||
|
||
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 | ||
// at the moment, we can perform simple regex to clean up text characters | ||
|
||
// since we are making such visitations on normal declarations in a file, | ||
// we do not need to filter out primitives at the moment | ||
cont.addAll(_getTypeNames(type.swiftType)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You shouldn't have to re-parse these types from strings. We did all the parsing already, and now have a nice clean AST to work with. In this case you can just check if the |
||
|
||
return cont; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,24 +8,55 @@ 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 dependencies = <Declaration>{}; | ||
final dependencyVisitor = DependencyVisitor(); | ||
|
||
var _d = decls; | ||
|
||
while (true) { | ||
final deps = _d.fold<Set<String>>( | ||
nikeokoronkwo marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{}, | ||
(previous, element) => | ||
previous.union(dependencyVisitor.visitDeclaration(element))); | ||
final depDecls = | ||
(allDecls ?? decls).where((d) => deps.contains(d.name)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Names aren't unique (eg two nested types, nested in different parent types, can have the same name). Use the declaration Better yet, why not just have the dependency visitor construct a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Most of the types do not reference declarations directly (like function parameters, variable types), which therefore means that declarations need to be looked up eventually. The lookup is done afterwards to prevent multiple lookups and just have a single lookup per visitation. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You shouldn't ever need to look up types by name or id, after parsing is complete. The AST already has references to the declarations. Check if the |
||
if (depDecls.isEmpty || (dependencies.union(depDecls.toSet()).length) == dependencies.length) { | ||
break; | ||
} else { | ||
dependencies.addAll(depDecls); | ||
_d = depDecls; | ||
} | ||
} | ||
|
||
return dependencies; | ||
} | ||
|
||
/// Transforms the given declarations into the desired ObjC wrapped declarations | ||
List<Declaration> transform(List<Declaration> declarations, | ||
{bool Function(Declaration)? filter}) { | ||
nikeokoronkwo marked this conversation as resolved.
Show resolved
Hide resolved
|
||
final transformationMap = <Declaration, Declaration>{}; | ||
|
||
final _declarations = | ||
declarations.where(filter ?? (declaration) => true).toSet(); | ||
_declarations.addAll(generateDependencies(_declarations, allDecls: declarations)); | ||
|
||
final globalNamer = UniqueNamer( | ||
declarations.map((declaration) => declaration.name), | ||
_declarations.map((declaration) => declaration.name), | ||
); | ||
|
||
final globals = Globals( | ||
functions: declarations.whereType<GlobalFunctionDeclaration>().toList(), | ||
variables: declarations.whereType<GlobalVariableDeclaration>().toList(), | ||
functions: _declarations.whereType<GlobalFunctionDeclaration>().toList(), | ||
variables: _declarations.whereType<GlobalVariableDeclaration>().toList(), | ||
); | ||
final nonGlobals = declarations | ||
final nonGlobals = _declarations | ||
.where( | ||
(declaration) => | ||
declaration is! GlobalFunctionDeclaration && | ||
|
Uh oh!
There was an error while loading. Please reload this page.