Skip to content

Commit 2fa4f72

Browse files
committed
[dart2wasm] Improve jsify calls based on the converted value's static type
Issue: #60357 Issue: #55174 Change-Id: I653bb35c892bf21a37fac87f5cac698e88855f2b Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/417520 Reviewed-by: Martin Kustermann <[email protected]>
1 parent 8d693e0 commit 2fa4f72

File tree

4 files changed

+369
-47
lines changed

4 files changed

+369
-47
lines changed

pkg/dart2wasm/lib/js/callback_specializer.dart

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,19 @@ class CallbackSpecializer {
4343
}
4444
callbackArguments.add(expression);
4545
}
46-
return ReturnStatement(StaticInvocation(
47-
_util.jsifyTarget(function.returnType),
48-
Arguments([
49-
FunctionInvocation(FunctionAccessKind.FunctionType,
50-
VariableGet(callbackVariable), Arguments(callbackArguments),
51-
functionType: null),
52-
])));
46+
47+
final callExpr = FunctionInvocation(FunctionAccessKind.FunctionType,
48+
VariableGet(callbackVariable), Arguments(callbackArguments),
49+
functionType: null);
50+
51+
final temp = VariableDeclaration(null,
52+
initializer: callExpr,
53+
type: callExpr.getStaticType(_staticTypeContext),
54+
isSynthesized: true);
55+
56+
final jsified = jsifyValue(temp, _util, _staticTypeContext.typeEnvironment);
57+
58+
return ReturnStatement(Let(temp, jsified));
5359
}
5460

5561
/// Creates a callback trampoline for the given [function].

pkg/dart2wasm/lib/js/interop_specializer.dart

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -133,8 +133,8 @@ abstract class _ProcedureSpecializer extends _Specializer {
133133
Expression invocation = StaticInvocation(
134134
_getOrCreateInteropProcedure(),
135135
Arguments(parameters
136-
.map<Expression>((value) => StaticInvocation(
137-
_util.jsifyTarget(value.type), Arguments([VariableGet(value)])))
136+
.map<Expression>((variable) => jsifyValue(variable, factory._util,
137+
factory._staticTypeContext.typeEnvironment))
138138
.toList()));
139139
return _util.castInvocationForReturn(invocation, function.returnType);
140140
}
@@ -227,11 +227,16 @@ abstract class _PositionalInvocationSpecializer extends _InvocationSpecializer {
227227
// arguments. Cast as needed and return the final invocation.
228228
final staticInvocation = StaticInvocation(
229229
_getOrCreateInteropProcedure(),
230-
Arguments(invocation.arguments.positional
231-
.map<Expression>((expr) => StaticInvocation(
232-
_util.jsifyTarget(expr.getStaticType(_staticTypeContext)),
233-
Arguments([expr])))
234-
.toList()));
230+
Arguments(invocation.arguments.positional.map<Expression>((expr) {
231+
final temp = VariableDeclaration(null,
232+
initializer: expr,
233+
type: expr.getStaticType(factory._staticTypeContext),
234+
isSynthesized: true);
235+
return Let(
236+
temp,
237+
jsifyValue(temp, factory._util,
238+
factory._staticTypeContext.typeEnvironment));
239+
}).toList()));
235240
return _util.castInvocationForReturn(
236241
staticInvocation, invocation.getStaticType(_staticTypeContext));
237242
}
@@ -318,11 +323,16 @@ class _ObjectLiteralSpecializer extends _InvocationSpecializer {
318323
}
319324
final arguments =
320325
parameters.map<Expression>((decl) => namedArgs[decl.name!]!).toList();
321-
final positionalArgs = arguments
322-
.map<Expression>((expr) => StaticInvocation(
323-
_util.jsifyTarget(expr.getStaticType(_staticTypeContext)),
324-
Arguments([expr])))
325-
.toList();
326+
final positionalArgs = arguments.map<Expression>((expr) {
327+
final temp = VariableDeclaration(null,
328+
initializer: expr,
329+
type: expr.getStaticType(_staticTypeContext),
330+
isSynthesized: true);
331+
return Let(
332+
temp,
333+
jsifyValue(
334+
temp, factory._util, factory._staticTypeContext.typeEnvironment));
335+
}).toList();
326336
assert(factory._extensionIndex.isStaticInteropType(function.returnType));
327337
return invokeOneArg(_util.jsValueBoxTarget,
328338
StaticInvocation(interopProcedure, Arguments(positionalArgs)));

