Skip to content

Commit

Permalink
[swift2objc] Support wrapper classes for primitives (#1984)
Browse files Browse the repository at this point in the history
  • Loading branch information
AmrAhmed119 authored Mar 6, 2025
1 parent dce9d73 commit 0df33b3
Show file tree
Hide file tree
Showing 9 changed files with 379 additions and 56 deletions.
65 changes: 65 additions & 0 deletions pkgs/swift2objc/lib/src/transformer/_core/primitive_wrappers.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// 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 'package:collection/collection.dart';

import '../../ast/_core/interfaces/declaration.dart';
import '../../ast/_core/shared/referred_type.dart';
import '../../ast/declarations/built_in/built_in_declaration.dart';
import '../../ast/declarations/compounds/class_declaration.dart';
import '../../ast/declarations/compounds/members/property_declaration.dart';
import '../../parser/_core/utils.dart';
import '../../transformer/_core/utils.dart';
import '../transform.dart';

final _primitiveWrappers = List<(ReferredType, ReferredType)>.unmodifiable([
(intType, _createWrapperClass(intType)),
(floatType, _createWrapperClass(floatType)),
(doubleType, _createWrapperClass(doubleType)),
(boolType, _createWrapperClass(boolType)),
]);

ReferredType _createWrapperClass(DeclaredType primitiveType) {
final property = PropertyDeclaration(
id: primitiveType.id.addIdSuffix('wrappedInstance'),
name: 'wrappedInstance',
type: primitiveType,
);
return ClassDeclaration(
id: primitiveType.id.addIdSuffix('wrapper'),
name: '${primitiveType.name}Wrapper',
hasObjCAnnotation: true,
superClass: objectType,
isWrapper: true,
wrappedInstance: property,
wrapperInitializer: buildWrapperInitializer(property))
.asDeclaredType;
}

// Support Optional primitives as return Type
// TODO(https://github.com/dart-lang/native/issues/1743)

(ReferredType, bool) maybeGetPrimitiveWrapper(
ReferredType type,
bool shouldWrapPrimitives,
TransformationMap transformationMap,
) {
if (type is! DeclaredType || !shouldWrapPrimitives) {
return (type, false);
}

final wrapper = _getPrimitiveWrapper(type);
if (wrapper == null) {
return (type, false);
}

transformationMap[type.declaration] = (wrapper as DeclaredType).declaration;
return (wrapper, true);
}

ReferredType? _getPrimitiveWrapper(DeclaredType other) {
return _primitiveWrappers
.firstWhereOrNull((pair) => pair.$1.sameAs(other))
?.$2;
}
43 changes: 37 additions & 6 deletions pkgs/swift2objc/lib/src/transformer/_core/utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,31 @@
// BSD-style license that can be found in the LICENSE file.

import '../../ast/_core/interfaces/declaration.dart';
import '../../ast/_core/shared/parameter.dart';
import '../../ast/_core/shared/referred_type.dart';
import '../../ast/declarations/compounds/class_declaration.dart';
import '../../ast/declarations/compounds/members/initializer_declaration.dart';
import '../../ast/declarations/compounds/members/property_declaration.dart';
import '../../transformer/_core/primitive_wrappers.dart';
import '../transform.dart';
import 'unique_namer.dart';

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

(String value, ReferredType type) maybeWrapValue(
ReferredType type,
String value,
UniqueNamer globalNamer,
TransformationMap transformationMap,
) {
(String value, ReferredType type) maybeWrapValue(ReferredType type,
String value, UniqueNamer globalNamer, TransformationMap transformationMap,
{bool shouldWrapPrimitives = false}) {
final (wrappedPrimitiveType, returnsWrappedPrimitive) =
maybeGetPrimitiveWrapper(type, shouldWrapPrimitives, transformationMap);
if (returnsWrappedPrimitive) {
return (
'${(wrappedPrimitiveType as DeclaredType).name}($value)',
wrappedPrimitiveType
);
}

if (type.isObjCRepresentable) {
return (value, type);
}
Expand Down Expand Up @@ -77,3 +87,24 @@ import 'unique_namer.dart';
throw UnimplementedError('Unknown type: $type');
}
}

InitializerDeclaration buildWrapperInitializer(
PropertyDeclaration wrappedClassInstance,
) {
return InitializerDeclaration(
id: '',
params: [
Parameter(
name: '_',
internalName: 'wrappedInstance',
type: wrappedClassInstance.type,
)
],
isOverriding: false,
isFailable: false,
throws: false,
async: false,
statements: ['self.${wrappedClassInstance.name} = wrappedInstance'],
hasObjCAnnotation: wrappedClassInstance.hasObjCAnnotation,
);
}
12 changes: 11 additions & 1 deletion pkgs/swift2objc/lib/src/transformer/transform.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import '../ast/_core/interfaces/compound_declaration.dart';
import '../ast/_core/interfaces/declaration.dart';
import '../ast/_core/interfaces/nestable_declaration.dart';
import '../ast/declarations/built_in/built_in_declaration.dart';
import '../ast/declarations/compounds/class_declaration.dart';
import '../ast/declarations/compounds/struct_declaration.dart';
import '../ast/declarations/globals/globals.dart';
Expand Down Expand Up @@ -51,7 +52,8 @@ List<Declaration> transform(List<Declaration> declarations,
transformGlobals(globals, globalNamer, transformationMap),
];

return transformedDeclarations
return (transformedDeclarations +
_getPrimitiveWrapperClasses(transformationMap))
..sort((Declaration a, Declaration b) => a.id.compareTo(b.id));
}

