Skip to content

Commit 3210a79

Browse files
authored
[swift2objc] Support the async annotation (#1779)
1 parent 4b03d95 commit 3210a79

19 files changed

+249
-21
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
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+
/// An interface to describe a Swift entity's ability to be annotated
6+
/// with `async`.
7+
abstract interface class CanAsync {
8+
abstract final bool async;
9+
}

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// BSD-style license that can be found in the LICENSE file.
44

55
import '../shared/referred_type.dart';
6+
import 'can_async.dart';
67
import 'can_throw.dart';
78
import 'declaration.dart';
89
import 'executable.dart';
@@ -16,6 +17,7 @@ abstract interface class FunctionDeclaration
1617
Parameterizable,
1718
Executable,
1819
TypeParameterizable,
19-
CanThrow {
20+
CanThrow,
21+
CanAsync {
2022
abstract final ReferredType returnType;
2123
}

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

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,17 @@
33
// BSD-style license that can be found in the LICENSE file.
44

55
import '../shared/referred_type.dart';
6+
import 'can_async.dart';
67
import 'can_throw.dart';
78
import 'declaration.dart';
89

910
/// Describes a variable-like entity.
1011
///
11-
/// This declaration [CanThrow] because Swift variables can have explicit
12-
/// getters, which can be marked with `throws`. Such variables may not have a
13-
/// setter.
14-
abstract interface class VariableDeclaration implements Declaration, CanThrow {
12+
/// This declaration implements [CanThrow] and [CanAsync] because Swift
13+
/// variables can have explicit getters, which can be marked with `throws` and
14+
/// `async`. Such variables may not have a setter.
15+
abstract interface class VariableDeclaration
16+
implements Declaration, CanThrow, CanAsync {
1517
abstract final bool isConstant;
1618
abstract final ReferredType type;
1719
}

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
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 '../../../_core/interfaces/can_async.dart';
56
import '../../../_core/interfaces/can_throw.dart';
67
import '../../../_core/interfaces/declaration.dart';
78
import '../../../_core/interfaces/executable.dart';
@@ -18,7 +19,8 @@ class InitializerDeclaration
1819
Parameterizable,
1920
ObjCAnnotatable,
2021
Overridable,
21-
CanThrow {
22+
CanThrow,
23+
CanAsync {
2224
@override
2325
String id;
2426

@@ -34,6 +36,9 @@ class InitializerDeclaration
3436
@override
3537
bool throws;
3638

39+
@override
40+
bool async;
41+
3742
bool isFailable;
3843

3944
@override
@@ -54,6 +59,7 @@ class InitializerDeclaration
5459
required this.hasObjCAnnotation,
5560
required this.isOverriding,
5661
required this.throws,
62+
required this.async,
5763
required this.isFailable,
5864
});
5965
}

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ class MethodDeclaration
3333
@override
3434
bool throws;
3535

36+
@override
37+
bool async;
38+
3639
@override
3740
List<String> statements;
3841

@@ -57,5 +60,6 @@ class MethodDeclaration
5760
this.isStatic = false,
5861
this.isOverriding = false,
5962
this.throws = false,
63+
this.async = false,
6064
}) : assert(!isStatic || !isOverriding);
6165
}

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ class PropertyDeclaration implements VariableDeclaration, ObjCAnnotatable {
2828
@override
2929
bool throws;
3030

31+
@override
32+
bool async;
33+
3134
bool hasSetter;
3235

3336
PropertyStatements? getter;
@@ -46,6 +49,7 @@ class PropertyDeclaration implements VariableDeclaration, ObjCAnnotatable {
4649
this.setter,
4750
this.isStatic = false,
4851
this.throws = false,
52+
this.async = false,
4953
}) : assert(!(isConstant && hasSetter)),
5054
assert(!(hasSetter && throws));
5155
}

pkgs/swift2objc/lib/src/ast/declarations/globals/globals.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ class GlobalFunctionDeclaration implements FunctionDeclaration {
3636
@override
3737
bool throws;
3838

39+
@override
40+
bool async;
41+
3942
@override
4043
ReferredType returnType;
4144

@@ -50,6 +53,7 @@ class GlobalFunctionDeclaration implements FunctionDeclaration {
5053
this.typeParams = const [],
5154
this.statements = const [],
5255
this.throws = false,
56+
this.async = false,
5357
});
5458
}
5559

@@ -70,11 +74,15 @@ class GlobalVariableDeclaration implements VariableDeclaration {
7074
@override
7175
bool throws;
7276

77+
@override
78+
bool async;
79+
7380
GlobalVariableDeclaration({
7481
required this.id,
7582
required this.name,
7683
required this.type,
7784
required this.isConstant,
7885
required this.throws,
86+
required this.async,
7987
}) : assert(!(throws && !isConstant));
8088
}

