Skip to content

Commit 0df33b3

Browse files
authored
[swift2objc] Support wrapper classes for primitives (#1984)
1 parent dce9d73 commit 0df33b3

File tree

9 files changed

+379
-56
lines changed

9 files changed

+379
-56
lines changed
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
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 'package:collection/collection.dart';
6+
7+
import '../../ast/_core/interfaces/declaration.dart';
8+
import '../../ast/_core/shared/referred_type.dart';
9+
import '../../ast/declarations/built_in/built_in_declaration.dart';
10+
import '../../ast/declarations/compounds/class_declaration.dart';
11+
import '../../ast/declarations/compounds/members/property_declaration.dart';
12+
import '../../parser/_core/utils.dart';
13+
import '../../transformer/_core/utils.dart';
14+
import '../transform.dart';
15+
16+
final _primitiveWrappers = List<(ReferredType, ReferredType)>.unmodifiable([
17+
(intType, _createWrapperClass(intType)),
18+
(floatType, _createWrapperClass(floatType)),
19+
(doubleType, _createWrapperClass(doubleType)),
20+
(boolType, _createWrapperClass(boolType)),
21+
]);
22+
23+
ReferredType _createWrapperClass(DeclaredType primitiveType) {
24+
final property = PropertyDeclaration(
25+
id: primitiveType.id.addIdSuffix('wrappedInstance'),
26+
name: 'wrappedInstance',
27+
type: primitiveType,
28+
);
29+
return ClassDeclaration(
30+
id: primitiveType.id.addIdSuffix('wrapper'),
31+
name: '${primitiveType.name}Wrapper',
32+
hasObjCAnnotation: true,
33+
superClass: objectType,
34+
isWrapper: true,
35+
wrappedInstance: property,
36+
wrapperInitializer: buildWrapperInitializer(property))
37+
.asDeclaredType;
38+
}
39+
40+
// Support Optional primitives as return Type
41+
// TODO(https://github.com/dart-lang/native/issues/1743)
42+
43+
(ReferredType, bool) maybeGetPrimitiveWrapper(
44+
ReferredType type,
45+
bool shouldWrapPrimitives,
46+
TransformationMap transformationMap,
47+
) {
48+
if (type is! DeclaredType || !shouldWrapPrimitives) {
49+
return (type, false);
50+
}
51+
52+
final wrapper = _getPrimitiveWrapper(type);
53+
if (wrapper == null) {
54+
return (type, false);
55+
}
56+
57+
transformationMap[type.declaration] = (wrapper as DeclaredType).declaration;
58+
return (wrapper, true);
59+
}
60+
61+
ReferredType? _getPrimitiveWrapper(DeclaredType other) {
62+
return _primitiveWrappers
63+
.firstWhereOrNull((pair) => pair.$1.sameAs(other))
64+
?.$2;
65+
}

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

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

55
import '../../ast/_core/interfaces/declaration.dart';
6+
import '../../ast/_core/shared/parameter.dart';
67
import '../../ast/_core/shared/referred_type.dart';
78
import '../../ast/declarations/compounds/class_declaration.dart';
9+
import '../../ast/declarations/compounds/members/initializer_declaration.dart';
10+
import '../../ast/declarations/compounds/members/property_declaration.dart';
11+
import '../../transformer/_core/primitive_wrappers.dart';
812
import '../transform.dart';
913
import 'unique_namer.dart';
1014

1115
// TODO(https://github.com/dart-lang/native/issues/1358): These functions should
1216
// probably be methods on ReferredType, but the transformDeclaration call makes
1317
// that weird. Refactor this as part of the transformer refactor.
1418

15-
(String value, ReferredType type) maybeWrapValue(
16-
ReferredType type,
17-
String value,
18-
UniqueNamer globalNamer,
19-
TransformationMap transformationMap,
20-
) {
19+
(String value, ReferredType type) maybeWrapValue(ReferredType type,
20+
String value, UniqueNamer globalNamer, TransformationMap transformationMap,
21+
{bool shouldWrapPrimitives = false}) {
22+
final (wrappedPrimitiveType, returnsWrappedPrimitive) =
23+
maybeGetPrimitiveWrapper(type, shouldWrapPrimitives, transformationMap);
24+
if (returnsWrappedPrimitive) {
25+
return (
26+
'${(wrappedPrimitiveType as DeclaredType).name}($value)',
27+
wrappedPrimitiveType
28+
);
29+
}
30+
2131
if (type.isObjCRepresentable) {
2232
return (value, type);
2333
}
@@ -77,3 +87,24 @@ import 'unique_namer.dart';
7787
throw UnimplementedError('Unknown type: $type');
7888
}
7989
}
90+
91+
InitializerDeclaration buildWrapperInitializer(
92+
PropertyDeclaration wrappedClassInstance,
93+
) {
94+
return InitializerDeclaration(
95+
id: '',
96+
params: [
97+
Parameter(
98+
name: '_',
99+
internalName: 'wrappedInstance',
100+
type: wrappedClassInstance.type,
101+
)
102+
],
103+
isOverriding: false,
104+
isFailable: false,
105+
throws: false,
106+
async: false,
107+
statements: ['self.${wrappedClassInstance.name} = wrappedInstance'],
108+
hasObjCAnnotation: wrappedClassInstance.hasObjCAnnotation,
109+
);
110+
}

pkgs/swift2objc/lib/src/transformer/transform.dart

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import '../ast/_core/interfaces/compound_declaration.dart';
66
import '../ast/_core/interfaces/declaration.dart';
77
import '../ast/_core/interfaces/nestable_declaration.dart';
8+
import '../ast/declarations/built_in/built_in_declaration.dart';
89
import '../ast/declarations/compounds/class_declaration.dart';
910
import '../ast/declarations/compounds/struct_declaration.dart';
1011
import '../ast/declarations/globals/globals.dart';
@@ -51,7 +52,8 @@ List<Declaration> transform(List<Declaration> declarations,
5152
transformGlobals(globals, globalNamer, transformationMap),
5253
];
5354

54-
return transformedDeclarations
55+
return (transformedDeclarations +
56+
_getPrimitiveWrapperClasses(transformationMap))
5557
..sort((Declaration a, Declaration b) => a.id.compareTo(b.id));
5658
}
5759