Expand Down Expand Up @@ -80,3 +82,11 @@ Declaration transformDeclaration(
_ => throw UnimplementedError(),
};
}

List<Declaration> _getPrimitiveWrapperClasses(
TransformationMap transformationMap) {
return transformationMap.entries
.where((entry) => entry.key is BuiltInDeclaration)
.map((entry) => entry.value)
.toList();
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@
import '../../ast/_core/interfaces/compound_declaration.dart';
import '../../ast/_core/interfaces/declaration.dart';
import '../../ast/_core/interfaces/nestable_declaration.dart';
import '../../ast/_core/shared/parameter.dart';
import '../../ast/declarations/built_in/built_in_declaration.dart';
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 '../../parser/_core/utils.dart';
import '../_core/unique_namer.dart';
import '../_core/utils.dart';
import '../transform.dart';
import 'transform_function.dart';
import 'transform_initializer.dart';
Expand All @@ -38,7 +38,7 @@ ClassDeclaration transformCompound(
superClass: objectType,
isWrapper: true,
wrappedInstance: wrappedCompoundInstance,
wrapperInitializer: _buildWrapperInitializer(wrappedCompoundInstance),
wrapperInitializer: buildWrapperInitializer(wrappedCompoundInstance),
);

transformationMap[originalCompound] = transformedCompound;
Expand Down Expand Up @@ -98,24 +98,3 @@ ClassDeclaration transformCompound(

return transformedCompound;
}

InitializerDeclaration _buildWrapperInitializer(
PropertyDeclaration wrappedClassInstance,
) {
return InitializerDeclaration(
id: '',
params: [
Parameter(
name: '_',
internalName: 'wrappedInstance',
type: wrappedClassInstance.type,
)
],
isOverriding: false,
isFailable: false,
throws: false,
async: false,
statements: ['self.${wrappedClassInstance.name} = wrappedInstance'],
hasObjCAnnotation: wrappedClassInstance.hasObjCAnnotation,
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -84,16 +84,17 @@ MethodDeclaration _transformFunction(
)
.toList();

final transformedReturnType = transformReferredType(
originalFunction.returnType,
globalNamer,
transformationMap,
);
final localNamer = UniqueNamer();
final resultName = localNamer.makeUnique('result');

final (wrapperResult, type) = maybeWrapValue(
originalFunction.returnType, resultName, globalNamer, transformationMap,
shouldWrapPrimitives: originalFunction.throws);

final transformedMethod = MethodDeclaration(
id: originalFunction.id,
name: wrapperMethodName,
returnType: transformedReturnType,
returnType: type,
params: transformedParams,
hasObjCAnnotation: true,
isStatic: originalFunction is MethodDeclaration
Expand All @@ -107,6 +108,9 @@ MethodDeclaration _transformFunction(
originalFunction,
transformedMethod,
globalNamer,
localNamer,
resultName,
wrapperResult,
transformationMap,
originalCallGenerator: originalCallStatementGenerator,
);
Expand Down Expand Up @@ -144,10 +148,12 @@ List<String> _generateStatements(
FunctionDeclaration originalFunction,
MethodDeclaration transformedMethod,
UniqueNamer globalNamer,
UniqueNamer localNamer,
String resultName,
String wrappedResult,
TransformationMap transformationMap, {
required String Function(String arguments) originalCallGenerator,
}) {
final localNamer = UniqueNamer();
final arguments = generateInvocationParams(
localNamer, originalFunction.params, transformedMethod.params);
var originalMethodCall = originalCallGenerator(arguments);
Expand All @@ -166,22 +172,8 @@ List<String> _generateStatements(
throw UnimplementedError('Generic types are not implemented yet');
}

final resultName = localNamer.makeUnique('result');
final methodCallStmt = 'let $resultName = $originalMethodCall';

final (wrappedResult, wrapperType) = maybeWrapValue(
originalFunction.returnType,
resultName,
globalNamer,
transformationMap,
);

assert(wrapperType.sameAs(transformedMethod.returnType));

final returnStmt = 'return $wrappedResult';

return [
methodCallStmt,
returnStmt,
'let $resultName = $originalMethodCall',
'return $wrappedResult',
];
}
Original file line number Diff line number Diff line change
Expand Up @@ -73,24 +73,32 @@ Declaration _transformVariable(
? originalVariable.hasSetter
: !originalVariable.isConstant;

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

final localNamer = UniqueNamer();
final resultName = localNamer.makeUnique('result');

final (wrapperResult, type) = maybeWrapValue(
originalVariable.type, resultName, globalNamer, transformationMap,
shouldWrapPrimitives: originalVariable.throws);

return MethodDeclaration(
id: originalVariable.id,
name: wrapperPropertyName,
returnType: transformedType,
returnType: type,
params: [],
hasObjCAnnotation: true,
isStatic: originalVariable is PropertyDeclaration
? originalVariable.isStatic
: true,
statements: [
'let result = $prefix $variableReferenceExpression',
'return $transformedType(result)',
'let $resultName = $prefix $variableReferenceExpression',
'return $wrapperResult',
],
throws: originalVariable.throws,
async: originalVariable.async,
Expand Down
1 change: 1 addition & 0 deletions pkgs/swift2objc/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ topics:
- codegen

dependencies:
collection: ^1.19.1
logging: ^1.3.0
meta: ^1.16.0
path: ^1.9.0
Expand Down
Loading

0 comments on commit 0df33b3

Please sign in to comment.