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] Support async initializers and properties #1963

Merged
merged 2 commits into from
Feb 3, 2025
Merged
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 @@ -21,13 +21,6 @@ InitializerDeclaration parseInitializerDeclaration(

final info = parseFunctionInfo(declarationFragments, symbolgraph);

if (info.async) {
// TODO(https://github.com/dart-lang/native/issues/1778): Support async
// initializerse.
throw Exception("Async initializers aren't supported yet, at "
'${initializerSymbolJson.path}');
}

return InitializerDeclaration(
id: id,
params: info.params,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,6 @@ bool _parseVariableThrows(Json json) {
bool _parseVariableAsync(Json json) {
final async = json['declarationFragments']
.any((frag) => matchFragment(frag, 'keyword', 'async'));
if (async) {
// TODO(https://github.com/dart-lang/native/issues/1778): Support async
// getters.
throw Exception("Async getters aren't supported yet, at ${json.path}");
}
return async;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,15 +62,14 @@ ClassDeclaration transformCompound(
.nonNulls
.toList();

transformedCompound.initializers = originalCompound.initializers
final transformedInitializers = originalCompound.initializers
.map((initializer) => transformInitializer(
initializer,
wrappedCompoundInstance,
parentNamer,
transformationMap,
))
.toList()
..sort((Declaration a, Declaration b) => a.id.compareTo(b.id));
.toList();

final transformedMethods = originalCompound.methods
.map((method) => transformMethod(
Expand All @@ -87,8 +86,14 @@ ClassDeclaration transformCompound(
.toList()
..sort((Declaration a, Declaration b) => a.id.compareTo(b.id));

transformedCompound.initializers = transformedInitializers
.whereType<InitializerDeclaration>()
.toList()
..sort((Declaration a, Declaration b) => a.id.compareTo(b.id));

transformedCompound.methods = (transformedMethods +
transformedProperties.whereType<MethodDeclaration>().toList())
transformedProperties.whereType<MethodDeclaration>().toList() +
transformedInitializers.whereType<MethodDeclaration>().toList())
..sort((Declaration a, Declaration b) => a.id.compareTo(b.id));

return transformedCompound;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,18 @@
// 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/_core/interfaces/declaration.dart';
import '../../ast/_core/shared/parameter.dart';
import '../../ast/_core/shared/referred_type.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 '../_core/unique_namer.dart';
import '../transform.dart';
import 'transform_function.dart';
import 'transform_referred_type.dart';

InitializerDeclaration transformInitializer(
Declaration transformInitializer(
InitializerDeclaration originalInitializer,
PropertyDeclaration wrappedClassInstance,
UniqueNamer globalNamer,
Expand All @@ -30,6 +33,30 @@ InitializerDeclaration transformInitializer(
)
.toList();

if (originalInitializer.async) {
final methodReturnType = transformReferredType(
wrappedClassInstance.type, globalNamer, transformationMap);

return MethodDeclaration(
id: originalInitializer.id,
name: '${originalInitializer.name}Wrapper',
returnType: originalInitializer.isFailable
? OptionalType(methodReturnType)
: methodReturnType,
params: transformedParams,
hasObjCAnnotation: true,
statements: _generateMethodStatements(
originalInitializer,
wrappedClassInstance,
methodReturnType,
transformedParams,
),
throws: originalInitializer.throws,
async: originalInitializer.async,
isStatic: true,
);
}

final transformedInitializer = InitializerDeclaration(
id: originalInitializer.id,
params: transformedParams,
Expand All @@ -56,17 +83,8 @@ List<String> _generateInitializerStatements(
PropertyDeclaration wrappedClassInstance,
InitializerDeclaration transformedInitializer,
) {
final localNamer = UniqueNamer();
final arguments = generateInvocationParams(
localNamer, originalInitializer.params, transformedInitializer.params);
var instanceConstruction =
'${wrappedClassInstance.type.swiftType}($arguments)';
if (transformedInitializer.async) {
instanceConstruction = 'await $instanceConstruction';
}
if (transformedInitializer.throws) {
instanceConstruction = 'try $instanceConstruction';
}
final (instanceConstruction, localNamer) = _generateInstanceConstruction(
originalInitializer, wrappedClassInstance, transformedInitializer.params);
if (originalInitializer.isFailable) {
final instance = localNamer.makeUnique('instance');
return [
Expand All @@ -80,3 +98,47 @@ List<String> _generateInitializerStatements(
return ['${wrappedClassInstance.name} = $instanceConstruction'];
}
}

List<String> _generateMethodStatements(
AmrAhmed119 marked this conversation as resolved.
Show resolved Hide resolved
InitializerDeclaration originalInitializer,
PropertyDeclaration wrappedClassInstance,
ReferredType wrapperClass,
List<Parameter> transformedParams,
) {
final (instanceConstruction, localNamer) = _generateInstanceConstruction(
originalInitializer, wrappedClassInstance, transformedParams);
final instance = localNamer.makeUnique('instance');
if (originalInitializer.isFailable) {
return [
'if let $instance = $instanceConstruction {',
' return ${wrapperClass.swiftType}($instance)',
'} else {',
' return nil',
'}',
];
} else {
return [
'let $instance = $instanceConstruction',
'return ${wrapperClass.swiftType}($instance)',
];
}
}

(String, UniqueNamer) _generateInstanceConstruction(
InitializerDeclaration originalInitializer,
PropertyDeclaration wrappedClassInstance,
List<Parameter> transformedParams,
) {
final localNamer = UniqueNamer();
final arguments = generateInvocationParams(
localNamer, originalInitializer.params, transformedParams);
var instanceConstruction =
'${wrappedClassInstance.type.swiftType}($arguments)';
if (originalInitializer.async) {
instanceConstruction = 'await $instanceConstruction';
}
if (originalInitializer.throws) {
instanceConstruction = 'try $instanceConstruction';
}
return (instanceConstruction, localNamer);
}
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,12 @@ Declaration _transformVariable(
? originalVariable.hasSetter
: !originalVariable.isConstant;

if (originalVariable.throws) {
if (originalVariable.throws || originalVariable.async) {
final prefix = [
if (originalVariable.throws) 'try',
if (originalVariable.async) 'await'
].join(' ');

return MethodDeclaration(
id: originalVariable.id,
name: wrapperPropertyName,
Expand All @@ -84,10 +89,10 @@ Declaration _transformVariable(
? originalVariable.isStatic
: true,
statements: [
'let result = try $variableReferenceExpression',
'let result = $prefix $variableReferenceExpression',
'return $transformedType(result)',
],
throws: true,
throws: originalVariable.throws,
async: originalVariable.async,
);
}
Expand Down
9 changes: 9 additions & 0 deletions pkgs/swift2objc/test/integration/async_init_input.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import Foundation

public class MyClass {
public init() {}
public init?(label1 name1: Int, _ name2: Int) async {}
public init(arg: Int) async {}
public init(arg1: Int, arg2: Int) async throws {}
}

35 changes: 35 additions & 0 deletions pkgs/swift2objc/test/integration/async_init_output.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Test preamble text

import Foundation

@objc public class MyClassWrapper: NSObject {
var wrappedInstance: MyClass

init(_ wrappedInstance: MyClass) {
self.wrappedInstance = wrappedInstance
}

@objc override init() {
wrappedInstance = MyClass()
}

@objc static public func initWrapper(arg: Int) async -> MyClassWrapper {
let instance = await MyClass(arg: arg)
return MyClassWrapper(instance)
}

@objc static public func initWrapper(arg1: Int, arg2: Int) async throws -> MyClassWrapper {
let instance = try await MyClass(arg1: arg1, arg2: arg2)
return MyClassWrapper(instance)
}

@objc static public func initWrapper(label1 name1: Int, _ name2: Int) async -> MyClassWrapper? {
if let instance = await MyClass(label1: name1, name2) {
return MyClassWrapper(instance)
} else {
return nil
}
}

}

29 changes: 29 additions & 0 deletions pkgs/swift2objc/test/integration/async_prop_input.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import Foundation

public class MyClass {
public init(y: Int) async {}

public var classGetter: MyClass {
get async {
await MyClass(y: 3)
}
}
public var otherClassGetter: OtherClass {
get async throws {
OtherClass()
}
}
}

public class OtherClass {}

public var globalClassGetter: MyClass {
get async {
await MyClass(y: 4)
}
}
public var globalOtherClassGetter: OtherClass {
get async throws {
OtherClass()
}
}
50 changes: 50 additions & 0 deletions pkgs/swift2objc/test/integration/async_prop_output.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Test preamble text

import Foundation

@objc public class GlobalsWrapper: NSObject {
@objc static public func globalClassGetterWrapper() async -> MyClassWrapper {
let result = await globalClassGetter
return MyClassWrapper(result)
}

@objc static public func globalOtherClassGetterWrapper() async throws -> OtherClassWrapper {
let result = try await globalOtherClassGetter
return OtherClassWrapper(result)
}

}

@objc public class OtherClassWrapper: NSObject {
var wrappedInstance: OtherClass

init(_ wrappedInstance: OtherClass) {
self.wrappedInstance = wrappedInstance
}

}

@objc public class MyClassWrapper: NSObject {
var wrappedInstance: MyClass

init(_ wrappedInstance: MyClass) {
self.wrappedInstance = wrappedInstance
}

@objc public func otherClassGetter() async throws -> OtherClassWrapper {
let result = try await wrappedInstance.otherClassGetter
return OtherClassWrapper(result)
}

@objc public func classGetter() async -> MyClassWrapper {
let result = await wrappedInstance.classGetter
return MyClassWrapper(result)
}

@objc static public func initWrapper(y: Int) async -> MyClassWrapper {
let instance = await MyClass(y: y)
return MyClassWrapper(instance)
}

}