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 unlabeled parameters #1756

Merged
merged 1 commit into from
Nov 27, 2024
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
2 changes: 2 additions & 0 deletions pkgs/swift2objc/lib/src/parser/_core/token_list.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ class TokenList {
'?(': ['?', '('],
'?)': ['?', ')'],
'?, ': ['?', ', '],
') -> ': [')', '->'],
'?) -> ': ['?', ')', '->'],
};

final list = <Json>[];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ GlobalFunctionDeclaration parseGlobalFunctionDeclaration(
id: parseSymbolId(globalFunctionSymbolJson),
name: parseSymbolName(globalFunctionSymbolJson),
returnType: _parseFunctionReturnType(globalFunctionSymbolJson, symbolgraph),
params: _parseFunctionParams(globalFunctionSymbolJson, symbolgraph),
params: parseFunctionParams(
globalFunctionSymbolJson['declarationFragments'], symbolgraph),
);
}

Expand All @@ -33,12 +34,81 @@ MethodDeclaration parseMethodDeclaration(
id: parseSymbolId(methodSymbolJson),
name: parseSymbolName(methodSymbolJson),
returnType: _parseFunctionReturnType(methodSymbolJson, symbolgraph),
params: _parseFunctionParams(methodSymbolJson, symbolgraph),
params: parseFunctionParams(
methodSymbolJson['declarationFragments'], symbolgraph),
hasObjCAnnotation: parseSymbolHasObjcAnnotation(methodSymbolJson),
isStatic: isStatic,
);
}

List<Parameter> parseFunctionParams(
Json declarationFragments,
ParsedSymbolgraph symbolgraph,
) {
// `declarationFragments` describes each part of the initializer declaration,
// things like the `func` keyword, brackets, spaces, etc. We only care about
// the parameter fragments here, and they always appear in this order:
// [
// ..., '(',
// externalParam, ' ', internalParam, ': ', type..., ', '
// externalParam, ': ', type..., ', '
// externalParam, ' ', internalParam, ': ', type..., ')'
// ]
// Note: `internalParam` may or may not exist.
//
// The following loop attempts to extract parameters from this flat array
// while making sure the parameter fragments have the expected order.

final parameters = <Parameter>[];

var tokens = TokenList(declarationFragments);
final openParen = tokens.indexWhere((tok) => matchFragment(tok, 'text', '('));
if (openParen != -1) {
tokens = tokens.slice(openParen + 1);
String? consume(String kind) {
if (tokens.isEmpty) return null;
final token = tokens[0];
tokens = tokens.slice(1);
return getSpellingForKind(token, kind);
}

final malformedInitializerException = Exception(
'Malformed initializer at ${declarationFragments.path}',
);
while (true) {
final externalParam = consume('externalParam');
if (externalParam == null) throw malformedInitializerException;

var sep = consume('text');
String? internalParam;
if (sep == ' ') {
internalParam = consume('internalParam');
if (internalParam == null) throw malformedInitializerException;
sep = consume('text');
}

if (sep != ': ') throw malformedInitializerException;
final (type, remainingTokens) = parseType(symbolgraph, tokens);
tokens = remainingTokens;

parameters.add(Parameter(
name: externalParam,
internalName: internalParam,
type: type,
));

final end = consume('text');
if (end == ')') break;
if (end != ', ') throw malformedInitializerException;
}
if (!(tokens.isEmpty || consume('text') == '->')) {
throw malformedInitializerException;
}
}

return parameters;
}