@@ -80,3 +82,11 @@ Declaration transformDeclaration(
8082
_ => throw UnimplementedError(),
8183
};
8284
}
85+
86+
List<Declaration> _getPrimitiveWrapperClasses(
87+
TransformationMap transformationMap) {
88+
return transformationMap.entries
89+
.where((entry) => entry.key is BuiltInDeclaration)
90+
.map((entry) => entry.value)
91+
.toList();
92+
}

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

Lines changed: 2 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@
55
import '../../ast/_core/interfaces/compound_declaration.dart';
66
import '../../ast/_core/interfaces/declaration.dart';
77
import '../../ast/_core/interfaces/nestable_declaration.dart';
8-
import '../../ast/_core/shared/parameter.dart';
98
import '../../ast/declarations/built_in/built_in_declaration.dart';
109
import '../../ast/declarations/compounds/class_declaration.dart';
1110
import '../../ast/declarations/compounds/members/initializer_declaration.dart';
1211
import '../../ast/declarations/compounds/members/method_declaration.dart';
1312
import '../../ast/declarations/compounds/members/property_declaration.dart';
1413
import '../../parser/_core/utils.dart';
1514
import '../_core/unique_namer.dart';
15+
import '../_core/utils.dart';
1616
import '../transform.dart';
1717
import 'transform_function.dart';
1818
import 'transform_initializer.dart';
@@ -38,7 +38,7 @@ ClassDeclaration transformCompound(
3838
superClass: objectType,
3939
isWrapper: true,
4040
wrappedInstance: wrappedCompoundInstance,
41-
wrapperInitializer: _buildWrapperInitializer(wrappedCompoundInstance),
41+
wrapperInitializer: buildWrapperInitializer(wrappedCompoundInstance),
4242
);
4343

4444
transformationMap[originalCompound] = transformedCompound;
@@ -98,24 +98,3 @@ ClassDeclaration transformCompound(
9898

9999
return transformedCompound;
100100
}
101-
102-
InitializerDeclaration _buildWrapperInitializer(
103-
PropertyDeclaration wrappedClassInstance,
104-
) {
105-
return InitializerDeclaration(
106-
id: '',
107-
params: [
108-
Parameter(
109-
name: '_',
110-
internalName: 'wrappedInstance',
111-
type: wrappedClassInstance.type,
112-
)
113-
],
114-
isOverriding: false,
115-
isFailable: false,
116-
throws: false,
117-
async: false,
118-
statements: ['self.${wrappedClassInstance.name} = wrappedInstance'],
119-
hasObjCAnnotation: wrappedClassInstance.hasObjCAnnotation,
120-
);
121-
}

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