pkgs/swift2objc/lib/src/generator/_core/utils.dart

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
import 'dart:io';
22
import 'package:path/path.dart' as path;
3+
import '../../ast/_core/interfaces/can_async.dart';
4+
import '../../ast/_core/interfaces/can_throw.dart';
5+
import '../../ast/_core/interfaces/declaration.dart';
36
import '../../ast/_core/shared/parameter.dart';
47

58
String generateParameters(List<Parameter> params) {
@@ -34,3 +37,14 @@ void outputNextToFile({
3437

3538
File(outputPath).writeAsStringSync(content);
3639
}
40+
41+
String generateAnnotations(Declaration decl) {
42+
final annotations = StringBuffer();
43+
if (decl is CanAsync && (decl as CanAsync).async) {
44+
annotations.write('async ');
45+
}
46+
if (decl is CanThrow && (decl as CanThrow).throws) {
47+
annotations.write('throws ');
48+
}
49+
return annotations.toString();
50+
}

pkgs/swift2objc/lib/src/generator/generators/class_generator.dart

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -87,12 +87,8 @@ List<String> _generateInitializer(InitializerDeclaration initializer) {
8787

8888
header.write('(${generateParameters(initializer.params)})');
8989

90-
if (initializer.throws) {
91-
header.write(' throws');
92-
}
93-
9490
return [
95-
'$header {',
91+
'$header ${generateAnnotations(initializer)}{',
9692
...initializer.statements.indent(),
9793
'}\n',
9894
];
@@ -117,19 +113,17 @@ List<String> _generateClassMethod(MethodDeclaration method) {
117113
}
118114

119115
header.write(
120-
'public func ${method.name}(${generateParameters(method.params)})',
116+
'public func ${method.name}(${generateParameters(method.params)}) ',
121117
);
122118

123-
if (method.throws) {
124-
header.write(' throws');
125-
}
119+
header.write(generateAnnotations(method));
126120

127121
if (!method.returnType.sameAs(voidType)) {
128-
header.write(' -> ${method.returnType.swiftType}');
122+
header.write('-> ${method.returnType.swiftType} ');
129123
}
130124

131125
return [
132-
'$header {',
126+
'$header{',
133127
...method.statements.indent(),
134128
'}\n',
135129
];
@@ -154,7 +148,7 @@ List<String> _generateClassProperty(PropertyDeclaration property) {
154148
header.write('public var ${property.name}: ${property.type.swiftType} {');
155149

156150
final getterLines = [
157-
'get {',
151+
'get ${generateAnnotations(property)}{',
158152
...(property.getter?.statements.indent() ?? <String>[]),
159153
'}'
160154
];

pkgs/swift2objc/lib/src/parser/parsers/declaration_parsers/parse_function_declaration.dart

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ GlobalFunctionDeclaration parseGlobalFunctionDeclaration(
2424
returnType: _parseFunctionReturnType(globalFunctionSymbolJson, symbolgraph),
2525
params: info.params,
2626
throws: info.throws,
27+
async: info.async,
2728
);
2829
}
2930

@@ -42,12 +43,14 @@ MethodDeclaration parseMethodDeclaration(
4243
hasObjCAnnotation: parseSymbolHasObjcAnnotation(methodSymbolJson),
4344
isStatic: isStatic,
4445
throws: info.throws,
46+
async: info.async,
4547
);
4648
}
4749

4850
typedef ParsedFunctionInfo = ({
4951
List<Parameter> params,
5052
bool throws,
53+
bool async,
5154
});
5255

5356
ParsedFunctionInfo parseFunctionInfo(
@@ -120,17 +123,22 @@ ParsedFunctionInfo parseFunctionInfo(
120123
}
121124
}
122125

123-
// Parse annotations until we run out.
126+
// Parse annotations until we run out. The annotations are keywords separated
127+
// by whitespace tokens.
124128
final annotations = <String>{};
125129
while (true) {
126130
final keyword = maybeConsume('keyword');
127-
if (keyword == null) break;
128-
annotations.add(keyword);
131+
if (keyword == null) {
132+
if (maybeConsume('text') != '') break;
133+
} else {
134+
annotations.add(keyword);
135+
}
129136
}
130137

131138
return (
132139
params: parameters,
133140
throws: annotations.contains('throws'),
141+
async: annotations.contains('async'),
134142
);
135143
}
136144

pkgs/swift2objc/lib/src/parser/parsers/declaration_parsers/parse_initializer_declaration.dart

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,22 @@ InitializerDeclaration parseInitializerDeclaration(
2020
}
2121

2222
final info = parseFunctionInfo(declarationFragments, symbolgraph);
23+
24+
if (info.async) {
25+
// TODO(https://github.com/dart-lang/native/issues/1778): Support async
26+
// initializerse.
27+
throw Exception("Async initializers aren't supported yet, at "
28+
'${initializerSymbolJson.path}');
29+
}
30+
2331
return InitializerDeclaration(
2432
id: id,
2533
params: info.params,
2634
hasObjCAnnotation: parseSymbolHasObjcAnnotation(initializerSymbolJson),
2735
isOverriding: parseIsOverriding(initializerSymbolJson),
2836
isFailable: parseIsFailableInit(id, declarationFragments),
2937
throws: info.throws,
38+
async: info.async,
3039
);
3140
}
3241