ReferredType _parseFunctionReturnType(
Json methodSymbolJson,
ParsedSymbolgraph symbolgraph,
Expand All @@ -49,29 +119,3 @@ ReferredType _parseFunctionReturnType(
assert(unparsed.isEmpty, '$returnJson\n\n$returnType\n\n$unparsed\n');
return returnType;
}

List<Parameter> _parseFunctionParams(
Json methodSymbolJson,
ParsedSymbolgraph symbolgraph,
) {
final paramList = methodSymbolJson['functionSignature']['parameters'];

if (!paramList.exists) return [];

return paramList
.map(
(param) => Parameter(
name: param['name'].get(),
internalName: param['internalName'].get(),
type: _parseParamType(param, symbolgraph),
),
)
.toList();
}

ReferredType _parseParamType(
Json paramSymbolJson,
ParsedSymbolgraph symbolgraph,
) =>
parseTypeAfterSeparator(
TokenList(paramSymbolJson['declarationFragments']), symbolgraph);
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import '../../../ast/_core/shared/parameter.dart';
import '../../../ast/declarations/compounds/members/initializer_declaration.dart';
import '../../_core/json.dart';
import '../../_core/parsed_symbolgraph.dart';
import '../../_core/token_list.dart';
import '../../_core/utils.dart';
import '../parse_type.dart';
import 'parse_function_declaration.dart';

InitializerDeclaration parseInitializerDeclaration(
Json initializerSymbolJson,
Expand All @@ -23,7 +21,7 @@ InitializerDeclaration parseInitializerDeclaration(

return InitializerDeclaration(
id: id,
params: parseInitializerParams(declarationFragments, symbolgraph),
params: parseFunctionParams(declarationFragments, symbolgraph),
hasObjCAnnotation: parseSymbolHasObjcAnnotation(initializerSymbolJson),
isOverriding: parseIsOverriding(initializerSymbolJson),
isFailable: parseIsFailableInit(id, declarationFragments),
Expand All @@ -32,69 +30,3 @@ InitializerDeclaration parseInitializerDeclaration(

bool parseIsFailableInit(String id, Json declarationFragments) =>
matchFragment(declarationFragments[1], 'text', '?(');

List<Parameter> parseInitializerParams(
Json declarationFragments,
ParsedSymbolgraph symbolgraph,
) {
// `declarationFragments` describes each part of the initializer declaration,
// things like `init` keyword, brackets, spaces, etc. We only care about the
// parameter fragments here, and they always appear in this order:
// [
// ..., '(',
// externalParam, ' ', internalParam, ': ', type..., ', '
// externalParam, ': ', type..., ', '
// externalParam, ' ', internalParam, ': ', type..., ')'
// ]
// Note: `internalParam` may or may not exist.
//
// The following loop attempts to extract parameters from this flat array
// while making sure the parameter fragments have the expected order.

final parameters = <Parameter>[];

var tokens = TokenList(declarationFragments);
final openParen = tokens.indexWhere((tok) => matchFragment(tok, 'text', '('));
if (openParen != -1) {
tokens = tokens.slice(openParen + 1);
String? consume(String kind) {
if (tokens.isEmpty) return null;
final token = tokens[0];
tokens = tokens.slice(1);
return getSpellingForKind(token, kind);
}

final malformedInitializerException = Exception(
'Malformed initializer at ${declarationFragments.path}',
);
while (true) {
final externalParam = consume('externalParam');
if (externalParam == null) throw malformedInitializerException;

var sep = consume('text');
String? internalParam;
if (sep == ' ') {
internalParam = consume('internalParam');
if (internalParam == null) throw malformedInitializerException;
sep = consume('text');
}

if (sep != ': ') throw malformedInitializerException;
final (type, remainingTokens) = parseType(symbolgraph, tokens);
tokens = remainingTokens;

parameters.add(Parameter(
name: externalParam,
internalName: internalParam,
type: type,
));

final end = consume('text');
if (end == ')') break;
if (end != ', ') throw malformedInitializerException;
}
if (!tokens.isEmpty) throw malformedInitializerException;
}

return parameters;
}
Original file line number Diff line number Diff line change
Expand Up @@ -112,21 +112,17 @@ MethodDeclaration _transformFunction(
return transformedMethod;
}

List<String> _generateStatements(
FunctionDeclaration originalFunction,
MethodDeclaration transformedMethod,
UniqueNamer globalNamer,
TransformationMap transformationMap, {
required String Function(String arguments) originalCallGenerator,
}) {
final argumentsList = <String>[];
String generateInvocationParams(UniqueNamer localNamer,
List<Parameter> originalParams, List<Parameter> transformedParams) {
assert(originalParams.length == transformedParams.length);

for (var i = 0; i < originalFunction.params.length; i++) {
final originalParam = originalFunction.params[i];
final transformedParam = transformedMethod.params[i];
final argumentsList = <String>[];
for (var i = 0; i < originalParams.length; i++) {
final originalParam = originalParams[i];
final transformedParam = transformedParams[i];

final transformedParamName =
transformedParam.internalName ?? transformedParam.name;
final transformedParamName = localNamer
.makeUnique(transformedParam.internalName ?? transformedParam.name);

final (unwrappedParamValue, unwrappedType) = maybeUnwrapValue(
transformedParam.type,
Expand All @@ -135,13 +131,23 @@ List<String> _generateStatements(

assert(unwrappedType.sameAs(originalParam.type));

var methodCallArg = '${originalParam.name}: $unwrappedParamValue';

argumentsList.add(methodCallArg);
argumentsList.add(originalParam.name == '_'
? unwrappedParamValue
: '${originalParam.name}: $unwrappedParamValue');
}
return argumentsList.join(', ');
}

final arguments = argumentsList.join(', ');

List<String> _generateStatements(
FunctionDeclaration originalFunction,
MethodDeclaration transformedMethod,
UniqueNamer globalNamer,
TransformationMap transformationMap, {
required String Function(String arguments) originalCallGenerator,
}) {
final localNamer = UniqueNamer();
final arguments = generateInvocationParams(
localNamer, originalFunction.params, transformedMethod.params);
final originalMethodCall = originalCallGenerator(arguments);

if (originalFunction.returnType.sameAs(transformedMethod.returnType)) {
Expand All @@ -152,11 +158,12 @@ List<String> _generateStatements(
throw UnimplementedError('Generic types are not implemented yet');
}

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

final (wrappedResult, wrapperType) = maybeWrapValue(
originalFunction.returnType,
'result',
resultName,
globalNamer,
transformationMap,
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import '../../ast/_core/shared/parameter.dart';
import '../../ast/declarations/compounds/members/initializer_declaration.dart';
import '../../ast/declarations/compounds/members/property_declaration.dart';
import '../_core/unique_namer.dart';
import '../_core/utils.dart';
import '../transform.dart';
import 'transform_function.dart';
import 'transform_referred_type.dart';

InitializerDeclaration transformInitializer(
Expand Down Expand Up @@ -54,30 +54,9 @@ List<String> _generateInitializerStatements(
PropertyDeclaration wrappedClassInstance,
InitializerDeclaration transformedInitializer,
) {
final argumentsList = <String>[];
final localNamer = UniqueNamer();

for (var i = 0; i < originalInitializer.params.length; i++) {
final originalParam = originalInitializer.params[i];
final transformedParam = transformedInitializer.params[i];

final transformedParamName = localNamer
.makeUnique(transformedParam.internalName ?? transformedParam.name);

final (unwrappedParamValue, unwrappedType) = maybeUnwrapValue(
transformedParam.type,
transformedParamName,
);

assert(unwrappedType.sameAs(originalParam.type));

var methodCallArg = '${originalParam.name}: $unwrappedParamValue';

argumentsList.add(methodCallArg);
}

final arguments = argumentsList.join(', ');

final arguments = generateInvocationParams(
localNamer, originalInitializer.params, transformedInitializer.params);
final instanceConstruction =
'${wrappedClassInstance.type.swiftType}($arguments)';
if (originalInitializer.isFailable) {
Expand Down
1 change: 1 addition & 0 deletions pkgs/swift2objc/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,6 @@ environment:
sdk: '>=3.3.0 <4.0.0'

dev_dependencies:
args: ^2.6.0
dart_flutter_team_lints: ^2.0.0
test: ^1.21.1
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ public class MyClass {
self.customProperty = customProperty
}

public init(label1 name1: Int, label2: Int, _ name3: Int) {
self.representableProperty = name1
self.customProperty = MyOtherClass()
}

public init?(outerLabel x: Int) {
if x == 0 {
return nil
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,9 @@ import Foundation
}
}

@objc init(label1 name1: Int, label2: Int, _ name3: Int) {
wrappedInstance = MyClass(label1: name1, label2: label2, name3)
}

}

Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Foundation

public class MyClass {
public func myMethod(label1 param1: Int, param2: MyOtherClass) -> MyOtherClass {
public func myMethod(label1 param1: Int, param2: MyOtherClass, _ param3: Int) -> MyOtherClass {
return MyOtherClass()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ import Foundation
self.wrappedInstance = wrappedInstance
}

@objc public func myMethod(label1 param1: Int, param2: MyOtherClassWrapper) -> MyOtherClassWrapper {
let result = wrappedInstance.myMethod(label1: param1, param2: param2.wrappedInstance)
@objc public func myMethod(label1 param1: Int, param2: MyOtherClassWrapper, _ param3: Int) -> MyOtherClassWrapper {
let result = wrappedInstance.myMethod(label1: param1, param2: param2.wrappedInstance, param3)
return MyOtherClassWrapper(result)
}

Expand Down
Loading