Skip to content
Draft
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
3 changes: 3 additions & 0 deletions pkgs/swift2objc/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

- Added support for inout parameters. Preserve inout types in AST and generate
correct & pass-by-reference invocation syntax.
- Added support for Swift subscripts. Non-representable subscripts (static,
throwing, async, or with multiple/optional parameters) are automatically
transformed into `@objc` methods.

## 0.1.0

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import '../../declarations/compounds/members/initializer_declaration.dart';
import '../../declarations/compounds/members/method_declaration.dart';
import '../../declarations/compounds/members/property_declaration.dart';
import '../../declarations/compounds/members/subscript_declaration.dart';
import 'declaration.dart';
import 'nestable_declaration.dart';
import 'protocol_conformable.dart';
Expand All @@ -22,5 +23,6 @@ abstract interface class CompoundDeclaration
InnerNestableDeclaration {
abstract List<PropertyDeclaration> properties;
abstract List<MethodDeclaration> methods;
abstract List<SubscriptDeclaration> subscripts;
abstract List<InitializerDeclaration> initializers;
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import '../built_in/built_in_declaration.dart';
import 'members/initializer_declaration.dart';
import 'members/method_declaration.dart';
import 'members/property_declaration.dart';
import 'members/subscript_declaration.dart';
import 'protocol_declaration.dart';

/// Describes the declaration of a Swift class.
Expand All @@ -40,6 +41,9 @@ class ClassDeclaration extends AstNode
@override
List<MethodDeclaration> methods;

@override
List<SubscriptDeclaration> subscripts;

@override
List<DeclaredType<ProtocolDeclaration>> conformedProtocols;

Expand Down Expand Up @@ -82,6 +86,7 @@ class ClassDeclaration extends AstNode
this.lineNumber,
this.properties = const [],
this.methods = const [],
this.subscripts = const [],
this.nestingParent,
this.nestedDeclarations = const [],
this.conformedProtocols = const [],
Expand All @@ -107,6 +112,7 @@ class ClassDeclaration extends AstNode
super.visitChildren(visitor);
visitor.visitAll(properties);
visitor.visitAll(methods);
visitor.visitAll(subscripts);
visitor.visitAll(conformedProtocols);
visitor.visitAll(typeParams);
visitor.visit(superClass);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import '../../ast_node.dart';
import 'members/initializer_declaration.dart';
import 'members/method_declaration.dart';
import 'members/property_declaration.dart';
import 'members/subscript_declaration.dart';
import 'protocol_declaration.dart';

/// Describes the declaration of a Swift enum.
Expand Down Expand Up @@ -41,6 +42,9 @@ class EnumDeclaration extends AstNode implements CompoundDeclaration {
@override
List<MethodDeclaration> methods;

@override
List<SubscriptDeclaration> subscripts;

@override
List<InitializerDeclaration> initializers;

Expand All @@ -65,6 +69,7 @@ class EnumDeclaration extends AstNode implements CompoundDeclaration {
required this.cases,
required this.properties,
required this.methods,
this.subscripts = const [],
required this.initializers,
this.conformedProtocols = const [],
this.typeParams = const [],
Expand All @@ -81,6 +86,7 @@ class EnumDeclaration extends AstNode implements CompoundDeclaration {
visitor.visitAll(cases);
visitor.visitAll(properties);
visitor.visitAll(methods);
visitor.visitAll(subscripts);
visitor.visitAll(initializers);
visitor.visitAll(typeParams);
visitor.visitAll(conformedProtocols);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// 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 '../../../../config.dart';
import '../../../_core/interfaces/availability.dart';
import '../../../_core/interfaces/can_async.dart';
import '../../../_core/interfaces/can_throw.dart';
import '../../../_core/interfaces/declaration.dart';
import '../../../_core/interfaces/objc_annotatable.dart';
import '../../../_core/shared/parameter.dart';
import '../../../_core/shared/referred_type.dart';
import '../../../ast_node.dart';
import 'property_declaration.dart';

/// Describes a subscript declaration for a Swift compound entity
class SubscriptDeclaration extends AstNode
implements Declaration, ObjCAnnotatable, CanThrow, CanAsync {
@override
String id;

@override
String name = 'subscript';

@override
InputConfig? source;

@override
final int? lineNumber;

@override
List<AvailabilityInfo> availability;

@override
bool hasObjCAnnotation;

ReferredType returnType;

List<Parameter> params;

@override
bool throws;

@override
bool async;

bool mutating;

bool hasSetter;

PropertyStatements? getter;
PropertyStatements? setter;

bool isStatic;

SubscriptDeclaration({
required this.id,
required this.source,
required this.availability,
required this.returnType,
required this.params,
this.lineNumber,
this.hasSetter = false,
this.hasObjCAnnotation = false,
this.getter,
this.setter,
this.isStatic = false,
this.throws = false,
this.async = false,
this.mutating = false,
});

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

@override
void visitChildren(Visitor visitor) {
super.visitChildren(visitor);
visitor.visitAll(params);
visitor.visit(returnType);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import '../../ast_node.dart';
import 'members/initializer_declaration.dart';
import 'members/method_declaration.dart';
import 'members/property_declaration.dart';
import 'members/subscript_declaration.dart';

/// Describes the declaration of a Swift protocol.
class ProtocolDeclaration extends AstNode implements CompoundDeclaration {
Expand All @@ -35,6 +36,9 @@ class ProtocolDeclaration extends AstNode implements CompoundDeclaration {
@override
List<MethodDeclaration> methods;

@override
List<SubscriptDeclaration> subscripts;

@override
List<DeclaredType<ProtocolDeclaration>> conformedProtocols;

Expand All @@ -58,6 +62,7 @@ class ProtocolDeclaration extends AstNode implements CompoundDeclaration {
required this.availability,
required this.properties,
required this.methods,
required this.subscripts,
required this.initializers,
required this.conformedProtocols,
required this.typeParams,
Expand All @@ -73,7 +78,9 @@ class ProtocolDeclaration extends AstNode implements CompoundDeclaration {
void visitChildren(Visitor visitor) {
super.visitChildren(visitor);
visitor.visitAll(properties);
visitor.visitAll(properties);
visitor.visitAll(methods);
visitor.visitAll(subscripts);
visitor.visitAll(conformedProtocols);
visitor.visitAll(typeParams);
visitor.visitAll(initializers);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import '../../ast_node.dart';
import 'members/initializer_declaration.dart';
import 'members/method_declaration.dart';
import 'members/property_declaration.dart';
import 'members/subscript_declaration.dart';
import 'protocol_declaration.dart';

/// Describes the declaration of a Swift struct.
Expand All @@ -36,6 +37,9 @@ class StructDeclaration extends AstNode implements CompoundDeclaration {
@override
List<MethodDeclaration> methods;

@override
List<SubscriptDeclaration> subscripts;

@override
List<DeclaredType<ProtocolDeclaration>> conformedProtocols;

Expand All @@ -59,6 +63,7 @@ class StructDeclaration extends AstNode implements CompoundDeclaration {
required this.availability,
this.properties = const [],
this.methods = const [],
this.subscripts = const [],
this.initializers = const [],
this.conformedProtocols = const [],
this.typeParams = const [],
Expand All @@ -73,7 +78,9 @@ class StructDeclaration extends AstNode implements CompoundDeclaration {
void visitChildren(Visitor visitor) {
super.visitChildren(visitor);
visitor.visitAll(properties);
visitor.visitAll(properties);
visitor.visitAll(methods);
visitor.visitAll(subscripts);
visitor.visitAll(conformedProtocols);
visitor.visitAll(typeParams);
visitor.visitAll(initializers);
Expand Down
3 changes: 3 additions & 0 deletions pkgs/swift2objc/lib/src/ast/visitor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import 'declarations/compounds/enum_declaration.dart';
import 'declarations/compounds/members/initializer_declaration.dart';
import 'declarations/compounds/members/method_declaration.dart';
import 'declarations/compounds/members/property_declaration.dart';
import 'declarations/compounds/members/subscript_declaration.dart';
import 'declarations/compounds/protocol_declaration.dart';
import 'declarations/compounds/struct_declaration.dart';
import 'declarations/globals/globals.dart';
Expand Down Expand Up @@ -96,6 +97,8 @@ abstract class Visitation {
visitVariableDeclaration(node);
void visitGlobalVariableDeclaration(GlobalVariableDeclaration node) =>
visitVariableDeclaration(node);
void visitSubscriptDeclaration(SubscriptDeclaration node) =>
visitDeclaration(node);
void visitCompoundDeclaration(CompoundDeclaration node) =>
visitDeclaration(node);
void visitClassDeclaration(ClassDeclaration node) =>
Expand Down
44 changes: 44 additions & 0 deletions pkgs/swift2objc/lib/src/generator/generators/class_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import '../../ast/declarations/compounds/class_declaration.dart';
import '../../ast/declarations/compounds/members/initializer_declaration.dart';
import '../../ast/declarations/compounds/members/method_declaration.dart';
import '../../ast/declarations/compounds/members/property_declaration.dart';
import '../../ast/declarations/compounds/members/subscript_declaration.dart';
import '../_core/utils.dart';
import '../generator.dart';

Expand All @@ -19,6 +20,7 @@ List<String> generateClass(ClassDeclaration declaration) {
...[
_generateClassWrappedInstance(declaration),
..._generateClassProperties(declaration),
..._generateClassSubscripts(declaration),
..._generateInitializers(declaration),
..._generateClassMethods(declaration),
..._generateNestedDeclarations(declaration),
Expand Down Expand Up @@ -144,6 +146,48 @@ List<String> _generateClassProperties(ClassDeclaration declaration) => [
..._generateClassProperty(property),
];

List<String> _generateClassSubscripts(ClassDeclaration declaration) => [
for (final subscript in declaration.subscripts)
..._generateClassSubscript(subscript),
];

List<String> _generateClassSubscript(SubscriptDeclaration subscript) {
final header = StringBuffer();

if (subscript.hasObjCAnnotation) {
header.write('@objc ');
}

if (subscript.isStatic) {
header.write('static ');
}

final params = generateParameters(subscript.params);
header.write(
'public subscript($params) -> ${subscript.returnType.swiftType} {',
);

final getterLines = [
'get ${generateAnnotations(subscript)}{',
...(subscript.getter?.statements.indent() ?? <String>[]),
'}',
];

final setterLines = [
'set {',
...(subscript.setter?.statements.indent() ?? <String>[]),
'}',
];

return [
...generateAvailability(subscript),
header.toString(),
...getterLines.indent(),
if (subscript.hasSetter) ...setterLines.indent(),
'}\n',
];
}

List<String> _generateClassProperty(PropertyDeclaration property) {
final header = StringBuffer();

Expand Down
2 changes: 1 addition & 1 deletion pkgs/swift2objc/lib/src/parser/_core/token_list.dart
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class TokenList extends Iterable<Json> {

@visibleForTesting
static Iterable<Json> splitToken(Json token) sync* {
const splittables = ['(', ')', '?', ',', '->'];
const splittables = ['(', ')', '?', ',', '->', '[', ']', ':'];
Json textToken(String text) =>
Json({'kind': 'text', 'spelling': text}, token.pathSegments);

Expand Down
19 changes: 17 additions & 2 deletions pkgs/swift2objc/lib/src/parser/_core/utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,12 @@ bool parseSymbolHasObjcAnnotation(Json symbolJson) =>
(json) => matchFragment(json, 'attribute', '@objc'),
);

bool parseIsOverriding(Json symbolJson) => symbolJson['declarationFragments']
.any((json) => matchFragment(json, 'keyword', 'override'));
bool parseIsOverriding(Json symbolJson) =>
findKeywordInFragments(symbolJson['declarationFragments'], 'override');

bool findKeywordInFragments(Json json, String keyword) {
return json.any((frag) => matchFragment(frag, 'keyword', keyword));
}

List<AvailabilityInfo> parseAvailability(Json symbolJson) {
final availability = symbolJson['availability'];
Expand Down Expand Up @@ -165,3 +169,14 @@ ReferredType parseTypeAfterSeparator(
assert(suffix.isEmpty, '$suffix');
return type;
}

ReferredType parseReturnType(
Context context,
Json symbolJson,
ParsedSymbolgraph symbolgraph,
) {
final returnJson = TokenList(symbolJson['functionSignature']['returns']);
final (returnType, unparsed) = parseType(context, symbolgraph, returnJson);
assert(unparsed.isEmpty, '$returnJson\n\n$returnType\n\n$unparsed\n');
return returnType;
}
Loading
Loading