Skip to content

Commit 82a60e8

Browse files
srujzsCommit Queue
authored and
Commit Queue
committed
[dart2wasm] Refactor method specialization to use inheritance
We've been using enums to denote procedure types and lowerings, but with object literals and invocation-level lowering, the code to handle the enums become increasingly complex. This CL separates out the logic to individual classes instead. Change-Id: I896dcfe1a00d649bf19d8e5315ed1deca3c3dad3 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/290606 Reviewed-by: Joshua Litt <[email protected]> Commit-Queue: Srujan Gaddam <[email protected]>
1 parent 56a43c0 commit 82a60e8

File tree

2 files changed

+373
-330
lines changed

2 files changed

+373
-330
lines changed
Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
// Copyright (c) 2023, 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:_js_interop_checks/src/transformations/js_util_optimizer.dart'
6+
show InlineExtensionIndex;
7+
import 'package:kernel/ast.dart';
8+
9+
bool parametersNeedParens(List<String> parameters) =>
10+
parameters.isEmpty || parameters.length > 1;
11+
12+
/// A general config class for an interop method.
13+
///
14+
/// dart2wasm needs to create a trampoline method in JS that then calls the
15+
/// interop member in question. In order to do so, we need information on things
16+
/// like the name of the member, how many parameters it takes in, and more.
17+
abstract class JSLoweringConfig {
18+
final Procedure interopMethod;
19+
final String jsString;
20+
final InlineExtensionIndex _inlineExtensionIndex;
21+
late final bool firstParameterIsObject =
22+
_inlineExtensionIndex.isInstanceInteropMember(interopMethod);
23+
24+
JSLoweringConfig(
25+
this.interopMethod, this.jsString, this._inlineExtensionIndex);
26+
27+
FunctionNode get function => interopMethod.function;
28+
Uri get fileUri => interopMethod.fileUri;
29+
30+
/// Whether this config is associated with a constructor or factory.
31+
bool get isConstructor;
32+
33+
/// The parameters that determine arity of the interop procedure that is
34+
/// created from this config.
35+
List<VariableDeclaration> get parameters;
36+
37+
/// Returns the string that will be the body of the JS trampoline.
38+
///
39+
/// [object] is the callee if there is one for this config. [callArguments] is
40+
/// the remaining arguments of the `interopMethod`.
41+
String bodyString(String object, List<String> callArguments);
42+
43+
/// Compute and return the JS trampoline string needed for this method
44+
/// lowering.
45+
String generateJS(List<String> parameterNames) {
46+
final object = isConstructor
47+
? ''
48+
: firstParameterIsObject
49+
? parameterNames[0]
50+
: 'globalThis';
51+
final callArguments =
52+
firstParameterIsObject ? parameterNames.sublist(1) : parameterNames;
53+
final callArgumentsString = callArguments.join(',');
54+
final functionParameters = firstParameterIsObject
55+
? '$object${callArguments.isEmpty ? '' : ',$callArgumentsString'}'
56+
: callArgumentsString;
57+
final body = bodyString(object, callArguments);
58+
if (parametersNeedParens(parameterNames)) {
59+
return '($functionParameters) => $body';
60+
} else {
61+
return '$functionParameters => $body';
62+
}
63+
}
64+
}
65+
66+
/// Config class for interop members that get lowered on the procedure side.
67+
abstract class JSProcedureLoweringConfig extends JSLoweringConfig {
68+
JSProcedureLoweringConfig(
69+
super.interopMethod, super.jsString, super._inlineExtensionIndex);
70+
71+
@override
72+
List<VariableDeclaration> get parameters => function.positionalParameters;
73+
}
74+
75+
class JSConstructorLoweringConfig extends JSProcedureLoweringConfig {
76+
JSConstructorLoweringConfig(
77+
super.interopMethod, super.jsString, super._inlineExtensionIndex);
78+
79+
@override
80+
bool get isConstructor => true;
81+
82+
@override
83+
String bodyString(String object, List<String> callArguments) =>
84+
"new $jsString(${callArguments.join(',')})";
85+
}
86+
87+
class JSGetterLoweringConfig extends JSProcedureLoweringConfig {
88+
JSGetterLoweringConfig(
89+
super.interopMethod, super.jsString, super._inlineExtensionIndex);
90+
91+
@override
92+
bool get isConstructor => false;
93+
94+
@override
95+
String bodyString(String object, List<String> callArguments) =>
96+
'$object.$jsString';
97+
}
98+
99+
class JSSetterLoweringConfig extends JSProcedureLoweringConfig {
100+
JSSetterLoweringConfig(
101+
super.interopMethod, super.jsString, super._inlineExtensionIndex);
102+
103+
@override
104+
bool get isConstructor => false;
105+
106+
@override
107+
String bodyString(String object, List<String> callArguments) =>
108+
'$object.$jsString = ${callArguments[0]}';
109+
}
110+
111+
class JSMethodLoweringConfig extends JSProcedureLoweringConfig {
112+
JSMethodLoweringConfig(
113+
super.interopMethod, super.jsString, super._inlineExtensionIndex);
114+
115+
@override
116+
bool get isConstructor => false;
117+
118+
@override
119+
String bodyString(String object, List<String> callArguments) =>
120+
"$object.$jsString(${callArguments.join(',')})";
121+
}
122+
123+
class JSOperatorLoweringConfig extends JSProcedureLoweringConfig {
124+
JSOperatorLoweringConfig(
125+
super.interopMethod, super.jsString, super._inlineExtensionIndex);
126+
127+
@override
128+
bool get isConstructor => false;
129+
130+
@override
131+
String bodyString(String object, List<String> callArguments) {
132+
if (jsString == '[]') {
133+
return '$object[${callArguments[0]}]';
134+
} else if (jsString == '[]=') {
135+
return '$object[${callArguments[0]}] = ${callArguments[1]}';
136+
} else {
137+
throw 'Unsupported operator: $jsString';
138+
}
139+
}
140+
}
141+
142+
/// Config class for interop members that get lowered on the invocation side.
143+
abstract class JSInvocationLoweringConfig extends JSLoweringConfig {
144+
final StaticInvocation invocation;
145+
JSInvocationLoweringConfig(super.interopMethod, super.jsString,
146+
super._inlineExtensionIndex, this.invocation);
147+
148+
/// The parameters of the given `interopMethod` that were given a correspondig
149+
/// argument in the `invocation`.
150+
@override
151+
List<VariableDeclaration> get parameters;
152+
153+
/// The expressions that were passed in the `invocation`.
154+
List<Expression> get arguments;
155+
}
156+
157+
/// Config class for procedures that are lowered on the invocation-side, but
158+
/// only contain positional parameters.
159+
abstract class JSPositionalInvocationLoweringConfig
160+
extends JSInvocationLoweringConfig {
161+
JSPositionalInvocationLoweringConfig(super.interopMethod, super.jsString,
162+
super._inlineExtensionIndex, super.invocation);
163+
164+
@override
165+
List<VariableDeclaration> get parameters => function.positionalParameters
166+
.sublist(0, invocation.arguments.positional.length);
167+
168+
@override
169+
List<Expression> get arguments => invocation.arguments.positional;
170+
}
171+
172+
class JSConstructorInvocationLoweringConfig
173+
extends JSPositionalInvocationLoweringConfig {
174+
JSConstructorInvocationLoweringConfig(super.interopMethod, super.jsString,
175+
super._inlineExtensionIndex, super.invocation);
176+
177+
@override
178+
bool get isConstructor => true;
179+
180+
@override
181+
String bodyString(String object, List<String> callArguments) =>
182+
"new $jsString(${callArguments.join(',')})";
183+
}
184+
185+
class JSMethodInvocationLoweringConfig
186+
extends JSPositionalInvocationLoweringConfig {
187+
JSMethodInvocationLoweringConfig(super.interopMethod, super.jsString,
188+
super._inlineExtensionIndex, super.invocation);
189+
190+
@override
191+
bool get isConstructor => false;
192+
193+
@override
194+
String bodyString(String object, List<String> callArguments) =>
195+
"$object.$jsString(${callArguments.join(',')})";
196+
}
197+
198+
/// Config class for object literals, which only use named arguments and are
199+
/// only lowered at the invocation-level.
200+
class JSObjectLiteralLoweringConfig extends JSInvocationLoweringConfig {
201+
JSObjectLiteralLoweringConfig(Procedure interopMethod,
202+
InlineExtensionIndex _inlineExtensionIndex, StaticInvocation invocation)
203+
: super(interopMethod, '', _inlineExtensionIndex, invocation);
204+
205+
@override
206+
bool get isConstructor => true;
207+
208+
@override
209+
List<VariableDeclaration> get parameters {
210+
// Compute the named parameters that were used in the given `invocation`.
211+
// Note that we preserve the procedure's ordering and not the invocation's.
212+
// This is also used below for the names of object literal arguments in
213+
// `generateJS`.
214+
final usedArgs =
215+
invocation.arguments.named.map((expr) => expr.name).toSet();
216+
return function.namedParameters
217+
.where((decl) => usedArgs.contains(decl.name))
218+
.toList();
219+
}
220+
221+
@override
222+
String bodyString(String object, List<String> callArguments) {
223+
final keys = parameters.map((named) => named.name!).toList();
224+
final keyValuePairs = <String>[];
225+
for (int i = 0; i < callArguments.length; i++) {
226+
keyValuePairs.add('${keys[i]}: ${callArguments[i]}');
227+
}
228+
return '({${keyValuePairs.join(',')}})';
229+
}
230+
231+
@override
232+
List<Expression> get arguments {
233+
// Return the args in the order of the procedure's parameters and not
234+
// the invocation.
235+
final namedArgs = <String, Expression>{};
236+
for (NamedExpression expr in invocation.arguments.named) {
237+
namedArgs[expr.name] = expr.value;
238+
}
239+
return parameters
240+
.map<Expression>((decl) => namedArgs[decl.name!]!)
241+
.toList();
242+
}
243+
}

0 commit comments

Comments
 (0)