blob: 7846a0c0c755c3bdd1a954bb7d5318255b378207 [file] [log] [blame] [edit]
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
// 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 'package:_js_interop_checks/src/transformations/js_util_optimizer.dart'
show ExtensionIndex;
import 'package:kernel/ast.dart';
import 'package:kernel/core_types.dart';
import 'package:kernel/type_environment.dart';
enum AnnotationType { import, export, weakExport }
/// A utility wrapper for [CoreTypes].
class CoreTypesUtil {
final ExtensionIndex extensionIndex;
final CoreTypes coreTypes;
final Procedure allowInteropTarget;
final Procedure dartifyRawTarget;
final Procedure functionToJSTarget;
final Procedure functionToJSCaptureThisTarget;
final Procedure greaterThanOrEqualToTarget;
final Procedure inlineJSTarget;
final Procedure isDartFunctionWrappedTarget;
final Procedure jsifyRawTarget;
final Procedure jsObjectFromDartObjectTarget;
final Class jsValueClass;
final Procedure jsValueBoxTarget;
final Procedure jsValueUnboxTarget;
final Procedure numToIntTarget;
final Class wasmExternRefClass;
final Class wasmArrayClass;
final Class wasmArrayRefClass;
final Procedure wrapDartFunctionTarget;
final Procedure exportWasmFunctionTarget;
final Member wasmExternRefNullRef;
final Class wasmI32Class;
final Procedure wasmI32ToIntSigned;
final Procedure isDartNullTarget;
final Procedure throwArgumentNullErrorTarget;
// Dart value to JS converters.
final Procedure toJSBoolean;
final Procedure jsifyInt;
final Procedure toJSNumber;
final Procedure jsifyNum;
final Procedure jsifyJSValue;
final Procedure jsifyString;
final Procedure jsifyJSInt8ArrayImpl;
final Procedure jsifyJSUint8ArrayImpl;
final Procedure jsifyJSUint8ClampedArrayImpl;
final Procedure jsifyJSInt16ArrayImpl;
final Procedure jsifyJSUint16ArrayImpl;
final Procedure jsifyJSInt32ArrayImpl;
final Procedure jsifyJSUint32ArrayImpl;
final Procedure jsifyJSFloat32ArrayImpl;
final Procedure jsifyJSFloat64ArrayImpl;
final Procedure jsInt8ArrayFromDartInt8List;
final Procedure jsUint8ArrayFromDartUint8List;
final Procedure jsUint8ClampedArrayFromDartUint8ClampedList;
final Procedure jsInt16ArrayFromDartInt16List;
final Procedure jsUint16ArrayFromDartUint16List;
final Procedure jsInt32ArrayFromDartInt32List;
final Procedure jsUint32ArrayFromDartUint32List;
final Procedure jsFloat32ArrayFromDartFloat32List;
final Procedure jsFloat64ArrayFromDartFloat64List;
final Procedure jsifyJSDataViewImpl; // JS ByteData
final Procedure jsifyByteData; // Wasm ByteData
final Procedure jsifyRawList;
final Procedure jsifyJSArrayBufferImpl; // JS ByteBuffer
final Procedure jsArrayBufferFromDartByteBuffer; // Wasm ByteBuffer
final Procedure jsifyFunction;
// Classes used in type tests for the converters.
final Class jsInt8ArrayImplClass;
final Class jsUint8ArrayImplClass;
final Class jsUint8ClampedArrayImplClass;
final Class jsInt16ArrayImplClass;
final Class jsUint16ArrayImplClass;
final Class jsInt32ArrayImplClass;
final Class jsUint32ArrayImplClass;
final Class jsFloat32ArrayImplClass;
final Class jsFloat64ArrayImplClass;
final Class int8ListClass;
final Class uint8ListClass;
final Class uint8ClampedListClass;
final Class int16ListClass;
final Class uint16ListClass;
final Class int32ListClass;
final Class uint32ListClass;
final Class float32ListClass;
final Class float64ListClass;
final Class jsDataViewImplClass;
final Class byteDataClass;
final Class jsArrayBufferImplClass;
final Class byteBufferClass;
// NB. We rely on iteration ordering being insertion order to handle subtypes
// before supertypes to convert as `int` and `double` before `num`.
late final Map<Class, Procedure> _jsifyMap = {
coreTypes.boolClass: toJSBoolean,
coreTypes.intClass: jsifyInt,
coreTypes.doubleClass: toJSNumber,
coreTypes.numClass: jsifyNum,
jsValueClass: jsifyJSValue,
coreTypes.stringClass: jsifyString,
jsInt8ArrayImplClass: jsifyJSInt8ArrayImpl,
jsUint8ArrayImplClass: jsifyJSUint8ArrayImpl,
jsUint8ClampedArrayImplClass: jsifyJSUint8ClampedArrayImpl,
jsInt16ArrayImplClass: jsifyJSInt16ArrayImpl,
jsUint16ArrayImplClass: jsifyJSUint16ArrayImpl,
jsInt32ArrayImplClass: jsifyJSInt32ArrayImpl,
jsUint32ArrayImplClass: jsifyJSUint32ArrayImpl,
jsFloat32ArrayImplClass: jsifyJSFloat32ArrayImpl,
jsFloat64ArrayImplClass: jsifyJSFloat64ArrayImpl,
int8ListClass: jsInt8ArrayFromDartInt8List,
uint8ListClass: jsUint8ArrayFromDartUint8List,
uint8ClampedListClass: jsUint8ClampedArrayFromDartUint8ClampedList,
int16ListClass: jsInt16ArrayFromDartInt16List,
uint16ListClass: jsUint16ArrayFromDartUint16List,
int32ListClass: jsInt32ArrayFromDartInt32List,
uint32ListClass: jsUint32ArrayFromDartUint32List,
float32ListClass: jsFloat32ArrayFromDartFloat32List,
float64ListClass: jsFloat64ArrayFromDartFloat64List,
jsDataViewImplClass: jsifyJSDataViewImpl,
byteDataClass: jsifyByteData,
coreTypes.listClass: jsifyRawList,
jsArrayBufferImplClass: jsifyJSArrayBufferImpl,
byteBufferClass: jsArrayBufferFromDartByteBuffer,
coreTypes.functionClass: jsifyFunction,
};
late final Map<Class, Procedure> _dartifyMap = {
coreTypes.boolClass:
coreTypes.index.getTopLevelProcedure('dart:_js_helper', 'toDartBool'),
coreTypes.intClass:
coreTypes.index.getTopLevelProcedure('dart:_js_helper', 'dartifyInt'),
coreTypes.doubleClass:
coreTypes.index.getTopLevelProcedure('dart:_js_helper', 'toDartNumber'),
coreTypes.numClass:
coreTypes.index.getTopLevelProcedure('dart:_js_helper', 'toDartNumber'),
coreTypes.stringClass:
coreTypes.index.getProcedure('dart:_string', 'JSStringImpl', 'fromRef'),
coreTypes.listClass:
coreTypes.index.getTopLevelProcedure('dart:_js_helper', 'toDartList'),
coreTypes.index.getClass('dart:typed_data', 'Int8List'): coreTypes.index
.getProcedure('dart:_js_types', 'JSInt8ArrayImpl', 'fromRef'),
coreTypes.index.getClass('dart:typed_data', 'Uint8List'): coreTypes.index
.getProcedure('dart:_js_types', 'JSUint8ArrayImpl', 'fromRef'),
coreTypes.index.getClass('dart:typed_data', 'Uint8ClampedList'): coreTypes
.index
.getProcedure('dart:_js_types', 'JSUint8ClampedArrayImpl', 'fromRef'),
coreTypes.index.getClass('dart:typed_data', 'Int16List'): coreTypes.index
.getProcedure('dart:_js_types', 'JSInt16ArrayImpl', 'fromRef'),
coreTypes.index.getClass('dart:typed_data', 'Uint16List'): coreTypes.index
.getProcedure('dart:_js_types', 'JSUint16ArrayImpl', 'fromRef'),
coreTypes.index.getClass('dart:typed_data', 'Int32List'): coreTypes.index
.getProcedure('dart:_js_types', 'JSInt32ArrayImpl', 'fromRef'),
coreTypes.index.getClass('dart:typed_data', 'Uint32List'): coreTypes.index
.getProcedure('dart:_js_types', 'JSUint32ArrayImpl', 'fromRef'),
coreTypes.index.getClass('dart:typed_data', 'Float32List'): coreTypes.index
.getProcedure('dart:_js_types', 'JSFloat32ArrayImpl', 'fromRef'),
coreTypes.index.getClass('dart:typed_data', 'Float64List'): coreTypes.index
.getProcedure('dart:_js_types', 'JSFloat64ArrayImpl', 'fromRef'),
coreTypes.index.getClass('dart:typed_data', 'ByteBuffer'): coreTypes.index
.getProcedure('dart:_js_types', 'JSArrayBufferImpl', 'fromRef'),
coreTypes.index.getClass('dart:typed_data', 'ByteData'): coreTypes.index
.getProcedure('dart:_js_types', 'JSDataViewImpl', 'fromRef'),
};
CoreTypesUtil(this.coreTypes, this.extensionIndex)
: allowInteropTarget = coreTypes.index
.getTopLevelProcedure('dart:js_util', 'allowInterop'),
dartifyRawTarget = coreTypes.index
.getTopLevelProcedure('dart:_js_helper', 'dartifyRaw'),
functionToJSTarget = coreTypes.index.getTopLevelProcedure(
'dart:js_interop', 'FunctionToJSExportedDartFunction|get#toJS'),
functionToJSCaptureThisTarget = coreTypes.index.getTopLevelProcedure(
'dart:js_interop',
'FunctionToJSExportedDartFunction|get#toJSCaptureThis'),
greaterThanOrEqualToTarget =
coreTypes.index.getProcedure('dart:core', 'num', '>='),
inlineJSTarget =
coreTypes.index.getTopLevelProcedure('dart:_js_helper', 'JS'),
isDartFunctionWrappedTarget = coreTypes.index
.getTopLevelProcedure('dart:_js_helper', '_isDartFunctionWrapped'),
numToIntTarget =
coreTypes.index.getProcedure('dart:core', 'num', 'toInt'),
jsifyRawTarget =
coreTypes.index.getTopLevelProcedure('dart:_js_helper', 'jsifyRaw'),
jsObjectFromDartObjectTarget = coreTypes.index
.getTopLevelProcedure('dart:_js_helper', 'jsObjectFromDartObject'),
jsValueBoxTarget =
coreTypes.index.getProcedure('dart:_js_helper', 'JSValue', 'box'),
jsValueClass = coreTypes.index.getClass('dart:_js_helper', 'JSValue'),
jsValueUnboxTarget =
coreTypes.index.getProcedure('dart:_js_helper', 'JSValue', 'unbox'),
wasmExternRefClass =
coreTypes.index.getClass('dart:_wasm', 'WasmExternRef'),
wasmExternRefNullRef = coreTypes.index
.getMember('dart:_wasm', 'WasmExternRef', 'get:nullRef'),
wasmArrayClass = coreTypes.index.getClass('dart:_wasm', 'WasmArray'),
wasmArrayRefClass =
coreTypes.index.getClass('dart:_wasm', 'WasmArrayRef'),
wasmI32Class = coreTypes.index.getClass('dart:_wasm', 'WasmI32'),
wasmI32ToIntSigned = coreTypes.index
.getProcedure('dart:_wasm', 'WasmI32', 'toIntSigned'),
wrapDartFunctionTarget = coreTypes.index
.getTopLevelProcedure('dart:_js_helper', '_wrapDartFunction'),
exportWasmFunctionTarget = coreTypes.index
.getTopLevelProcedure('dart:_internal', 'exportWasmFunction'),
toJSBoolean = coreTypes.index
.getTopLevelProcedure('dart:_js_helper', 'toJSBoolean'),
jsifyInt =
coreTypes.index.getTopLevelProcedure('dart:_js_helper', 'jsifyInt'),
toJSNumber = coreTypes.index
.getTopLevelProcedure('dart:_js_helper', 'toJSNumber'),
jsifyNum =
coreTypes.index.getTopLevelProcedure('dart:_js_helper', 'jsifyNum'),
jsifyJSValue = coreTypes.index
.getTopLevelProcedure('dart:_js_helper', 'jsifyJSValue'),
jsifyString = coreTypes.index
.getTopLevelProcedure('dart:_js_helper', 'jsifyString'),
jsifyJSInt8ArrayImpl = coreTypes.index
.getTopLevelProcedure('dart:_js_helper', 'jsifyJSInt8ArrayImpl'),
jsifyJSUint8ArrayImpl = coreTypes.index
.getTopLevelProcedure('dart:_js_helper', 'jsifyJSUint8ArrayImpl'),
jsifyJSUint8ClampedArrayImpl = coreTypes.index.getTopLevelProcedure(
'dart:_js_helper', 'jsifyJSUint8ClampedArrayImpl'),
jsifyJSInt16ArrayImpl = coreTypes.index
.getTopLevelProcedure('dart:_js_helper', 'jsifyJSInt16ArrayImpl'),
jsifyJSUint16ArrayImpl = coreTypes.index
.getTopLevelProcedure('dart:_js_helper', 'jsifyJSUint16ArrayImpl'),
jsifyJSInt32ArrayImpl = coreTypes.index
.getTopLevelProcedure('dart:_js_helper', 'jsifyJSInt32ArrayImpl'),
jsifyJSUint32ArrayImpl = coreTypes.index
.getTopLevelProcedure('dart:_js_helper', 'jsifyJSUint32ArrayImpl'),
jsifyJSFloat32ArrayImpl = coreTypes.index
.getTopLevelProcedure('dart:_js_helper', 'jsifyJSFloat32ArrayImpl'),
jsifyJSFloat64ArrayImpl = coreTypes.index
.getTopLevelProcedure('dart:_js_helper', 'jsifyJSFloat64ArrayImpl'),
jsInt8ArrayFromDartInt8List = coreTypes.index.getTopLevelProcedure(
'dart:_js_helper', 'jsInt8ArrayFromDartInt8List'),
jsUint8ArrayFromDartUint8List = coreTypes.index.getTopLevelProcedure(
'dart:_js_helper', 'jsUint8ArrayFromDartUint8List'),
jsUint8ClampedArrayFromDartUint8ClampedList = coreTypes.index
.getTopLevelProcedure('dart:_js_helper',
'jsUint8ClampedArrayFromDartUint8ClampedList'),
jsInt16ArrayFromDartInt16List = coreTypes.index.getTopLevelProcedure(
'dart:_js_helper', 'jsInt16ArrayFromDartInt16List'),
jsUint16ArrayFromDartUint16List = coreTypes.index.getTopLevelProcedure(
'dart:_js_helper', 'jsUint16ArrayFromDartUint16List'),
jsInt32ArrayFromDartInt32List = coreTypes.index.getTopLevelProcedure(
'dart:_js_helper', 'jsInt32ArrayFromDartInt32List'),
jsUint32ArrayFromDartUint32List = coreTypes.index.getTopLevelProcedure(
'dart:_js_helper', 'jsUint32ArrayFromDartUint32List'),
jsFloat32ArrayFromDartFloat32List = coreTypes.index
.getTopLevelProcedure(
'dart:_js_helper', 'jsFloat32ArrayFromDartFloat32List'),
jsFloat64ArrayFromDartFloat64List = coreTypes.index
.getTopLevelProcedure(
'dart:_js_helper', 'jsFloat64ArrayFromDartFloat64List'),
jsifyJSDataViewImpl = coreTypes.index
.getTopLevelProcedure('dart:_js_helper', 'jsifyJSDataViewImpl'),
jsifyByteData = coreTypes.index
.getTopLevelProcedure('dart:_js_helper', 'jsifyByteData'),
jsifyRawList = coreTypes.index
.getTopLevelProcedure('dart:_js_helper', '_jsifyRawList'),
jsifyJSArrayBufferImpl = coreTypes.index
.getTopLevelProcedure('dart:_js_helper', 'jsifyJSArrayBufferImpl'),
jsArrayBufferFromDartByteBuffer = coreTypes.index.getTopLevelProcedure(
'dart:_js_helper', 'jsArrayBufferFromDartByteBuffer'),
jsifyFunction = coreTypes.index
.getTopLevelProcedure('dart:_js_helper', 'jsifyFunction'),
jsInt8ArrayImplClass =
coreTypes.index.getClass('dart:_js_types', 'JSInt8ArrayImpl'),
jsUint8ArrayImplClass =
coreTypes.index.getClass('dart:_js_types', 'JSUint8ArrayImpl'),
jsUint8ClampedArrayImplClass = coreTypes.index
.getClass('dart:_js_types', 'JSUint8ClampedArrayImpl'),
jsInt16ArrayImplClass =
coreTypes.index.getClass('dart:_js_types', 'JSInt16ArrayImpl'),
jsUint16ArrayImplClass =
coreTypes.index.getClass('dart:_js_types', 'JSUint16ArrayImpl'),
jsInt32ArrayImplClass =
coreTypes.index.getClass('dart:_js_types', 'JSInt32ArrayImpl'),
jsUint32ArrayImplClass =
coreTypes.index.getClass('dart:_js_types', 'JSUint32ArrayImpl'),
jsFloat32ArrayImplClass =
coreTypes.index.getClass('dart:_js_types', 'JSFloat32ArrayImpl'),
jsFloat64ArrayImplClass =
coreTypes.index.getClass('dart:_js_types', 'JSFloat64ArrayImpl'),
int8ListClass = coreTypes.index.getClass('dart:typed_data', 'Int8List'),
uint8ListClass =
coreTypes.index.getClass('dart:typed_data', 'Uint8List'),
uint8ClampedListClass =
coreTypes.index.getClass('dart:typed_data', 'Uint8ClampedList'),
int16ListClass =
coreTypes.index.getClass('dart:typed_data', 'Int16List'),
uint16ListClass =
coreTypes.index.getClass('dart:typed_data', 'Uint16List'),
int32ListClass =
coreTypes.index.getClass('dart:typed_data', 'Int32List'),
uint32ListClass =
coreTypes.index.getClass('dart:typed_data', 'Uint32List'),
float32ListClass =
coreTypes.index.getClass('dart:typed_data', 'Float32List'),
float64ListClass =
coreTypes.index.getClass('dart:typed_data', 'Float64List'),
jsDataViewImplClass =
coreTypes.index.getClass('dart:_js_types', 'JSDataViewImpl'),
byteDataClass = coreTypes.index.getClass('dart:typed_data', 'ByteData'),
byteBufferClass =
coreTypes.index.getClass('dart:typed_data', 'ByteBuffer'),
jsArrayBufferImplClass =
coreTypes.index.getClass('dart:_js_types', 'JSArrayBufferImpl'),
isDartNullTarget = coreTypes.index
.getTopLevelProcedure('dart:_js_helper', 'isDartNull'),
throwArgumentNullErrorTarget = coreTypes.index.getTopLevelProcedure(
'dart:_error_utils', '_throwArgumentNullError');
DartType get nonNullableObjectType =>
coreTypes.objectRawType(Nullability.nonNullable);
DartType get nonNullableWasmExternRefType =>
wasmExternRefClass.getThisType(coreTypes, Nullability.nonNullable);
DartType get nullableJSValueType =>
InterfaceType(jsValueClass, Nullability.nullable);
DartType get nullableWasmExternRefType =>
wasmExternRefClass.getThisType(coreTypes, Nullability.nullable);
/// Whether [type] erases to a `JSValue` or `JSValue?`.
bool isJSValueType(DartType type) =>
extensionIndex.isStaticInteropType(type) ||
extensionIndex.isExternalDartReferenceType(type);
void annotateProcedure(
Procedure procedure, String pragmaOptionString, AnnotationType type) {
String pragmaNameType = switch (type) {
AnnotationType.import => 'import',
AnnotationType.export => 'export',
AnnotationType.weakExport => 'weak-export',
};
procedure.addAnnotation(ConstantExpression(
InstanceConstant(coreTypes.pragmaClass.reference, [], {
coreTypes.pragmaName.fieldReference:
StringConstant('wasm:$pragmaNameType'),
coreTypes.pragmaOptions.fieldReference: StringConstant(pragmaOptionString)
})));
}
Expression variableCheckConstant(
VariableDeclaration variable, Constant constant) =>
StaticInvocation(coreTypes.identicalProcedure,
Arguments([VariableGet(variable), ConstantExpression(constant)]));
Expression variableGreaterThanOrEqualToConstant(
VariableDeclaration variable, Constant constant) =>
InstanceInvocation(
InstanceAccessKind.Instance,
VariableGet(variable),
greaterThanOrEqualToTarget.name,
Arguments([ConstantExpression(constant)]),
interfaceTarget: greaterThanOrEqualToTarget,
functionType: greaterThanOrEqualToTarget.getterType as FunctionType,
);
/// Cast the [invocation] if needed to conform to the expected [expectedType].
Expression castInvocationForReturn(
Expression invocation, DartType expectedType) {
Expression expression;
if (expectedType is VoidType) {
// Technically a `void` return value can still be used, by casting the
// return type to `dynamic` or `Object?`. However this case should be
// extremely rare, and `dartifyRaw` overhead for return values that should
// never be used in practice is too much, so we avoid `dartifyRaw` on
// `void` returns and always return `null`.
return BlockExpression(
Block([ExpressionStatement(invocation)]), NullLiteral());
}
if (isJSValueType(expectedType)) {
// TODO(joshualitt): Expose boxed `JSNull` and `JSUndefined` to Dart
// code after migrating existing users of js interop on Dart2Wasm.
// expression = _createJSValue(invocation);
// Casts are expensive, so we stick to a null-assertion if needed. If
// the nullability can't be determined, cast.
expression = invokeOneArg(jsValueBoxTarget, invocation);
final nullability = expectedType.extensionTypeErasure.nullability;
if (nullability == Nullability.nonNullable) {
expression = NullCheck(expression);
} else if (nullability == Nullability.undetermined) {
expression = AsExpression(expression, expectedType);
}
} else {
final expectedTypeExtensionTypeErasure =
expectedType.extensionTypeErasure;
final expectNullable =
expectedTypeExtensionTypeErasure.isPotentiallyNullable;
final conversionProcedure =
_dartConversionProcedure(expectedTypeExtensionTypeErasure);
final invocationValueVar = VariableDeclaration('#jsInvocation',
initializer: invocation,
type: nullableWasmExternRefType,
isSynthesized: true);
expression = Let(
invocationValueVar,
ConditionalExpression(
StaticInvocation(
isDartNullTarget, Arguments([VariableGet(invocationValueVar)])),
expectNullable
? NullLiteral()
: StaticInvocation(throwArgumentNullErrorTarget, Arguments([])),
invokeOneArg(conversionProcedure, VariableGet(invocationValueVar)),
expectedType,
),
);
}
return expression;
}
// Handles any necessary type conversions. Today this is just for handling the
// case where a user wants us to coerce a JS number to an int instead of a
// double. This is okay as long as the value is an integer value.
Expression convertAndCast(DartType staticType, Expression expression) {
final erasedType = staticType.extensionTypeErasure;
if (erasedType == coreTypes.intNullableRawType ||
erasedType == coreTypes.intNonNullableRawType) {
// let v = [expression] as double? in
// if (v == null) {
// return null;
// } else {
// let v2 = v.toInt() in
// if (v == v2) {
// return v2;
// } else {
// throw;
// }
VariableDeclaration v = VariableDeclaration('#vardouble',
initializer:
AsExpression(expression, coreTypes.doubleNullableRawType),
type: coreTypes.doubleNullableRawType,
isSynthesized: true);
VariableDeclaration v2 = VariableDeclaration('#varint',
initializer: invokeMethod(v, numToIntTarget),
type: coreTypes.intNonNullableRawType,
isSynthesized: true);
expression = Let(
v,
ConditionalExpression(
variableCheckConstant(v, NullConstant()),
ConstantExpression(NullConstant()),
Let(
v2,
ConditionalExpression(
invokeMethod(v, coreTypes.objectEquals,
Arguments([VariableGet(v2)])),
VariableGet(v2),
Throw(StringLiteral(
'Expected integer value, but was not integer.')),
coreTypes.intNonNullableRawType)),
coreTypes.intNullableRawType));
}
return AsExpression(expression, staticType);
}
/// Return the function to convert a value with type [valueType] to Dart
/// interop type [expectedType].
///
/// [expectedType] can be any interop type, but for now this only handles the
/// interop types generated by [_Specializer._getRawInteropProcedure].
///
/// `null` return value means no conversion is needed, the value can be passed
/// to the interop function directly.
///
/// The argument passed to the returned conversion function needs to be
/// non-nullable. This function does not check the nullability of [valueType]
/// and assume that the argument passed to the conversion function won't be
/// `null`.
Procedure? _jsConversionProcedure(
DartType valueType, DartType expectedType, TypeEnvironment typeEnv) {
if (expectedType == coreTypes.doubleNonNullableRawType) {
assert(valueType is InterfaceType &&
valueType.classNode == coreTypes.doubleClass);
return null;
}
assert(expectedType == nullableWasmExternRefType,
'Unexpected expected type: $expectedType');
for (final entry in _jsifyMap.entries) {
if (typeEnv.isSubtypeOf(
valueType, InterfaceType(entry.key, Nullability.nonNullable))) {
return entry.value;
}
}
// `dynamic` or `Object?`, convert based on runtime type.
return jsifyRawTarget;
}
/// Return the function to convert a value returned by an interop procedure
/// generated by [_Specializer.getRawInteropProcedure] to the expected Dart
/// type.
///
/// The value passed to the returned conversion function should be an
/// `externref` and should be tested for `null` and `undefined`. The returned
/// procedures do not handle `null`s and `undefined`s.
Procedure _dartConversionProcedure(DartType expectedType) {
Procedure? conversionProcedure;
if (expectedType is InterfaceType) {
conversionProcedure = _dartifyMap[expectedType.classNode];
}
return conversionProcedure ?? dartifyRawTarget;
}
}
StaticInvocation invokeOneArg(Procedure target, Expression arg) =>
StaticInvocation(target, Arguments([arg]));
InstanceInvocation invokeMethod(VariableDeclaration receiver, Procedure target,
[Arguments? arguments]) =>
InstanceInvocation(InstanceAccessKind.Instance, VariableGet(receiver),
target.name, arguments ?? Arguments([]),
interfaceTarget: target,
functionType:
target.function.computeFunctionType(Nullability.nonNullable));
bool parametersNeedParens(List<String> parameters) =>
parameters.isEmpty || parameters.length > 1;
Expression jsifyValue(VariableDeclaration variable, DartType expectedType,
CoreTypesUtil coreTypes, TypeEnvironment typeEnv) {
final Procedure? conversionProcedure;
if (coreTypes.extensionIndex.isStaticInteropType(variable.type) ||
coreTypes.extensionIndex.isExternalDartReferenceType(variable.type)) {
conversionProcedure = coreTypes.jsValueUnboxTarget;
} else {
conversionProcedure =
coreTypes._jsConversionProcedure(variable.type, expectedType, typeEnv);
}
final conversion = conversionProcedure == null
? VariableGet(variable)
: StaticInvocation(
conversionProcedure, Arguments([VariableGet(variable)]));
if (variable.type.isPotentiallyNullable) {
return ConditionalExpression(
EqualsNull(VariableGet(variable)),
StaticGet(coreTypes.wasmExternRefNullRef),
conversion,
InterfaceType(coreTypes.wasmExternRefClass, Nullability.nullable));
} else {
return conversion;
}
}