pkgs/swift2objc/lib/src/parser/parsers/declaration_parsers/parse_variable_declaration.dart

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ PropertyDeclaration parsePropertyDeclaration(
2525
hasSetter: isConstant ? false : _parsePropertyHasSetter(propertySymbolJson),
2626
isStatic: isStatic,
2727
throws: _parseVariableThrows(propertySymbolJson),
28+
async: _parseVariableAsync(propertySymbolJson),
2829
);
2930
}
3031

@@ -41,6 +42,7 @@ GlobalVariableDeclaration parseGlobalVariableDeclaration(
4142
type: _parseVariableType(variableSymbolJson, symbolgraph),
4243
isConstant: isConstant || !hasSetter,
4344
throws: _parseVariableThrows(variableSymbolJson),
45+
async: _parseVariableAsync(variableSymbolJson),
4446
);
4547
}
4648

@@ -78,6 +80,17 @@ bool _parseVariableThrows(Json json) {
7880
return throws;
7981
}
8082

83+
bool _parseVariableAsync(Json json) {
84+
final async = json['declarationFragments']
85+
.any((frag) => matchFragment(frag, 'keyword', 'async'));
86+
if (async) {
87+
// TODO(https://github.com/dart-lang/native/issues/1778): Support async
88+
// getters.
89+
throw Exception("Async getters aren't supported yet, at ${json.path}");
90+
}
91+
return async;
92+
}
93+
8194
bool _parsePropertyHasSetter(Json propertySymbolJson) {
8295
final fragmentsJson = propertySymbolJson['declarationFragments'];
8396

pkgs/swift2objc/lib/src/transformer/transformers/transform_compound.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ InitializerDeclaration _buildWrapperInitializer(
101101
isOverriding: false,
102102
isFailable: false,
103103
throws: false,
104+
async: false,
104105
statements: ['self.${wrappedClassInstance.name} = wrappedInstance'],
105106
hasObjCAnnotation: wrappedClassInstance.hasObjCAnnotation,
106107
);

pkgs/swift2objc/lib/src/transformer/transformers/transform_function.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ MethodDeclaration _transformFunction(
100100
? originalFunction.isStatic
101101
: true,
102102
throws: originalFunction.throws,
103+
async: originalFunction.async,
103104
);
104105

105106
transformedMethod.statements = _generateStatements(
@@ -150,6 +151,9 @@ List<String> _generateStatements(
150151
final arguments = generateInvocationParams(
151152
localNamer, originalFunction.params, transformedMethod.params);
152153
var originalMethodCall = originalCallGenerator(arguments);
154+
if (transformedMethod.async) {
155+
originalMethodCall = 'await $originalMethodCall';
156+
}
153157
if (transformedMethod.throws) {
154158
originalMethodCall = 'try $originalMethodCall';
155159
}

pkgs/swift2objc/lib/src/transformer/transformers/transform_initializer.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ InitializerDeclaration transformInitializer(
3636
hasObjCAnnotation: true,
3737
isFailable: originalInitializer.isFailable,
3838
throws: originalInitializer.throws,
39+
async: originalInitializer.async,
3940
// Because the wrapper class extends NSObject that has an initializer with
4041
// no parameters. If we make a similar parameterless initializer we need
4142
// to add `override` keyword.
@@ -60,6 +61,9 @@ List<String> _generateInitializerStatements(
6061
localNamer, originalInitializer.params, transformedInitializer.params);
6162
var instanceConstruction =
6263
'${wrappedClassInstance.type.swiftType}($arguments)';
64+
if (transformedInitializer.async) {
65+
instanceConstruction = 'await $instanceConstruction';
66+
}
6367
if (transformedInitializer.throws) {
6468
instanceConstruction = 'try $instanceConstruction';
6569
}

pkgs/swift2objc/lib/src/transformer/transformers/transform_variable.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ PropertyDeclaration _transformVariable(
8282
: true,
8383
isConstant: originalVariable.isConstant,
8484
throws: originalVariable.throws,
85+
async: originalVariable.async,
8586
);
8687

8788
final getterStatements = _generateGetterStatements(
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import Foundation
2+
3+
public class MyClass {
4+
public func voidMethod() async {}
5+
public func intMethod(y: Int) async -> MyClass { return MyClass() }
6+
public func asyncThrowsMethod(y: Int) async throws -> MyClass {
7+
return MyClass()
8+
}
9+
}
10+
11+
public func voidFunc(x: Int, y: Int) async {}
12+
public func intFunc() async -> MyClass { return MyClass() }

0 commit comments

Comments
 (0)