Skip to content

Commit

Permalink
Support unlabeled parameters (#1756)
Browse files Browse the repository at this point in the history
  • Loading branch information
liamappelbe authored Nov 27, 2024
1 parent 9ec7403 commit 70e83a9
Show file tree
Hide file tree
Showing 16 changed files with 186 additions and 174 deletions.
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

0 comments on commit 70e83a9

Please sign in to comment.