[dart2wasm] Use [WasmArray]s for positional/named arguments in dynamic calls

This avoids allocating lists in dynamic calls for positional/named
arguments.

=> Doing so allows removing more usages of [List] in the RTT.
=> Also results in up to 50% faster dynamic calls.
=> Probably also results in smaller code.

The list allocation is now pushed to the slow path where we
create [Invocation] object that is then used for NoSuchMethod
handling.

Change-Id: If578fb044a6cf7f31bd409c177b361181ba68a01
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/344821
Reviewed-by: Ömer Ağacan <omersa@google.com>
Commit-Queue: Martin Kustermann <kustermann@google.com>
This commit is contained in:
Martin Kustermann 2024-01-05 15:00:19 +00:00 committed by Commit Queue
parent 5a4b438ddb
commit 3343b7a841
7 changed files with 151 additions and 156 deletions

View file

@ -1908,12 +1908,11 @@ class CodeGenerator extends ExpressionVisitor1<w.ValueType, w.ValueType>
b.local_set(typeArgsLocal);
// Evaluate positional arguments
makeList(DynamicType(), positionalArguments.length,
final positionalArgsLocal = function.addLocal(makeArray(
translator.nullableObjectArrayType, positionalArguments.length,
(elementType, elementIdx) {
wrap(positionalArguments[elementIdx], elementType);
}, isGrowable: false);
final positionalArgsLocal = function.addLocal(
translator.classInfo[translator.fixedLengthListClass]!.nonNullableType);
}));
b.local_set(positionalArgsLocal);
// Evaluate named arguments. The arguments need to be evaluated in the
@ -1929,9 +1928,10 @@ class CodeGenerator extends ExpressionVisitor1<w.ValueType, w.ValueType>
}
namedArgumentLocals.sort((e1, e2) => e1.key.compareTo(e2.key));
// Create named argument list
makeList(DynamicType(), namedArguments.length * 2,
(elementType, elementIdx) {
// Create named argument array
final namedArgsLocal = function.addLocal(
makeArray(translator.nullableObjectArrayType, namedArguments.length * 2,
(elementType, elementIdx) {
if (elementIdx % 2 == 0) {
final name = namedArgumentLocals[elementIdx ~/ 2].key;
final w.ValueType symbolValueType =
@ -1942,9 +1942,7 @@ class CodeGenerator extends ExpressionVisitor1<w.ValueType, w.ValueType>
final local = namedArgumentLocals[elementIdx ~/ 2].value;
b.local_get(local);
}
}, isGrowable: false);
final namedArgsLocal = function.addLocal(
translator.classInfo[translator.fixedLengthListClass]!.nonNullableType);
}));
b.local_set(namedArgsLocal);
final nullBlock = b.block([], [translator.topInfo.nonNullableType]);
@ -2973,14 +2971,18 @@ class CodeGenerator extends ExpressionVisitor1<w.ValueType, w.ValueType>
w.ValueType makeArrayFromExpressions(
List<Expression> expressions, InterfaceType elementType) {
return translator.makeArray(
function,
translator.arrayTypeForDartType(elementType),
expressions.length, (w.ValueType elementType, int i) {
wrap(expressions[i], elementType);
return makeArray(
translator.arrayTypeForDartType(elementType), expressions.length,
(w.ValueType type, int i) {
wrap(expressions[i], type);
});
}
w.ValueType makeArray(w.ArrayType arrayType, int length,
void Function(w.ValueType, int) generateItem) {
return translator.makeArray(function, arrayType, length, generateItem);
}
@override
w.ValueType visitMapLiteral(MapLiteral node, w.ValueType expectedType) {
types.makeType(this, node.keyType);
@ -3456,7 +3458,8 @@ class CodeGenerator extends ExpressionVisitor1<w.ValueType, w.ValueType>
param.name!,
() {
b.local_get(positionalArgsLocal);
translator.indexList(b, (b) => b.i32_const(positionalParamIdx));
b.i32_const(positionalParamIdx);
b.array_get(translator.nullableObjectArrayType);
},
() {
types.makeType(this, param.type);
@ -3490,8 +3493,8 @@ class CodeGenerator extends ExpressionVisitor1<w.ValueType, w.ValueType>
param.name!,
() {
b.local_get(namedArgsLocal);
translator.indexList(b,
(b) => b.i32_const(mapNamedParameterToArrayIndex(param.name!)));
b.i32_const(mapNamedParameterToArrayIndex(param.name!));
b.array_get(translator.nullableObjectArrayType);
},
() {
types.makeType(this, param.type);
@ -3519,7 +3522,8 @@ class CodeGenerator extends ExpressionVisitor1<w.ValueType, w.ValueType>
void pushArgument(w.Local listLocal, int listIdx, int wasmInputIdx) {
b.local_get(listLocal);
translator.indexList(b, (b) => b.i32_const(listIdx));
b.i32_const(listIdx);
b.array_get(translator.nullableObjectArrayType);
translator.convertType(function, translator.topInfo.nullableType,
memberWasmInputs[wasmInputIdx]);
}

View file

@ -201,8 +201,8 @@ class Forwarder {
final receiverLocal = function.locals[0]; // ref #Top
final typeArgsLocal = function.locals[1]; // ref _ListBase
final positionalArgsLocal = function.locals[2]; // ref _ListBase
final namedArgsLocal = function.locals[3]; // ref _ListBase
final positionalArgsLocal = function.locals[2]; // ref WasmArray
final namedArgsLocal = function.locals[3]; // ref WasmArray
final classIdLocal = function.addLocal(w.NumType.i32);
@ -302,7 +302,7 @@ class Forwarder {
// positionalArgs.length >= nRequired &&
// positionalArgs.length <= nTotal
b.local_get(positionalArgsLocal);
translator.getListLength(b);
b.array_len();
b.local_tee(numArgsLocal);
b.i32_const(nRequired);
b.i32_ge_u();
@ -316,9 +316,10 @@ class Forwarder {
// Add default values of optional positional parameters if needed
w.Local? adjustedPositionalArgsLocal;
if (nRequired != nTotal) {
adjustedPositionalArgsLocal = function.addLocal(translator
.classInfo[translator.growableListClass]!.nonNullableType);
_makeEmptyGrowableList(translator, function, nTotal);
adjustedPositionalArgsLocal =
function.addLocal(translator.nullableObjectArrayTypeRef);
b.i32_const(nTotal);
b.array_new_default(translator.nullableObjectArrayType);
b.local_set(adjustedPositionalArgsLocal);
// Copy passed arguments
@ -332,11 +333,11 @@ class Forwarder {
b.i32_lt_u();
b.if_();
b.local_get(adjustedPositionalArgsLocal);
b.local_get(argIdxLocal);
b.local_get(positionalArgsLocal);
translator.indexList(b, (b) => b.local_get(argIdxLocal));
b.call(translator.functions
.getFunction(translator.growableListAdd.reference));
b.drop();
b.local_get(argIdxLocal);
b.array_get(translator.nullableObjectArrayType);
b.array_set(translator.nullableObjectArrayType);
b.local_get(argIdxLocal);
b.i32_const(1);
b.i32_add();
@ -353,17 +354,14 @@ class Forwarder {
b.i32_const(optionalParamIdx);
b.i32_le_u();
b.if_();
b.local_get(adjustedPositionalArgsLocal);
final param = targetMemberParamInfo.positional[optionalParamIdx]!;
b.local_get(adjustedPositionalArgsLocal);
b.i32_const(optionalParamIdx);
translator.constants.instantiateConstant(
function, b, param, translator.topInfo.nullableType);
b.call(translator.functions
.getFunction(translator.growableListAdd.reference));
b.drop();
b.array_set(translator.nullableObjectArrayType);
b.end();
}
}
@ -375,15 +373,15 @@ class Forwarder {
if (targetMemberParamInfo.named.isEmpty) {
// namedArgs.length == 0
b.local_get(namedArgsLocal);
translator.getListLength(b);
b.array_len();
b.i32_eqz();
b.i32_eqz();
b.br_if(noSuchMethodBlock);
} else {
adjustedNamedArgsLocal = function.addLocal(translator
.classInfo[translator.growableListClass]!.nonNullableType);
_makeEmptyGrowableList(
translator, function, targetMemberParamInfo.named.length);
adjustedNamedArgsLocal =
function.addLocal(translator.nullableObjectArrayTypeRef);
b.i32_const(targetMemberParamInfo.named.length);
b.array_new_default(translator.nullableObjectArrayType);
b.local_set(adjustedNamedArgsLocal);
final namedParameterIdxLocal = function.addLocal(
@ -391,7 +389,7 @@ class Forwarder {
final remainingNamedArgsLocal = numArgsLocal;
b.local_get(namedArgsLocal);
translator.getListLength(b);
b.array_len();
b.i32_const(1);
b.i32_shr_u();
b.local_set(remainingNamedArgsLocal);
@ -409,7 +407,10 @@ class Forwarder {
return null;
}
for (final name in targetMemberParamInfo.names) {
for (int nameIdx = 0;
nameIdx < targetMemberParamInfo.names.length;
++nameIdx) {
final String name = targetMemberParamInfo.names[nameIdx];
final Constant? paramInfoDefaultValue =
targetMemberParamInfo.named[name]!;
final Expression? functionNodeDefaultValue =
@ -441,23 +442,27 @@ class Forwarder {
paramInfoDefaultValue == null) {
// Required parameter missing
b.br_if(noSuchMethodBlock);
// Copy provided named parameter.
b.local_get(adjustedNamedArgsLocal);
b.i32_const(nameIdx);
b.local_get(namedArgsLocal);
translator.indexList(b, (b) {
b.local_get(namedParameterIdxLocal);
translator.convertType(
function, namedParameterIdxLocal.type, w.NumType.i64);
b.i32_wrap_i64();
});
b.call(translator.functions
.getFunction(translator.growableListAdd.reference));
b.drop();
b.local_get(namedParameterIdxLocal);
translator.convertType(
function, namedParameterIdxLocal.type, w.NumType.i64);
b.i32_wrap_i64();
b.array_get(translator.nullableObjectArrayType);
b.array_set(translator.nullableObjectArrayType);
} else {
// Optional, either has a default in the member or not used by
// the member
b.if_();
b.local_get(adjustedNamedArgsLocal);
b.i32_const(nameIdx);
if (functionNodeDefaultValue != null) {
// Used by the member, has a default value
@ -475,24 +480,19 @@ class Forwarder {
translator.topInfo.nullableType,
);
}
b.call(translator.functions
.getFunction(translator.growableListAdd.reference));
b.drop();
b.array_set(translator.nullableObjectArrayType);
b.else_();
b.local_get(adjustedNamedArgsLocal);
b.i32_const(nameIdx);
b.local_get(namedArgsLocal);
translator.indexList(b, (b) {
b.local_get(namedParameterIdxLocal);
translator.convertType(
function, namedParameterIdxLocal.type, w.NumType.i64);
b.i32_wrap_i64();
});
b.call(translator.functions
.getFunction(translator.growableListAdd.reference));
b.drop();
b.local_get(namedParameterIdxLocal);
translator.convertType(
function, namedParameterIdxLocal.type, w.NumType.i64);
b.i32_wrap_i64();
b.array_get(translator.nullableObjectArrayType);
b.array_set(translator.nullableObjectArrayType);
b.end();
}
@ -676,8 +676,8 @@ void generateDynamicFunctionCall(
final listArgumentType =
translator.classInfo[translator.listBaseClass]!.nonNullableType;
assert(typeArgsLocal.type == listArgumentType);
assert(posArgsLocal.type == listArgumentType);
assert(namedArgsLocal.type == listArgumentType);
assert(posArgsLocal.type == translator.nullableObjectArrayTypeRef);
assert(namedArgsLocal.type == translator.nullableObjectArrayTypeRef);
final b = function.body;
@ -744,9 +744,11 @@ void createInvocationObject(
b.local_get(typeArgsLocal);
b.local_get(positionalArgsLocal);
b.call(translator.functions
.getFunction(translator.positionalParametersToList.reference));
b.local_get(namedArgsLocal);
b.call(translator.functions
.getFunction(translator.namedParameterListToMap.reference));
.getFunction(translator.namedParametersToMap.reference));
b.call(translator.functions
.getFunction(translator.invocationGenericMethodFactory.reference));
}
@ -853,27 +855,6 @@ void generateNoSuchMethodCall(
b.call_indirect(noSuchMethodWasmFunctionType);
}
void _makeEmptyGrowableList(
Translator translator, w.FunctionBuilder function, int capacity) {
final b = function.body;
Class cls = translator.growableListClass;
ClassInfo info = translator.classInfo[cls]!;
translator.functions.allocateClass(info.classId);
w.ArrayType arrayType = translator.listArrayType;
b.i32_const(info.classId);
b.i32_const(initialIdentityHash);
translator.constants.instantiateConstant(
function,
b,
TypeLiteralConstant(DynamicType()),
translator.classInfo[translator.typeClass]!.nonNullableType);
b.i64_const(0); // _length
b.i32_const(capacity);
b.array_new_default(arrayType); // _data
b.struct_new(info.struct);
}
class ClassIdRange {
final int start;
final int end; // inclusive

View file

@ -1728,17 +1728,15 @@ class Intrinsifier {
b.local_set(typeArgsLocal);
// Create empty list for positional args if the argument is null
final posArgsLocal = function.addLocal(listArgumentType);
final posArgsLocal =
function.addLocal(translator.nullableObjectArrayTypeRef);
b.local_get(posArgsNullableLocal);
b.ref_is_null();
b.if_([], [listArgumentType]);
translator.constants.instantiateConstant(
function,
b,
ListConstant(
InterfaceType(translator.objectInfo.cls!, Nullability.nullable),
[]),
translator.objectInfo.nonNullableType);
b.if_([], [translator.nullableObjectArrayTypeRef]);
translator.makeArray(
function, translator.nullableObjectArrayType, 0, (_, __) {});
b.else_();
// List argument may be a custom list type, convert it to `_ListBase`
// with `_List.of`.
@ -1751,15 +1749,16 @@ class Intrinsifier {
b.local_get(posArgsNullableLocal);
b.ref_as_non_null();
codeGen.call(translator.listOf.reference);
translator.getListBaseArray(b);
b.end();
b.local_set(posArgsLocal);
// Convert named argument map to list, to be passed to shape and type
// checkers and the dynamic call entry.
final namedArgsListLocal = function.addLocal(listArgumentType);
final namedArgsListLocal =
function.addLocal(translator.nullableObjectArrayTypeRef);
b.local_get(namedArgsLocal);
codeGen.call(translator.namedParameterMapToList.reference);
b.ref_cast(listArgumentType); // ref Object -> ref _ListBase
codeGen.call(translator.namedParameterMapToArray.reference);
b.local_set(namedArgsListLocal);
final noSuchMethodBlock = b.block();

View file

@ -275,10 +275,12 @@ mixin KernelNodes {
// dart:core dynamic invocation helper procedures
late final Procedure getNamedParameterIndex =
index.getTopLevelProcedure("dart:core", "_getNamedParameterIndex");
late final Procedure namedParameterListToMap =
index.getTopLevelProcedure("dart:core", "_namedParameterListToMap");
late final Procedure namedParameterMapToList =
index.getTopLevelProcedure("dart:core", "_namedParameterMapToList");
late final Procedure positionalParametersToList =
index.getTopLevelProcedure("dart:core", "_positionalParametersToList");
late final Procedure namedParametersToMap =
index.getTopLevelProcedure("dart:core", "_namedParametersToMap");
late final Procedure namedParameterMapToArray =
index.getTopLevelProcedure("dart:core", "_namedParameterMapToArray");
late final Procedure listOf = index.getProcedure("dart:core", "_List", "of");
// dart:_wasm procedures

View file

@ -128,6 +128,10 @@ class Translator with KernelNodes {
.fields[FieldIndex.listArray]
.type as w.RefType)
.heapType as w.ArrayType;
late final w.ArrayType nullableObjectArrayType =
arrayTypeForDartType(coreTypes.objectRawType(Nullability.nullable));
late final w.RefType nullableObjectArrayTypeRef =
w.RefType.def(nullableObjectArrayType, nullable: false);
/// Dart types that have specialized Wasm representations.
late final Map<Class, w.StorageType> builtinTypes = {
@ -184,10 +188,10 @@ class Translator with KernelNodes {
classInfo[listBaseClass]!.nonNullableType,
// Positional arguments
classInfo[listBaseClass]!.nonNullableType,
nullableObjectArrayTypeRef,
// Named arguments, represented as array of symbol and object pairs
classInfo[listBaseClass]!.nonNullableType,
nullableObjectArrayTypeRef,
], [
topInfo.nullableType
]);
@ -202,10 +206,10 @@ class Translator with KernelNodes {
classInfo[listBaseClass]!.nonNullableType,
// Positional arguments
classInfo[listBaseClass]!.nonNullableType,
nullableObjectArrayTypeRef,
// Named arguments, represented as array of symbol and object pairs
classInfo[listBaseClass]!.nonNullableType,
nullableObjectArrayTypeRef,
], [
topInfo.nullableType
]);
@ -1029,16 +1033,12 @@ class Translator with KernelNodes {
return arrayTypeRef;
}
/// Indexes a Dart `List` on the stack.
/// Indexes a Dart `_ListBase` on the stack.
void indexList(
w.InstructionsBuilder b, void pushIndex(w.InstructionsBuilder b)) {
ClassInfo info = classInfo[listBaseClass]!;
w.ArrayType arrayType =
(info.struct.fields[FieldIndex.listArray].type as w.RefType).heapType
as w.ArrayType;
b.struct_get(info.struct, FieldIndex.listArray);
getListBaseArray(b);
pushIndex(b);
b.array_get(arrayType);
b.array_get(nullableObjectArrayType);
}
/// Pushes a Dart `List`'s length onto the stack as `i32`.
@ -1048,6 +1048,12 @@ class Translator with KernelNodes {
b.i32_wrap_i64();
}
/// Get the _ListBase._array field of type WasmArray<Object?>.
void getListBaseArray(w.InstructionsBuilder b) {
ClassInfo info = classInfo[listBaseClass]!;
b.struct_get(info.struct, FieldIndex.listArray);
}
ClassInfo getRecordClassInfo(RecordType recordType) =>
classInfo[recordClasses[RecordShape.fromType(recordType)]!]!;
@ -1207,16 +1213,18 @@ class _ClosureDynamicEntryGenerator implements _FunctionGenerator {
if (posIdx < positionalRequired) {
// Shape check passed, argument must be passed
b.local_get(posArgsListLocal);
translator.indexList(b, (b) => b.i32_const(posIdx));
b.i32_const(posIdx);
b.array_get(translator.nullableObjectArrayType);
} else {
// Argument may be missing
b.i32_const(posIdx);
b.local_get(posArgsListLocal);
translator.getListLength(b);
b.array_len();
b.i32_lt_u();
b.if_([], [translator.topInfo.nullableType]);
b.local_get(posArgsListLocal);
translator.indexList(b, (b) => b.i32_const(posIdx));
b.i32_const(posIdx);
b.array_get(translator.nullableObjectArrayType);
b.else_();
translator.constants.instantiateConstant(function, b,
paramInfo.positional[posIdx]!, translator.topInfo.nullableType);
@ -1260,12 +1268,11 @@ class _ClosureDynamicEntryGenerator implements _FunctionGenerator {
if (functionNodeDefaultValue == null && paramInfoDefaultValue == null) {
// Shape check passed, parameter must be passed
b.local_get(namedArgsListLocal);
translator.indexList(b, (b) {
b.local_get(namedArgValueIndexLocal);
translator.convertType(
function, namedArgValueIndexLocal.type, w.NumType.i64);
b.i32_wrap_i64();
});
b.local_get(namedArgValueIndexLocal);
translator.convertType(
function, namedArgValueIndexLocal.type, w.NumType.i64);
b.i32_wrap_i64();
b.array_get(translator.nullableObjectArrayType);
} else {
// Parameter may not be passed.
b.local_get(namedArgValueIndexLocal);
@ -1289,12 +1296,11 @@ class _ClosureDynamicEntryGenerator implements _FunctionGenerator {
}
b.else_(); // value index not null
b.local_get(namedArgsListLocal);
translator.indexList(b, (b) {
b.local_get(namedArgValueIndexLocal);
translator.convertType(
function, namedArgValueIndexLocal.type, w.NumType.i64);
b.i32_wrap_i64();
});
b.local_get(namedArgValueIndexLocal);
translator.convertType(
function, namedArgValueIndexLocal.type, w.NumType.i64);
b.i32_wrap_i64();
b.array_get(translator.nullableObjectArrayType);
b.end();
translator.convertType(
function, translator.topInfo.nullableType, targetInputs[inputIdx]);

View file

@ -8,7 +8,8 @@ part of "core_patch.dart";
/// forwarder and returns the index of the value of that named parameter.
/// Returns `null` if the name is not in the list.
@pragma("wasm:entry-point")
int? _getNamedParameterIndex(List<Object?> namedArguments, Symbol paramName) {
int? _getNamedParameterIndex(
WasmArray<Object?> namedArguments, Symbol paramName) {
for (int i = 0; i < namedArguments.length; i += 2) {
if (identical(namedArguments[i], paramName)) {
return i + 1;
@ -17,10 +18,21 @@ int? _getNamedParameterIndex(List<Object?> namedArguments, Symbol paramName) {
return null;
}
/// Converts a positional parameter list passed to a dynamic forwarder to a
/// list that can be passed to `Invocation` constructors.
@pragma("wasm:entry-point")
List<Object?> _positionalParametersToList(WasmArray<Object?> positional) {
final result = <Object?>[];
for (int i = 0; i < positional.length; ++i) {
result.add(positional[i]);
}
return result;
}
/// Converts a named parameter list passed to a dynamic forwarder to a map that
/// can be passed to `Invocation` constructors.
@pragma("wasm:entry-point")
Map<Symbol, Object?> _namedParameterListToMap(List<Object?> namedArguments) {
Map<Symbol, Object?> _namedParametersToMap(WasmArray<Object?> namedArguments) {
final Map<Symbol, Object?> map = {};
for (int i = 0; i < namedArguments.length; i += 2) {
map[namedArguments[i] as Symbol] = namedArguments[i + 1];
@ -33,9 +45,10 @@ Map<Symbol, Object?> _namedParameterListToMap(List<Object?> namedArguments) {
///
/// This is the opposite of [_namedParameterListToMap].
@pragma("wasm:entry-point")
List<Object?> _namedParameterMapToList(Map<Symbol, Object?>? namedArguments) {
WasmArray<Object?> _namedParameterMapToArray(
Map<Symbol, Object?>? namedArguments) {
if (namedArguments == null || namedArguments.isEmpty) {
return const [];
return const WasmArray.literal([]);
}
final List<MapEntry<Symbol, Object?>> entries = namedArguments.entries
@ -43,12 +56,12 @@ List<Object?> _namedParameterMapToList(Map<Symbol, Object?>? namedArguments) {
..sort((entry1, entry2) =>
_symbolToString(entry1.key).compareTo(_symbolToString(entry2.key)));
final List<Object?> list = [];
final WasmArray<Object?> result = WasmArray<Object?>(2 * entries.length);
for (final entry in entries) {
list.add(entry.key);
list.add(entry.value);
for (int i = 0; i < entries.length; ++i) {
result[2 * i] = entries[i].key;
result[2 * i + 1] = entries[i].value;
}
return list;
return result;
}

View file

@ -70,15 +70,6 @@ extension on List<_Type> {
}
}
// Direct getter to bypass the covariance check and the bounds check when
// indexing into a Dart list. This makes the indexing more efficient and avoids
// performing type checks while performing type checks.
extension _BypassListIndexingChecks<T> on List<T> {
@pragma("wasm:prefer-inline")
T _getUnchecked(int index) =>
unsafeCast(unsafeCast<_ListBase<T>>(this)._data[index]);
}
// TODO(joshualitt): We can cache the result of [_FutureOrType.asFuture].
abstract class _Type implements Type {
final bool isDeclaredNullable;
@ -1175,7 +1166,7 @@ class _TypeCheckVerificationError extends Error {
/// [namedArguments] is a list of `Symbol` and `Object?` pairs.
@pragma("wasm:entry-point")
bool _checkClosureShape(_FunctionType functionType, List<_Type> typeArguments,
List<Object?> positionalArguments, List<dynamic> namedArguments) {
WasmArray<Object?> positionalArguments, WasmArray<dynamic> namedArguments) {
// Check type args, add default types to the type list if its empty
if (typeArguments.isEmpty) {
final defaults = functionType.typeParameterDefaults;
@ -1208,8 +1199,7 @@ bool _checkClosureShape(_FunctionType functionType, List<_Type> typeArguments,
continue;
}
String argName = _symbolToString(
namedArguments._getUnchecked(namedArgIdx * 2) as Symbol);
String argName = _symbolToString(namedArguments[namedArgIdx * 2] as Symbol);
final cmp = argName.compareTo(param.name);
@ -1247,7 +1237,7 @@ bool _checkClosureShape(_FunctionType functionType, List<_Type> typeArguments,
/// [namedArguments] is a list of `Symbol` and `Object?` pairs.
@pragma("wasm:entry-point")
void _checkClosureType(_FunctionType functionType, List<_Type> typeArguments,
List<Object?> positionalArguments, List<dynamic> namedArguments) {
WasmArray<Object?> positionalArguments, WasmArray<dynamic> namedArguments) {
assert(functionType.typeParameterBounds.length == typeArguments.length);
if (!typeArguments.isEmpty) {
@ -1272,7 +1262,7 @@ void _checkClosureType(_FunctionType functionType, List<_Type> typeArguments,
// Check positional arguments
for (int i = 0; i < positionalArguments.length; i += 1) {
final Object? arg = positionalArguments._getUnchecked(i);
final Object? arg = positionalArguments[i];
final _Type paramTy = functionType.positionalParameters[i];
if (!_isSubtype(arg, paramTy)) {
// TODO(50991): Positional parameter names not available in runtime
@ -1286,10 +1276,10 @@ void _checkClosureType(_FunctionType functionType, List<_Type> typeArguments,
int namedParamIdx = 0;
int namedArgIdx = 0;
while (namedArgIdx * 2 < namedArguments.length) {
final String argName = _symbolToString(
namedArguments._getUnchecked(namedArgIdx * 2) as Symbol);
final String argName =
_symbolToString(namedArguments[namedArgIdx * 2] as Symbol);
if (argName == functionType.namedParameters[namedParamIdx].name) {
final arg = namedArguments._getUnchecked(namedArgIdx * 2 + 1);
final arg = namedArguments[namedArgIdx * 2 + 1];
final paramTy = functionType.namedParameters[namedParamIdx].type;
if (!_isSubtype(arg, paramTy)) {
_TypeError._throwArgumentTypeCheckError(