Lines changed: 15 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -84,16 +84,17 @@ MethodDeclaration _transformFunction(
8484
)
8585
.toList();
8686

87-
final transformedReturnType = transformReferredType(
88-
originalFunction.returnType,
89-
globalNamer,
90-
transformationMap,
91-
);
87+
final localNamer = UniqueNamer();
88+
final resultName = localNamer.makeUnique('result');
89+
90+
final (wrapperResult, type) = maybeWrapValue(
91+
originalFunction.returnType, resultName, globalNamer, transformationMap,
92+
shouldWrapPrimitives: originalFunction.throws);
9293

9394
final transformedMethod = MethodDeclaration(
9495
id: originalFunction.id,
9596
name: wrapperMethodName,
96-
returnType: transformedReturnType,
97+
returnType: type,
9798
params: transformedParams,
9899
hasObjCAnnotation: true,
99100
isStatic: originalFunction is MethodDeclaration
@@ -107,6 +108,9 @@ MethodDeclaration _transformFunction(
107108
originalFunction,
108109
transformedMethod,
109110
globalNamer,
111+
localNamer,
112+
resultName,
113+
wrapperResult,
110114
transformationMap,
111115
originalCallGenerator: originalCallStatementGenerator,
112116
);
@@ -144,10 +148,12 @@ List<String> _generateStatements(
144148
FunctionDeclaration originalFunction,
145149
MethodDeclaration transformedMethod,
146150
UniqueNamer globalNamer,
151+
UniqueNamer localNamer,
152+
String resultName,
153+
String wrappedResult,
147154
TransformationMap transformationMap, {
148155
required String Function(String arguments) originalCallGenerator,
149156
}) {
150-
final localNamer = UniqueNamer();
151157
final arguments = generateInvocationParams(
152158
localNamer, originalFunction.params, transformedMethod.params);
153159
var originalMethodCall = originalCallGenerator(arguments);
@@ -166,22 +172,8 @@ List<String> _generateStatements(
166172
throw UnimplementedError('Generic types are not implemented yet');
167173
}
168174

169-
final resultName = localNamer.makeUnique('result');
170-
final methodCallStmt = 'let $resultName = $originalMethodCall';
171-
172-
final (wrappedResult, wrapperType) = maybeWrapValue(
173-
originalFunction.returnType,
174-
resultName,
175-
globalNamer,
176-
transformationMap,
177-
);
178-
179-
assert(wrapperType.sameAs(transformedMethod.returnType));
180-
181-
final returnStmt = 'return $wrappedResult';
182-
183175
return [
184-
methodCallStmt,
185-
returnStmt,
176+
'let $resultName = $originalMethodCall',
177+
'return $wrappedResult',
186178
];
187179
}

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

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,24 +73,32 @@ Declaration _transformVariable(
7373
? originalVariable.hasSetter
7474
: !originalVariable.isConstant;
7575

76+
// properties that throw or are async need to be wrapped in a method
7677
if (originalVariable.throws || originalVariable.async) {
7778
final prefix = [
7879
if (originalVariable.throws) 'try',
7980
if (originalVariable.async) 'await'
8081
].join(' ');
8182

83+
final localNamer = UniqueNamer();
84+
final resultName = localNamer.makeUnique('result');
85+
86+
final (wrapperResult, type) = maybeWrapValue(
87+
originalVariable.type, resultName, globalNamer, transformationMap,
88+
shouldWrapPrimitives: originalVariable.throws);
89+
8290
return MethodDeclaration(
8391
id: originalVariable.id,
8492
name: wrapperPropertyName,
85-
returnType: transformedType,
93+
returnType: type,
8694
params: [],
8795
hasObjCAnnotation: true,
8896
isStatic: originalVariable is PropertyDeclaration
8997
? originalVariable.isStatic
9098
: true,
9199
statements: [
92-
'let result = $prefix $variableReferenceExpression',
93-
'return $transformedType(result)',
100+
'let $resultName = $prefix $variableReferenceExpression',
101+
'return $wrapperResult',
94102
],
95103
throws: originalVariable.throws,
96104
async: originalVariable.async,

pkgs/swift2objc/pubspec.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ topics:
1515
- codegen
1616

1717
dependencies:
18+
collection: ^1.19.1
1819
logging: ^1.3.0
1920
meta: ^1.16.0
2021
path: ^1.9.0

0 commit comments

Comments
 (0)