pkg/dart2wasm/lib/js/util.dart

Lines changed: 247 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,13 @@ import 'package:_js_interop_checks/src/transformations/js_util_optimizer.dart'
66
show ExtensionIndex;
77
import 'package:kernel/ast.dart';
88
import 'package:kernel/core_types.dart';
9+
import 'package:kernel/type_environment.dart';
910

1011
enum AnnotationType { import, export, weakExport }
1112

1213
/// A utility wrapper for [CoreTypes].
1314
class CoreTypesUtil {
14-
final ExtensionIndex _extensionIndex;
15+
final ExtensionIndex extensionIndex;
1516
final CoreTypes coreTypes;
1617
final Procedure allowInteropTarget;
1718
final Procedure dartifyRawTarget;
@@ -31,8 +32,65 @@ class CoreTypesUtil {
3132
final Class wasmArrayRefClass;
3233
final Procedure wrapDartFunctionTarget;
3334
final Procedure exportWasmFunctionTarget;
35+
final Member wasmExternRefNullRef;
3436

35-
CoreTypesUtil(this.coreTypes, this._extensionIndex)
37+
// Dart value to JS converters.
38+
final Procedure toJSBoolean;
39+
final Procedure jsifyInt;
40+
final Procedure toJSNumber;
41+
final Procedure jsifyNum;
42+
final Procedure jsifyJSValue;
43+
final Procedure jsifyString;
44+
final Procedure jsifyJSInt8ArrayImpl;
45+
final Procedure jsifyJSUint8ArrayImpl;
46+
final Procedure jsifyJSUint8ClampedArrayImpl;
47+
final Procedure jsifyJSInt16ArrayImpl;
48+
final Procedure jsifyJSUint16ArrayImpl;
49+
final Procedure jsifyJSInt32ArrayImpl;
50+
final Procedure jsifyJSUint32ArrayImpl;
51+
final Procedure jsifyJSFloat32ArrayImpl;
52+
final Procedure jsifyJSFloat64ArrayImpl;
53+
final Procedure jsInt8ArrayFromDartInt8List;
54+
final Procedure jsUint8ArrayFromDartUint8List;
55+
final Procedure jsUint8ClampedArrayFromDartUint8ClampedList;
56+
final Procedure jsInt16ArrayFromDartInt16List;
57+
final Procedure jsUint16ArrayFromDartUint16List;
58+
final Procedure jsInt32ArrayFromDartInt32List;
59+
final Procedure jsUint32ArrayFromDartUint32List;
60+
final Procedure jsFloat32ArrayFromDartFloat32List;
61+
final Procedure jsFloat64ArrayFromDartFloat64List;
62+
final Procedure jsifyJSDataViewImpl; // JS ByteData
63+
final Procedure jsifyByteData; // Wasm ByteData
64+
final Procedure jsifyRawList;
65+
final Procedure jsifyJSArrayBufferImpl; // JS ByteBuffer
66+
final Procedure jsArrayBufferFromDartByteBuffer; // Wasm ByteBuffer
67+
final Procedure jsifyFunction;
68+
69+
// Classes used in type tests for the converters.
70+
final Class jsInt8ArrayImplClass;
71+
final Class jsUint8ArrayImplClass;
72+
final Class jsUint8ClampedArrayImplClass;
73+
final Class jsInt16ArrayImplClass;
74+
final Class jsUint16ArrayImplClass;
75+
final Class jsInt32ArrayImplClass;
76+
final Class jsUint32ArrayImplClass;
77+
final Class jsFloat32ArrayImplClass;
78+
final Class jsFloat64ArrayImplClass;
79+
final Class int8ListClass;
80+
final Class uint8ListClass;
81+
final Class uint8ClampedListClass;
82+
final Class int16ListClass;
83+
final Class uint16ListClass;
84+
final Class int32ListClass;
85+
final Class uint32ListClass;
86+
final Class float32ListClass;
87+
final Class float64ListClass;
88+
final Class jsDataViewImplClass;
89+
final Class byteDataClass;
90+
final Class jsArrayBufferImplClass;
91+
final Class byteBufferClass;
92+
93+
CoreTypesUtil(this.coreTypes, this.extensionIndex)
3694
: allowInteropTarget = coreTypes.index
3795
.getTopLevelProcedure('dart:js_util', 'allowInterop'),
3896
dartifyRawTarget = coreTypes.index
@@ -61,13 +119,120 @@ class CoreTypesUtil {
61119
coreTypes.index.getProcedure('dart:_js_helper', 'JSValue', 'unbox'),
62120
wasmExternRefClass =
63121
coreTypes.index.getClass('dart:_wasm', 'WasmExternRef'),
122+
wasmExternRefNullRef = coreTypes.index
123+
.getMember('dart:_wasm', 'WasmExternRef', 'get:nullRef'),
64124
wasmArrayClass = coreTypes.index.getClass('dart:_wasm', 'WasmArray'),
65125
wasmArrayRefClass =
66126
coreTypes.index.getClass('dart:_wasm', 'WasmArrayRef'),
67127
wrapDartFunctionTarget = coreTypes.index
68128
.getTopLevelProcedure('dart:_js_helper', '_wrapDartFunction'),
69129
exportWasmFunctionTarget = coreTypes.index
70-
.getTopLevelProcedure('dart:_internal', 'exportWasmFunction');
130+
.getTopLevelProcedure('dart:_internal', 'exportWasmFunction'),
131+
toJSBoolean = coreTypes.index
132+
.getTopLevelProcedure('dart:_js_helper', 'toJSBoolean'),
133+
jsifyInt =
134+
coreTypes.index.getTopLevelProcedure('dart:_js_helper', 'jsifyInt'),
135+
toJSNumber = coreTypes.index
136+
.getTopLevelProcedure('dart:_js_helper', 'toJSNumber'),
137+
jsifyNum =
138+
coreTypes.index.getTopLevelProcedure('dart:_js_helper', 'jsifyNum'),
139+
jsifyJSValue = coreTypes.index
140+
.getTopLevelProcedure('dart:_js_helper', 'jsifyJSValue'),
141+
jsifyString = coreTypes.index
142+
.getTopLevelProcedure('dart:_js_helper', 'jsifyString'),
143+
jsifyJSInt8ArrayImpl = coreTypes.index
144+
.getTopLevelProcedure('dart:_js_helper', 'jsifyJSInt8ArrayImpl'),
145+
jsifyJSUint8ArrayImpl = coreTypes.index
146+
.getTopLevelProcedure('dart:_js_helper', 'jsifyJSUint8ArrayImpl'),
147+
jsifyJSUint8ClampedArrayImpl = coreTypes.index.getTopLevelProcedure(
148+
'dart:_js_helper', 'jsifyJSUint8ClampedArrayImpl'),
149+
jsifyJSInt16ArrayImpl = coreTypes.index
150+
.getTopLevelProcedure('dart:_js_helper', 'jsifyJSInt16ArrayImpl'),
151+
jsifyJSUint16ArrayImpl = coreTypes.index
152+
.getTopLevelProcedure('dart:_js_helper', 'jsifyJSUint16ArrayImpl'),
153+
jsifyJSInt32ArrayImpl = coreTypes.index
154+
.getTopLevelProcedure('dart:_js_helper', 'jsifyJSInt32ArrayImpl'),
155+
jsifyJSUint32ArrayImpl = coreTypes.index
156+
.getTopLevelProcedure('dart:_js_helper', 'jsifyJSUint32ArrayImpl'),
157+
jsifyJSFloat32ArrayImpl = coreTypes.index
158+
.getTopLevelProcedure('dart:_js_helper', 'jsifyJSFloat32ArrayImpl'),
159+
jsifyJSFloat64ArrayImpl = coreTypes.index
160+
.getTopLevelProcedure('dart:_js_helper', 'jsifyJSFloat64ArrayImpl'),
161+
jsInt8ArrayFromDartInt8List = coreTypes.index.getTopLevelProcedure(
162+
'dart:_js_helper', 'jsInt8ArrayFromDartInt8List'),
163+
jsUint8ArrayFromDartUint8List = coreTypes.index.getTopLevelProcedure(
164+
'dart:_js_helper', 'jsUint8ArrayFromDartUint8List'),
165+
jsUint8ClampedArrayFromDartUint8ClampedList = coreTypes.index
166+
.getTopLevelProcedure('dart:_js_helper',
167+
'jsUint8ClampedArrayFromDartUint8ClampedList'),
168+
jsInt16ArrayFromDartInt16List = coreTypes.index.getTopLevelProcedure(
169+
'dart:_js_helper', 'jsInt16ArrayFromDartInt16List'),
170+
jsUint16ArrayFromDartUint16List = coreTypes.index.getTopLevelProcedure(
171+
'dart:_js_helper', 'jsUint16ArrayFromDartUint16List'),
172+
jsInt32ArrayFromDartInt32List = coreTypes.index.getTopLevelProcedure(
173+
'dart:_js_helper', 'jsInt32ArrayFromDartInt32List'),
174+
jsUint32ArrayFromDartUint32List = coreTypes.index.getTopLevelProcedure(
175+
'dart:_js_helper', 'jsUint32ArrayFromDartUint32List'),
176+
jsFloat32ArrayFromDartFloat32List = coreTypes.index
177+
.getTopLevelProcedure(
178+
'dart:_js_helper', 'jsFloat32ArrayFromDartFloat32List'),
179+
jsFloat64ArrayFromDartFloat64List = coreTypes.index
180+
.getTopLevelProcedure(
181+
'dart:_js_helper', 'jsFloat64ArrayFromDartFloat64List'),
182+
jsifyJSDataViewImpl = coreTypes.index
183+
.getTopLevelProcedure('dart:_js_helper', 'jsifyJSDataViewImpl'),
184+
jsifyByteData = coreTypes.index
185+
.getTopLevelProcedure('dart:_js_helper', 'jsifyByteData'),
186+
jsifyRawList = coreTypes.index
187+
.getTopLevelProcedure('dart:_js_helper', '_jsifyRawList'),
188+
jsifyJSArrayBufferImpl = coreTypes.index
189+
.getTopLevelProcedure('dart:_js_helper', 'jsifyJSArrayBufferImpl'),
190+
jsArrayBufferFromDartByteBuffer = coreTypes.index.getTopLevelProcedure(
191+
'dart:_js_helper', 'jsArrayBufferFromDartByteBuffer'),
192+
jsifyFunction = coreTypes.index
193+
.getTopLevelProcedure('dart:_js_helper', 'jsifyFunction'),
194+
jsInt8ArrayImplClass =
195+
coreTypes.index.getClass('dart:_js_types', 'JSInt8ArrayImpl'),
196+
jsUint8ArrayImplClass =
197+
coreTypes.index.getClass('dart:_js_types', 'JSUint8ArrayImpl'),
198+
jsUint8ClampedArrayImplClass = coreTypes.index
199+
.getClass('dart:_js_types', 'JSUint8ClampedArrayImpl'),
200+
jsInt16ArrayImplClass =
201+
coreTypes.index.getClass('dart:_js_types', 'JSInt16ArrayImpl'),
202+
jsUint16ArrayImplClass =
203+
coreTypes.index.getClass('dart:_js_types', 'JSUint16ArrayImpl'),
204+
jsInt32ArrayImplClass =
205+
coreTypes.index.getClass('dart:_js_types', 'JSInt32ArrayImpl'),
206+
jsUint32ArrayImplClass =
207+
coreTypes.index.getClass('dart:_js_types', 'JSUint32ArrayImpl'),
208+
jsFloat32ArrayImplClass =
209+
coreTypes.index.getClass('dart:_js_types', 'JSFloat32ArrayImpl'),
210+
jsFloat64ArrayImplClass =
211+
coreTypes.index.getClass('dart:_js_types', 'JSFloat64ArrayImpl'),
212+
int8ListClass = coreTypes.index.getClass('dart:typed_data', 'Int8List'),
213+
uint8ListClass =
214+
coreTypes.index.getClass('dart:typed_data', 'Uint8List'),
215+
uint8ClampedListClass =
216+
coreTypes.index.getClass('dart:typed_data', 'Uint8ClampedList'),
217+
int16ListClass =
218+
coreTypes.index.getClass('dart:typed_data', 'Int16List'),
219+
uint16ListClass =
220+
coreTypes.index.getClass('dart:typed_data', 'Uint16List'),
221+
int32ListClass =
222+
coreTypes.index.getClass('dart:typed_data', 'Int32List'),
223+
uint32ListClass =
224+
coreTypes.index.getClass('dart:typed_data', 'Uint32List'),
225+
float32ListClass =
226+
coreTypes.index.getClass('dart:typed_data', 'Float32List'),
227+
float64ListClass =
228+
coreTypes.index.getClass('dart:typed_data', 'Float64List'),
229+
jsDataViewImplClass =
230+
coreTypes.index.getClass('dart:_js_types', 'JSDataViewImpl'),
231+
byteDataClass = coreTypes.index.getClass('dart:typed_data', 'ByteData'),
232+
byteBufferClass =
233+
coreTypes.index.getClass('dart:typed_data', 'ByteBuffer'),
234+
jsArrayBufferImplClass =
235+
coreTypes.index.getClass('dart:_js_types', 'JSArrayBufferImpl');
71236

72237
DartType get nonNullableObjectType =>
73238
coreTypes.objectRawType(Nullability.nonNullable);
@@ -81,13 +246,10 @@ class CoreTypesUtil {
81246
DartType get nullableWasmExternRefType =>
82247
wasmExternRefClass.getThisType(coreTypes, Nullability.nullable);
83248

84-
Procedure jsifyTarget(DartType type) =>
85-
isJSValueType(type) ? jsValueUnboxTarget : jsifyRawTarget;
86-
87249
/// Whether [type] erases to a `JSValue` or `JSValue?`.
88250
bool isJSValueType(DartType type) =>
89-
_extensionIndex.isStaticInteropType(type) ||
90-
_extensionIndex.isExternalDartReferenceType(type);
251+
extensionIndex.isStaticInteropType(type) ||
252+
extensionIndex.isExternalDartReferenceType(type);
91253

92254
void annotateProcedure(
93255
Procedure procedure, String pragmaOptionString, AnnotationType type) {
@@ -215,3 +377,80 @@ InstanceInvocation invokeMethod(VariableDeclaration receiver, Procedure target,
215377

216378
bool parametersNeedParens(List<String> parameters) =>
217379
parameters.isEmpty || parameters.length > 1;
380+
381+
Expression jsifyValue(VariableDeclaration variable, CoreTypesUtil coreTypes,
382+
TypeEnvironment typeEnv) {
383+
final Procedure conversionProcedure;
384+
385+
if (coreTypes.extensionIndex.isStaticInteropType(variable.type) ||
386+
coreTypes.extensionIndex.isExternalDartReferenceType(variable.type)) {
387+
conversionProcedure = coreTypes.jsValueUnboxTarget;
388+
} else {
389+
conversionProcedure =
390+
_conversionProcedure(variable.type, coreTypes, typeEnv);
391+
}
392+
393+
final conversion =
394+
StaticInvocation(conversionProcedure, Arguments([VariableGet(variable)]));
395+
396+
if (variable.type.isPotentiallyNullable) {
397+
return ConditionalExpression(
398+
EqualsNull(VariableGet(variable)),
399+
StaticGet(coreTypes.wasmExternRefNullRef),
400+
conversion,
401+
InterfaceType(coreTypes.wasmExternRefClass, Nullability.nullable));
402+
} else {
403+
return conversion;
404+
}
405+
}
406+
407+
Procedure _conversionProcedure(
408+
DartType type, CoreTypesUtil util, TypeEnvironment typeEnv) {
409+
// NB. We rely on iteration ordering being insertion order to handle subtypes
410+
// before supertypes to convert as `int` and `double` before `num`.
411+
final Map<Class, Procedure> converterMap = {
412+
util.coreTypes.boolClass: util.toJSBoolean,
413+
util.coreTypes.intClass: util.jsifyInt,
414+
util.coreTypes.doubleClass: util.toJSNumber,
415+
util.coreTypes.numClass: util.jsifyNum,
416+
util.jsValueClass: util.jsifyJSValue,
417+
util.coreTypes.stringClass: util.jsifyString,
418+
util.jsInt8ArrayImplClass: util.jsifyJSInt8ArrayImpl,
419+
util.jsUint8ArrayImplClass: util.jsifyJSUint8ArrayImpl,
420+
util.jsUint8ClampedArrayImplClass: util.jsifyJSUint8ClampedArrayImpl,
421+
util.jsInt16ArrayImplClass: util.jsifyJSInt16ArrayImpl,
422+
util.jsUint16ArrayImplClass: util.jsifyJSUint16ArrayImpl,
423+
util.jsInt32ArrayImplClass: util.jsifyJSInt32ArrayImpl,
424+
util.jsUint32ArrayImplClass: util.jsifyJSUint32ArrayImpl,
425+
util.jsFloat32ArrayImplClass: util.jsifyJSFloat32ArrayImpl,
426+
util.jsFloat64ArrayImplClass: util.jsifyJSFloat64ArrayImpl,
427+
util.int8ListClass: util.jsInt8ArrayFromDartInt8List,
428+
util.uint8ListClass: util.jsUint8ArrayFromDartUint8List,
429+
util.uint8ClampedListClass:
430+
util.jsUint8ClampedArrayFromDartUint8ClampedList,
431+
util.int16ListClass: util.jsInt16ArrayFromDartInt16List,
432+
util.uint16ListClass: util.jsUint16ArrayFromDartUint16List,
433+
util.int32ListClass: util.jsInt32ArrayFromDartInt32List,
434+
util.uint32ListClass: util.jsUint32ArrayFromDartUint32List,
435+
util.float32ListClass: util.jsFloat32ArrayFromDartFloat32List,
436+
util.float64ListClass: util.jsFloat64ArrayFromDartFloat64List,
437+
util.jsDataViewImplClass: util.jsifyJSDataViewImpl,
438+
util.byteDataClass: util.jsifyByteData,
439+
util.coreTypes.listClass: util.jsifyRawList,
440+
util.jsArrayBufferImplClass: util.jsifyJSArrayBufferImpl,
441+
util.byteBufferClass: util.jsArrayBufferFromDartByteBuffer,
442+
util.coreTypes.functionClass: util.jsifyFunction,
443+
};
444+
445+
for (final entry in converterMap.entries) {
446+
if (typeEnv.isSubtypeOf(
447+
type,
448+
InterfaceType(entry.key, Nullability.nonNullable),
449+
SubtypeCheckMode.withNullabilities)) {
450+
return entry.value;
451+
}
452+
}
453+
454+
// `dynamic` or `Object?`, convert based on runtime type.
455+
return util.jsifyRawTarget;
456+
}

0 commit comments

Comments
 (0)