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] Visitor infra #1834

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +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 '../../ast_node.dart';
import '../shared/referred_type.dart';

/// A common interface for all Swift entities declarations.
abstract interface class Declaration {
abstract interface class Declaration implements AstNode {
abstract final String id;
abstract final String name;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +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 '../../ast_node.dart';
import 'declaration.dart';

/// A Swift entity that can be nested inside other declarations.
abstract interface class NestableDeclaration implements Declaration {
abstract interface class NestableDeclaration implements Declaration, AstNode {
abstract NestableDeclaration? nestingParent;
abstract final List<NestableDeclaration> nestedDeclarations;
}
Expand Down
9 changes: 8 additions & 1 deletion pkgs/swift2objc/lib/src/ast/_core/shared/parameter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +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 '../../ast_node.dart';
import 'referred_type.dart';

/// Describes parameters of function-like entities (e.g methods).
class Parameter {
class Parameter extends AstNode {
String name;
String? internalName;
ReferredType type;
Expand All @@ -18,4 +19,10 @@ class Parameter {

@override
String toString() => '$name $internalName: $type';

@override
void visitChildren(Visitor visitor) {
super.visitChildren(visitor);
visitor.visit(type);
}
}
35 changes: 31 additions & 4 deletions pkgs/swift2objc/lib/src/ast/_core/shared/referred_type.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,30 @@
// 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 '../../ast_node.dart';
import '../interfaces/declaration.dart';
import '../interfaces/nestable_declaration.dart';
import '../interfaces/objc_annotatable.dart';

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

abstract final String swiftType;

bool sameAs(ReferredType other);

const ReferredType();

@override
void visit(Visitation visitation) => visitation.visitReferredType(this);
}

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

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

@override
String toString() => name;

@override
void visit(Visitation visitation) => visitation.visitDeclaredType(this);

@override
void visitChildren(Visitor visitor) {
super.visitChildren(visitor);
visitor.visit(declaration);
visitor.visitAll(typeParams);
}
}

/// Describes a reference of a generic type
/// (e.g a method return type `T` within a generic class).
class GenericType implements ReferredType {
class GenericType extends AstNode implements ReferredType {
final String id;

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

@override
String toString() => name;

@override
void visit(Visitation visitation) => visitation.visitGenericType(this);
}

/// An optional type, like Dart's nullable types. Eg `String?`.
class OptionalType implements ReferredType {
class OptionalType extends AstNode implements ReferredType {
final ReferredType child;

@override
Expand All @@ -97,4 +115,13 @@ class OptionalType implements ReferredType {

@override
String toString() => swiftType;

@override
void visit(Visitation visitation) => visitation.visitOptionalType(this);

@override
void visitChildren(Visitor visitor) {
super.visitChildren(visitor);
visitor.visit(child);
}
}
104 changes: 104 additions & 0 deletions pkgs/swift2objc/lib/src/ast/ast_node.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
// 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 'visitor.dart';
export 'visitor.dart';

/// Base interface of all AST nodes, including types, bindings, and other
/// elements.
///
/// Anything that we might want to visit should be an [AstNode]. Most things
/// that contain [AstNode]s should also be [AstNode]s (except for top level
/// objects like `Library` that are in charge of running the visitors, or
/// caching objects like `BindingsIndex` or `Writer`).
///
/// The AST and visitor infrastucture is pretty complicated, so here are the
/// common tasks you might need to do as a maintainer:
///
/// ## Creating a new type of [AstNode]
///
/// For example, adding a class `Foo` which extends `Bar`, which somewhere up
/// the heirarchy extends [AstNode]:
///
/// 1. Write your `Foo` class and extend `Bar`.
/// 2. If `Foo` has children (ie fields that are [AstNode]s), override the
/// `visitChildren` method. Make sure to call `super.visitChildren`
/// so that `Bar`'s children are also visited. If all `Foo`'s children
/// are inherited from `Bar`, it's not necessary to implement this method.
/// ```dart
/// @override
/// void visitChildren(Visitor visitor) {
/// super.visitChildren(visitor);
/// visitor.visit(myChild);
/// visitor.visitAll(myChildList);
/// }
/// ```
///
/// Since there are many AstNodes that no one is ever going to need to visit,
/// we're using a lazy-loading policy for the `visitFoo` method. When someone
/// wants to write a [Visitation] that is specific to `Foo` (ie not covered by
/// `visitBar`), that's when the `visitFoo` function will be added.
///
/// The steps are essentially the same to change an existing class to extend
/// [AstNode].
///
/// ## Creating a new [Visitation]
///
/// 1. Write the visitation as a class that extends [Visitation]. The class may
/// be stateful, but shouldn't care about the order that nodes are visited.
/// This class may mutate the nodes, but shouldn't assume that all children
/// have finished being visited when processing the parent.
/// 2. Override the visit methods for whichever type you're interested in.
/// ```dart
/// @override
/// void visitFoo(Foo node) {
/// // Typically this method would visit the children, but that's not always
/// // what you want to do.
/// node.visitChildren(this);
///
/// // It's not necessarily true that all the children have finished being
/// // visited at this point.
///
/// // The rest of this method can edit the node in-place.
/// }
/// ```
/// 3. If you want to visit `Foo` specifically (and not its supertypes), but
/// there's no `visitFoo` method to override, add a `visitFoo` method to
/// [Visitation]. Assuming `Foo` extends `Bar`, `visitFoo` should delegate to
/// `visitBar`:
/// `void visitFoo(Foo node) => visitBar(node);`
/// 4. Then add a `visit` method to `Foo` that invokes `visitFoo`:
/// ```dart
/// @override
/// void visit(Visitation visitation) => visitation.visitFoo(this);
/// ```
/// 5. Repeat 3 and 4 for `Bar` and any other supertypes as necessary.
///
/// ## Running a [Visitation]
///
/// 1. Construct the [Visitation] and wrap it in a [Visitor]:
/// `final visitor = Visitor(MyFancyVisitation(1, 2, 3));`
/// 2. Invoke the [Visitor] on the root nodes of the AST:
/// ```dart
/// visitor.visit(someRootNode);
/// visitor.visitAll(listOfRootNodes);
/// ```
abstract class AstNode {
const AstNode();

/// Visit this node. All this method does is delegate to the visit method that
/// is specific to this type of node.
///
/// This method should be implemented for a particular type of [AstNode] only
/// when there are [Visitation]s that need to visit that type specifically.
void visit(Visitation visitation) => visitation.visitAstNode(this);

/// Visit this node's children. This is the method that actually understands
/// the structure of the node. It should invoke [Visitor.visit] etc on all the
/// node's children.
///
/// This method must be implemented if the node has children. The
/// implementation should call `super.visitChildren(visitor);`.
void visitChildren(Visitor visitor) {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,11 @@

import '../../_core/interfaces/declaration.dart';
import '../../_core/interfaces/objc_annotatable.dart';
import '../../ast_node.dart';

/// Describes a built-in Swift type (e.g Int, String, etc).
enum BuiltInDeclaration implements Declaration, ObjCAnnotatable {
swiftNSObject(id: 'c:objc(cs)NSObject', name: 'NSObject'),
swiftString(id: 's:SS', name: 'String'),
swiftInt(id: 's:Si', name: 'Int'),
swiftFloat(id: 's:Sf', name: 'Float'),
swiftDouble(id: 's:Sd', name: 'Double'),
swiftBool(id: 's:Sb', name: 'Bool'),
swiftVoid(id: 's:s4Voida', name: 'Void');

class BuiltInDeclaration extends AstNode
implements Declaration, ObjCAnnotatable {
@override
final String id;

Expand All @@ -28,12 +22,34 @@ enum BuiltInDeclaration implements Declaration, ObjCAnnotatable {
required this.id,
required this.name,
});

@override
void visit(Visitation visitation) => visitation.visitBuiltInDeclaration(this);
}

final objectType = BuiltInDeclaration.swiftNSObject.asDeclaredType;
final stringType = BuiltInDeclaration.swiftString.asDeclaredType;
final intType = BuiltInDeclaration.swiftInt.asDeclaredType;
final floatType = BuiltInDeclaration.swiftFloat.asDeclaredType;
final doubleType = BuiltInDeclaration.swiftDouble.asDeclaredType;
final boolType = BuiltInDeclaration.swiftBool.asDeclaredType;
final voidType = BuiltInDeclaration.swiftVoid.asDeclaredType;
const _objectDecl =
BuiltInDeclaration(id: 'c:objc(cs)NSObject', name: 'NSObject');
const _stringDecl = BuiltInDeclaration(id: 's:SS', name: 'String');
const _intDecl = BuiltInDeclaration(id: 's:Si', name: 'Int');
const _floatDecl = BuiltInDeclaration(id: 's:Sf', name: 'Float');
const _doubleDecl = BuiltInDeclaration(id: 's:Sd', name: 'Double');
const _boolDecl = BuiltInDeclaration(id: 's:Sb', name: 'Bool');
const _voidDecl = BuiltInDeclaration(id: 's:s4Voida', name: 'Void');

const builtInDeclarations = [
_objectDecl,
_stringDecl,
_intDecl,
_floatDecl,
_doubleDecl,
_boolDecl,
_voidDecl,
];

final objectType = _objectDecl.asDeclaredType;
final stringType = _stringDecl.asDeclaredType;
final intType = _intDecl.asDeclaredType;
final floatType = _floatDecl.asDeclaredType;
final doubleType = _doubleDecl.asDeclaredType;
final boolType = _boolDecl.asDeclaredType;
final voidType = _voidDecl.asDeclaredType;
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,16 @@ import '../../_core/interfaces/declaration.dart';
import '../../_core/interfaces/nestable_declaration.dart';
import '../../_core/interfaces/objc_annotatable.dart';
import '../../_core/shared/referred_type.dart';
import '../../ast_node.dart';
import '../built_in/built_in_declaration.dart';
import 'members/initializer_declaration.dart';
import 'members/method_declaration.dart';
import 'members/property_declaration.dart';
import 'protocol_declaration.dart';

/// Describes the declaration of a Swift class.
class ClassDeclaration implements CompoundDeclaration, ObjCAnnotatable {
class ClassDeclaration extends AstNode
implements CompoundDeclaration, ObjCAnnotatable {
@override
String id;

Expand Down Expand Up @@ -75,5 +77,23 @@ class ClassDeclaration implements CompoundDeclaration, ObjCAnnotatable {
this.initializers = const [],
}) : assert(superClass == null ||
superClass.declaration is ClassDeclaration ||
superClass.declaration == BuiltInDeclaration.swiftNSObject);
superClass.sameAs(objectType));

@override
void visit(Visitation visitation) => visitation.visitClassDeclaration(this);

@override
void visitChildren(Visitor visitor) {
super.visitChildren(visitor);
visitor.visitAll(properties);
visitor.visitAll(methods);
visitor.visitAll(conformedProtocols);
visitor.visitAll(typeParams);
visitor.visit(superClass);
visitor.visit(wrappedInstance);
visitor.visit(wrapperInitializer);
visitor.visitAll(initializers);
visitor.visit(nestingParent);
visitor.visitAll(nestedDeclarations);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ import '../../../_core/interfaces/objc_annotatable.dart';
import '../../../_core/interfaces/overridable.dart';
import '../../../_core/interfaces/parameterizable.dart';
import '../../../_core/shared/parameter.dart';
import '../../../ast_node.dart';

/// Describes an initializer for a Swift compound entity (e.g, class, structs)
class InitializerDeclaration
class InitializerDeclaration extends AstNode
implements
Declaration,
Executable,
Expand Down Expand Up @@ -62,4 +63,14 @@ class InitializerDeclaration
required this.async,
required this.isFailable,
});

@override
void visit(Visitation visitation) =>
visitation.visitInitializerDeclaration(this);

@override
void visitChildren(Visitor visitor) {
super.visitChildren(visitor);
visitor.visitAll(params);
}
}
Loading
Loading