Skip to content

Commit bc1ca8c

Browse files
authored
[swift2objc] Visitor infra (#1834)
1 parent 3832e4b commit bc1ca8c

25 files changed

+517
-304
lines changed

pkgs/swift2objc/lib/src/ast/_core/interfaces/declaration.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

5+
import '../../ast_node.dart';
56
import '../shared/referred_type.dart';
67

78
/// A common interface for all Swift entities declarations.
8-
abstract interface class Declaration {
9+
abstract interface class Declaration implements AstNode {
910
abstract final String id;
1011
abstract final String name;
1112
}

pkgs/swift2objc/lib/src/ast/_core/interfaces/nestable_declaration.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

5+
import '../../ast_node.dart';
56
import 'declaration.dart';
67

78
/// A Swift entity that can be nested inside other declarations.
8-
abstract interface class NestableDeclaration implements Declaration {
9+
abstract interface class NestableDeclaration implements Declaration, AstNode {
910
abstract NestableDeclaration? nestingParent;
1011
abstract final List<NestableDeclaration> nestedDeclarations;
1112
}

pkgs/swift2objc/lib/src/ast/_core/shared/parameter.dart

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

5+
import '../../ast_node.dart';
56
import 'referred_type.dart';
67

78
/// Describes parameters of function-like entities (e.g methods).
8-
class Parameter {
9+
class Parameter extends AstNode {
910
String name;
1011
String? internalName;
1112
ReferredType type;
@@ -18,4 +19,10 @@ class Parameter {
1819

1920
@override
2021
String toString() => '$name $internalName: $type';
22+
23+
@override
24+
void visitChildren(Visitor visitor) {
25+
super.visitChildren(visitor);
26+
visitor.visit(type);
27+
}
2128
}

pkgs/swift2objc/lib/src/ast/_core/shared/referred_type.dart

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,30 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

5+
import '../../ast_node.dart';
56
import '../interfaces/declaration.dart';
67
import '../interfaces/nestable_declaration.dart';
78
import '../interfaces/objc_annotatable.dart';
89

910
/// Describes a type reference in declaration of Swift
1011
/// entities (e.g a method return type).
1112
/// See `DeclaredType` and `GenericType` for concrete implementation.
12-
sealed class ReferredType {
13+
sealed class ReferredType extends AstNode {
1314
abstract final bool isObjCRepresentable;
1415

1516
abstract final String swiftType;
1617

1718
bool sameAs(ReferredType other);
1819

1920
const ReferredType();
21+
22+
@override
23+
void visit(Visitation visitation) => visitation.visitReferredType(this);
2024
}
2125

2226
/// Describes a reference of a declared type (user-defined or built-in).
23-
class DeclaredType<T extends Declaration> implements ReferredType {
27+
class DeclaredType<T extends Declaration> extends AstNode
28+
implements ReferredType {
2429
final String id;
2530

2631
String get name {
@@ -52,11 +57,21 @@ class DeclaredType<T extends Declaration> implements ReferredType {
5257

5358
@override
5459
String toString() => name;
60+
61+
@override
62+
void visit(Visitation visitation) => visitation.visitDeclaredType(this);
63+
64+
@override
65+
void visitChildren(Visitor visitor) {
66+
super.visitChildren(visitor);
67+
visitor.visit(declaration);
68+
visitor.visitAll(typeParams);
69+
}
5570
}
5671

5772
/// Describes a reference of a generic type
5873
/// (e.g a method return type `T` within a generic class).
59-
class GenericType implements ReferredType {
74+
class GenericType extends AstNode implements ReferredType {
6075
final String id;
6176

6277
final String name;
@@ -77,10 +92,13 @@ class GenericType implements ReferredType {
7792

7893
@override
7994
String toString() => name;
95+
96+
@override
97+
void visit(Visitation visitation) => visitation.visitGenericType(this);
8098
}
8199

82100
/// An optional type, like Dart's nullable types. Eg `String?`.
83-
class OptionalType implements ReferredType {
101+
class OptionalType extends AstNode implements ReferredType {
84102
final ReferredType child;
85103

86104
@override
@@ -97,4 +115,13 @@ class OptionalType implements ReferredType {
97115

98116
@override
99117
String toString() => swiftType;
118+
119+
@override
120+
void visit(Visitation visitation) => visitation.visitOptionalType(this);
121+
122+
@override
123+
void visitChildren(Visitor visitor) {
124+
super.visitChildren(visitor);
125+
visitor.visit(child);
126+
}
100127
}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'visitor.dart';
6+
export 'visitor.dart';
7+
8+
/// Base interface of all AST nodes, including types, bindings, and other
9+
/// elements.
10+
///
11+
/// Anything that we might want to visit should be an [AstNode]. Most things
12+
/// that contain [AstNode]s should also be [AstNode]s (except for top level
13+
/// objects like `Library` that are in charge of running the visitors, or
14+
/// caching objects like `BindingsIndex` or `Writer`).
15+
///
16+
/// The AST and visitor infrastucture is pretty complicated, so here are the
17+
/// common tasks you might need to do as a maintainer:
18+
///
19+
/// ## Creating a new type of [AstNode]
20+
///
21+
/// For example, adding a class `Foo` which extends `Bar`, which somewhere up
22+
/// the hierarchy extends [AstNode]:
23+
///
24+
/// 1. Write your `Foo` class and extend `Bar`.
25+
/// 2. If `Foo` has children (ie fields that are [AstNode]s), override the
26+
/// `visitChildren` method. Make sure to call `super.visitChildren`
27+
/// so that `Bar`'s children are also visited. If all `Foo`'s children
28+
/// are inherited from `Bar`, it's not necessary to implement this method.
29+
/// ```dart
30+
/// @override
31+
/// void visitChildren(Visitor visitor) {
32+
/// super.visitChildren(visitor);
33+
/// visitor.visit(myChild);
34+
/// visitor.visitAll(myChildList);
35+
/// }
36+
/// ```
37+
///
38+
/// Since there are many AstNodes that no one is ever going to need to visit,
39+
/// we're using a lazy-loading policy for the `visitFoo` method. When someone
40+
/// wants to write a [Visitation] that is specific to `Foo` (ie not covered by
41+
/// `visitBar`), that's when the `visitFoo` function will be added.
42+
///
43+
/// The steps are essentially the same to change an existing class to extend
44+
/// [AstNode].
45+
///
46+
/// ## Creating a new [Visitation]
47+
///
48+
/// 1. Write the visitation as a class that extends [Visitation]. The class may
49+
/// be stateful, but shouldn't care about the order that nodes are visited.
50+
/// This class may mutate the nodes, but shouldn't assume that all children
51+
/// have finished being visited when processing the parent.
52+
/// 2. Override the visit methods for whichever type you're interested in.
53+
/// ```dart
54+
/// @override
55+
/// void visitFoo(Foo node) {
56+
/// // Typically this method would visit the children, but that's not always
57+
/// // what you want to do.
58+
/// node.visitChildren(this);
59+
///
60+
/// // It's not necessarily true that all the children have finished being
61+
/// // visited at this point.
62+
///
63+
/// // The rest of this method can edit the node in-place.
64+
/// }
65+
/// ```
66+
/// 3. If you want to visit `Foo` specifically (and not its supertypes), but
67+
/// there's no `visitFoo` method to override, add a `visitFoo` method to
68+
/// [Visitation]. Assuming `Foo` extends `Bar`, `visitFoo` should delegate to
69+
/// `visitBar`:
70+
/// `void visitFoo(Foo node) => visitBar(node);`
71+
/// 4. Then add a `visit` method to `Foo` that invokes `visitFoo`:
72+
/// ```dart
73+
/// @override
74+
/// void visit(Visitation visitation) => visitation.visitFoo(this);
75+
/// ```
76+
/// 5. Repeat 3 and 4 for `Bar` and any other supertypes as necessary.
77+
///
78+
/// ## Running a [Visitation]
79+
///
80+
/// 1. Construct the [Visitation] and wrap it in a [Visitor]:
81+
/// `final visitor = Visitor(MyFancyVisitation(1, 2, 3));`
82+
/// 2. Invoke the [Visitor] on the root nodes of the AST:
83+
/// ```dart
84+
/// visitor.visit(someRootNode);
85+
/// visitor.visitAll(listOfRootNodes);
86+
/// ```
87+
abstract class AstNode {
88+
const AstNode();
89+
90+
/// Visit this node. All this method does is delegate to the visit method that
91+
/// is specific to this type of node.
92+
///
93+
/// This method should be implemented for a particular type of [AstNode] only
94+
/// when there are [Visitation]s that need to visit that type specifically.
95+
void visit(Visitation visitation) => visitation.visitAstNode(this);
96+
97+
/// Visit this node's children. This is the method that actually understands
98+
/// the structure of the node. It should invoke [Visitor.visit] etc on all the
99+
/// node's children.
100+
///
101+
/// This method must be implemented if the node has children. The
102+
/// implementation should call `super.visitChildren(visitor);`.
103+
void visitChildren(Visitor visitor) {}
104+
}

pkgs/swift2objc/lib/src/ast/declarations/built_in/built_in_declaration.dart

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,11 @@
44

55
import '../../_core/interfaces/declaration.dart';
66
import '../../_core/interfaces/objc_annotatable.dart';
7+
import '../../ast_node.dart';
78

89
/// Describes a built-in Swift type (e.g Int, String, etc).
9-
enum BuiltInDeclaration implements Declaration, ObjCAnnotatable {
10-
swiftNSObject(id: 'c:objc(cs)NSObject', name: 'NSObject'),
11-
swiftString(id: 's:SS', name: 'String'),
12-
swiftInt(id: 's:Si', name: 'Int'),
13-
swiftFloat(id: 's:Sf', name: 'Float'),
14-
swiftDouble(id: 's:Sd', name: 'Double'),
15-
swiftBool(id: 's:Sb', name: 'Bool'),
16-
swiftVoid(id: 's:s4Voida', name: 'Void');
17-
10+
class BuiltInDeclaration extends AstNode
11+
implements Declaration, ObjCAnnotatable {
1812
@override
1913
final String id;
2014

@@ -28,12 +22,34 @@ enum BuiltInDeclaration implements Declaration, ObjCAnnotatable {
2822
required this.id,
2923
required this.name,
3024
});
25+
26+
@override
27+
void visit(Visitation visitation) => visitation.visitBuiltInDeclaration(this);
3128
}
3229

33-
final objectType = BuiltInDeclaration.swiftNSObject.asDeclaredType;
34-
final stringType = BuiltInDeclaration.swiftString.asDeclaredType;
35-
final intType = BuiltInDeclaration.swiftInt.asDeclaredType;
36-
final floatType = BuiltInDeclaration.swiftFloat.asDeclaredType;
37-
final doubleType = BuiltInDeclaration.swiftDouble.asDeclaredType;
38-
final boolType = BuiltInDeclaration.swiftBool.asDeclaredType;
39-
final voidType = BuiltInDeclaration.swiftVoid.asDeclaredType;
30+
const _objectDecl =
31+
BuiltInDeclaration(id: 'c:objc(cs)NSObject', name: 'NSObject');
32+
const _stringDecl = BuiltInDeclaration(id: 's:SS', name: 'String');
33+
const _intDecl = BuiltInDeclaration(id: 's:Si', name: 'Int');
34+
const _floatDecl = BuiltInDeclaration(id: 's:Sf', name: 'Float');
35+
const _doubleDecl = BuiltInDeclaration(id: 's:Sd', name: 'Double');
36+
const _boolDecl = BuiltInDeclaration(id: 's:Sb', name: 'Bool');
37+
const _voidDecl = BuiltInDeclaration(id: 's:s4Voida', name: 'Void');
38+
39+
const builtInDeclarations = [
40+
_objectDecl,
41+
_stringDecl,
42+
_intDecl,
43+
_floatDecl,
44+
_doubleDecl,
45+
_boolDecl,
46+
_voidDecl,
47+
];
48+
49+
final objectType = _objectDecl.asDeclaredType;
50+
final stringType = _stringDecl.asDeclaredType;
51+
final intType = _intDecl.asDeclaredType;
52+
final floatType = _floatDecl.asDeclaredType;
53+
final doubleType = _doubleDecl.asDeclaredType;
54+
final boolType = _boolDecl.asDeclaredType;
55+
final voidType = _voidDecl.asDeclaredType;

pkgs/swift2objc/lib/src/ast/declarations/compounds/class_declaration.dart

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,16 @@ import '../../_core/interfaces/declaration.dart';
77
import '../../_core/interfaces/nestable_declaration.dart';
88
import '../../_core/interfaces/objc_annotatable.dart';
99
import '../../_core/shared/referred_type.dart';
10+
import '../../ast_node.dart';
1011
import '../built_in/built_in_declaration.dart';
1112
import 'members/initializer_declaration.dart';
1213
import 'members/method_declaration.dart';
1314
import 'members/property_declaration.dart';
1415
import 'protocol_declaration.dart';
1516

1617
/// Describes the declaration of a Swift class.
17-
class ClassDeclaration implements CompoundDeclaration, ObjCAnnotatable {
18+
class ClassDeclaration extends AstNode
19+
implements CompoundDeclaration, ObjCAnnotatable {
1820
@override
1921
String id;
2022

@@ -75,5 +77,23 @@ class ClassDeclaration implements CompoundDeclaration, ObjCAnnotatable {
7577
this.initializers = const [],
7678
}) : assert(superClass == null ||
7779
superClass.declaration is ClassDeclaration ||
78-
superClass.declaration == BuiltInDeclaration.swiftNSObject);
80+
superClass.sameAs(objectType));
81+
82+
@override
83+
void visit(Visitation visitation) => visitation.visitClassDeclaration(this);
84+
85+
@override
86+
void visitChildren(Visitor visitor) {
87+
super.visitChildren(visitor);
88+
visitor.visitAll(properties);
89+
visitor.visitAll(methods);
90+
visitor.visitAll(conformedProtocols);
91+
visitor.visitAll(typeParams);
92+
visitor.visit(superClass);
93+
visitor.visit(wrappedInstance);
94+
visitor.visit(wrapperInitializer);
95+
visitor.visitAll(initializers);
96+
visitor.visit(nestingParent);
97+
visitor.visitAll(nestedDeclarations);
98+
}
7999
}

pkgs/swift2objc/lib/src/ast/declarations/compounds/members/initializer_declaration.dart

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,10 @@ import '../../../_core/interfaces/objc_annotatable.dart';
1010
import '../../../_core/interfaces/overridable.dart';
1111
import '../../../_core/interfaces/parameterizable.dart';
1212
import '../../../_core/shared/parameter.dart';
13+
import '../../../ast_node.dart';
1314

1415
/// Describes an initializer for a Swift compound entity (e.g, class, structs)
15-
class InitializerDeclaration
16+
class InitializerDeclaration extends AstNode
1617
implements
1718
Declaration,
1819
Executable,
@@ -62,4 +63,14 @@ class InitializerDeclaration
6263
required this.async,
6364
required this.isFailable,
6465
});
66+
67+
@override
68+
void visit(Visitation visitation) =>
69+
visitation.visitInitializerDeclaration(this);
70+
71+
@override
72+
void visitChildren(Visitor visitor) {
73+
super.visitChildren(visitor);
74+
visitor.visitAll(params);
75+
}
6576
}

0 commit comments

Comments
 (0)