blob: e62bf7f0ecfada43589a8e1299872b503782142d [file] [log] [blame]
// Copyright (c) 2022, 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 'dart:math';
import 'dart:typed_data';
import 'package:dart2wasm/class_info.dart';
import 'package:dart2wasm/translator.dart';
import 'package:dart2wasm/types.dart';
import 'package:kernel/ast.dart';
import 'package:kernel/type_algebra.dart' show substitute;
import 'package:wasm_builder/wasm_builder.dart' as w;
class ConstantInfo {
final Constant constant;
final w.DefinedGlobal global;
final w.DefinedFunction? function;
ConstantInfo(this.constant, this.global, this.function);
}
typedef ConstantCodeGenerator = void Function(
w.DefinedFunction?, w.Instructions);
/// Handles the creation of Dart constants. Can operate in two modes - eager and
/// lazy - controlled by [TranslatorOptions.lazyConstants].
///
/// Each (non-trivial) constant is assigned to a Wasm global. Multiple
/// occurrences of the same constant use the same global.
///
/// In eager mode, the constant is contained within the global initializer,
/// meaning all constants are initialized eagerly during module initialization.
/// In lazy mode, the global starts out uninitialized, and every use of the
/// constant checks the global to see if it has been initialized and calls an
/// initialization function otherwise.
class Constants {
final Translator translator;
final Map<Constant, ConstantInfo> constantInfo = {};
final StringBuffer oneByteStrings = StringBuffer();
final StringBuffer twoByteStrings = StringBuffer();
late final w.DefinedFunction oneByteStringFunction;
late final w.DefinedFunction twoByteStringFunction;
late final w.DataSegment oneByteStringSegment;
late final w.DataSegment twoByteStringSegment;
late final w.DefinedGlobal emptyString;
late final w.DefinedGlobal emptyTypeList;
late final ClassInfo typeInfo = translator.classInfo[translator.typeClass]!;
bool currentlyCreating = false;
Constants(this.translator) {
if (lazyConstants) {
oneByteStringFunction = makeStringFunction(translator.oneByteStringClass);
twoByteStringFunction = makeStringFunction(translator.twoByteStringClass);
} else if (stringDataSegments) {
oneByteStringSegment = m.addDataSegment();
twoByteStringSegment = m.addDataSegment();
}
initEmptyString();
initEmptyTypeList();
}
w.Module get m => translator.m;
bool get lazyConstants => translator.options.lazyConstants;
bool get stringDataSegments => translator.options.stringDataSegments;
void initEmptyString() {
ClassInfo info = translator.classInfo[translator.oneByteStringClass]!;
translator.functions.allocateClass(info.classId);
w.ArrayType arrayType =
(info.struct.fields.last.type as w.RefType).heapType as w.ArrayType;
if (lazyConstants) {
w.RefType emptyStringType = info.nullableType;
emptyString = m.addGlobal(w.GlobalType(emptyStringType));
emptyString.initializer.ref_null(emptyStringType.heapType);
emptyString.initializer.end();
w.Instructions b = translator.initFunction.body;
b.i32_const(info.classId);
b.i32_const(initialIdentityHash);
b.i32_const(0);
translator.array_new_default(b, arrayType);
translator.struct_new(b, info);
b.global_set(emptyString);
} else {
w.RefType emptyStringType = info.nonNullableType;
emptyString = m.addGlobal(w.GlobalType(emptyStringType, mutable: false));
w.Instructions ib = emptyString.initializer;
ib.i32_const(info.classId);
ib.i32_const(initialIdentityHash);
translator.array_init(ib, arrayType, 0);
translator.struct_new(ib, info);
ib.end();
}
Constant emptyStringConstant = StringConstant("");
constantInfo[emptyStringConstant] =
ConstantInfo(emptyStringConstant, emptyString, null);
}
void initEmptyTypeList() {
ClassInfo info = translator.classInfo[translator.immutableListClass]!;
translator.functions.allocateClass(info.classId);
w.RefType refType = info.struct.fields.last.type.unpacked as w.RefType;
w.ArrayType arrayType = refType.heapType as w.ArrayType;
// Create the empty type list with its type parameter uninitialized for now.
if (lazyConstants) {
w.RefType emptyListType = info.nullableType;
emptyTypeList = m.addGlobal(w.GlobalType(emptyListType));
emptyTypeList.initializer.ref_null(emptyListType.heapType);
emptyTypeList.initializer.end();
w.Instructions b = translator.initFunction.body;
b.i32_const(info.classId);
b.i32_const(initialIdentityHash);
b.ref_null(typeInfo.struct); // Initialized later
b.i64_const(0);
b.i32_const(0);
translator.array_new_default(b, arrayType);
translator.struct_new(b, info);
b.global_set(emptyTypeList);
} else {
w.RefType emptyListType = info.nonNullableType;
emptyTypeList = m.addGlobal(w.GlobalType(emptyListType, mutable: false));
w.Instructions ib = emptyTypeList.initializer;
ib.i32_const(info.classId);
ib.i32_const(initialIdentityHash);
ib.ref_null(typeInfo.struct); // Initialized later
ib.i64_const(0);
translator.array_init(ib, arrayType, 0);
translator.struct_new(ib, info);
ib.end();
}
Constant emptyTypeListConstant = ListConstant(
InterfaceType(translator.typeClass, Nullability.nonNullable), const []);
constantInfo[emptyTypeListConstant] =
ConstantInfo(emptyTypeListConstant, emptyTypeList, null);
// Initialize the type parameter of the empty type list to the type object
// for _Type, which itself refers to the empty type list.
w.Instructions b = translator.initFunction.body;
b.global_get(emptyTypeList);
instantiateConstant(
translator.initFunction,
b,
TypeLiteralConstant(
InterfaceType(translator.typeClass, Nullability.nonNullable)),
typeInfo.nullableType);
b.struct_set(info.struct,
translator.typeParameterIndex[info.cls!.typeParameters.single]!);
}
void finalize() {
if (lazyConstants) {
finalizeStrings();
}
}
void finalizeStrings() {
Uint8List oneByteStringsAsBytes =
Uint8List.fromList(oneByteStrings.toString().codeUnits);
assert(Endian.host == Endian.little);
Uint8List twoByteStringsAsBytes =
Uint16List.fromList(twoByteStrings.toString().codeUnits)
.buffer
.asUint8List();
Uint8List stringsAsBytes = (BytesBuilder()
..add(twoByteStringsAsBytes)
..add(oneByteStringsAsBytes))
.toBytes();
w.Memory stringMemory =
m.addMemory(false, stringsAsBytes.length, stringsAsBytes.length);
m.addDataSegment(stringsAsBytes, stringMemory, 0);
makeStringFunctionBody(translator.oneByteStringClass, oneByteStringFunction,
(b) {
b.i32_load8_u(stringMemory, twoByteStringsAsBytes.length);
});
makeStringFunctionBody(translator.twoByteStringClass, twoByteStringFunction,
(b) {
b.i32_const(1);
b.i32_shl();
b.i32_load16_u(stringMemory, 0);
});
}
/// Create one of the two Wasm functions (one for each string type) called
/// from every lazily initialized string constant (of that type) to create and
/// initialize the string.
///
/// The function signature is (i32 offset, i32 length) -> (ref stringClass)
/// where offset and length are measured in characters and indicate the place
/// in the corresponding string data segment from which to copy this string.
w.DefinedFunction makeStringFunction(Class cls) {
ClassInfo info = translator.classInfo[cls]!;
w.FunctionType ftype = translator.functionType(
const [w.NumType.i32, w.NumType.i32], [info.nonNullableType]);
return m.addFunction(ftype, "makeString (${cls.name})");
}
void makeStringFunctionBody(Class cls, w.DefinedFunction function,
void Function(w.Instructions) emitLoad) {
ClassInfo info = translator.classInfo[cls]!;
w.ArrayType arrayType =
(info.struct.fields.last.type as w.RefType).heapType as w.ArrayType;
w.Local offset = function.locals[0];
w.Local length = function.locals[1];
w.Local array = function.addLocal(
translator.typeForLocal(w.RefType.def(arrayType, nullable: false)));
w.Local index = function.addLocal(w.NumType.i32);
w.Instructions b = function.body;
b.local_get(length);
translator.array_new_default(b, arrayType);
b.local_set(array);
b.i32_const(0);
b.local_set(index);
w.Label loop = b.loop();
b.local_get(array);
b.local_get(index);
b.local_get(offset);
b.local_get(index);
b.i32_add();
emitLoad(b);
b.array_set(arrayType);
b.local_get(index);
b.i32_const(1);
b.i32_add();
b.local_tee(index);
b.local_get(length);
b.i32_lt_u();
b.br_if(loop);
b.end();
b.i32_const(info.classId);
b.i32_const(initialIdentityHash);
b.local_get(array);
translator.struct_new(b, info);
b.end();
}
/// Makes a type list [ListConstant].
ListConstant makeTypeList(List<DartType> types) => ListConstant(
InterfaceType(translator.typeClass, Nullability.nonNullable),
types.map((t) => TypeLiteralConstant(t)).toList());
/// Makes a `_NamedParameter` [InstanceConstant].
InstanceConstant makeNamedParameterConstant(NamedType n) {
Class namedParameter = translator.namedParameterClass;
assert(namedParameter.fields[0].name.text == 'name' &&
namedParameter.fields[1].name.text == 'type' &&
namedParameter.fields[2].name.text == 'isRequired');
Reference namedParameterName = namedParameter.fields[0].fieldReference;
Reference namedParameterType = namedParameter.fields[1].fieldReference;
Reference namedParameterIsRequired =
namedParameter.fields[2].fieldReference;
return InstanceConstant(namedParameter.reference, [], {
namedParameterName: StringConstant(n.name),
namedParameterType: TypeLiteralConstant(n.type),
namedParameterIsRequired: BoolConstant(n.isRequired)
});
}
/// Makes a [ListConstant] of `_NamedParameters` to initialize a [FunctionType].
ListConstant makeNamedParametersList(FunctionType type) => ListConstant(
translator.types.namedParameterType,
type.namedParameters.map(makeNamedParameterConstant).toList());
/// Ensure that the constant has a Wasm global assigned.
///
/// In eager mode, sub-constants must have Wasm globals assigned before the
/// global for the composite constant is assigned, since global initializers
/// can only refer to earlier globals.
void ensureConstant(Constant constant) {
ConstantCreator(this).ensureConstant(constant);
}
/// Emit code to push a constant onto the stack.
void instantiateConstant(w.DefinedFunction? function, w.Instructions b,
Constant constant, w.ValueType expectedType) {
if (expectedType == translator.voidMarker) return;
ConstantInstantiator(this, function, b, expectedType).instantiate(constant);
}
}
class ConstantInstantiator extends ConstantVisitor<w.ValueType> {
final Constants constants;
final w.DefinedFunction? function;
final w.Instructions b;
final w.ValueType expectedType;
ConstantInstantiator(
this.constants, this.function, this.b, this.expectedType);
Translator get translator => constants.translator;
w.Module get m => translator.m;
void instantiate(Constant constant) {
w.ValueType resultType = constant.accept(this);
assert(!translator.needsConversion(resultType, expectedType),
"For $constant: expected $expectedType, got $resultType");
}
@override
w.ValueType defaultConstant(Constant constant) {
ConstantInfo info = ConstantCreator(constants).ensureConstant(constant)!;
w.ValueType globalType = info.global.type.type;
if (globalType.nullable) {
if (info.function != null) {
// Lazily initialized constant.
w.Label done = b.block(const [], [globalType.withNullability(false)]);
b.global_get(info.global);
b.br_on_non_null(done);
b.call(info.function!);
b.end();
} else {
// Constant initialized in the module init function.
b.global_get(info.global);
b.ref_as_non_null();
}
return globalType.withNullability(false);
} else {
// Constant initialized eagerly in a global initializer.
b.global_get(info.global);
return globalType;
}
}
@override
w.ValueType visitNullConstant(NullConstant node) {
w.ValueType? expectedType = this.expectedType;
if (expectedType != translator.voidMarker) {
if (expectedType.nullable) {
w.HeapType heapType =
expectedType is w.RefType ? expectedType.heapType : w.HeapType.data;
b.ref_null(heapType);
} else {
// This only happens in invalid but unreachable code produced by the
// TFA dead-code elimination.
b.comment("Non-nullable null constant");
b.block(const [], [expectedType]);
b.unreachable();
b.end();
}
}
return expectedType;
}
w.ValueType _maybeBox(w.ValueType wasmType, void Function() pushValue) {
if (expectedType is w.RefType) {
ClassInfo info = translator.classInfo[translator.boxedClasses[wasmType]]!;
b.i32_const(info.classId);
pushValue();
translator.struct_new(b, info);
return info.nonNullableType;
} else {
pushValue();
return wasmType;
}
}
@override
w.ValueType visitBoolConstant(BoolConstant constant) {
return _maybeBox(w.NumType.i32, () {
b.i32_const(constant.value ? 1 : 0);
});
}
@override
w.ValueType visitIntConstant(IntConstant constant) {
return _maybeBox(w.NumType.i64, () {
b.i64_const(constant.value);
});
}
@override
w.ValueType visitDoubleConstant(DoubleConstant constant) {
return _maybeBox(w.NumType.f64, () {
b.f64_const(constant.value);
});
}
}
class ConstantCreator extends ConstantVisitor<ConstantInfo?> {
final Constants constants;
ConstantCreator(this.constants);
Translator get translator => constants.translator;
Types get types => translator.types;
w.Module get m => constants.m;
bool get lazyConstants => constants.lazyConstants;
ConstantInfo? ensureConstant(Constant constant) {
ConstantInfo? info = constants.constantInfo[constant];
if (info == null) {
info = constant.accept(this);
if (info != null) {
constants.constantInfo[constant] = info;
}
}
return info;
}
ConstantInfo createConstant(
Constant constant, w.RefType type, ConstantCodeGenerator generator) {
assert(!type.nullable);
if (lazyConstants) {
// Create uninitialized global and function to initialize it.
w.DefinedGlobal global =
m.addGlobal(w.GlobalType(type.withNullability(true)));
global.initializer.ref_null(type.heapType);
global.initializer.end();
w.FunctionType ftype = translator.functionType(const [], [type]);
w.DefinedFunction function = m.addFunction(ftype, "$constant");
generator(function, function.body);
w.Local temp = function.addLocal(translator.typeForLocal(type));
w.Instructions b2 = function.body;
b2.local_tee(temp);
b2.global_set(global);
b2.local_get(temp);
translator.convertType(function, temp.type, type);
b2.end();
return ConstantInfo(constant, global, function);
} else {
// Create global with the constant in its initializer.
assert(!constants.currentlyCreating);
constants.currentlyCreating = true;
w.DefinedGlobal global = m.addGlobal(w.GlobalType(type, mutable: false));
generator(null, global.initializer);
global.initializer.end();
constants.currentlyCreating = false;
return ConstantInfo(constant, global, null);
}
}
@override
ConstantInfo? defaultConstant(Constant constant) => null;
@override
ConstantInfo? visitStringConstant(StringConstant constant) {
bool isOneByte = constant.value.codeUnits.every((c) => c <= 255);
ClassInfo info = translator.classInfo[isOneByte
? translator.oneByteStringClass
: translator.twoByteStringClass]!;
translator.functions.allocateClass(info.classId);
w.RefType type = info.nonNullableType;
return createConstant(constant, type, (function, b) {
if (lazyConstants) {
// Copy string contents from linear memory on initialization. The memory
// is initialized by an active data segment for each string type.
StringBuffer buffer =
isOneByte ? constants.oneByteStrings : constants.twoByteStrings;
int offset = buffer.length;
int length = constant.value.length;
buffer.write(constant.value);
b.i32_const(offset);
b.i32_const(length);
b.call(isOneByte
? constants.oneByteStringFunction
: constants.twoByteStringFunction);
} else {
w.ArrayType arrayType =
(info.struct.fields.last.type as w.RefType).heapType as w.ArrayType;
b.i32_const(info.classId);
b.i32_const(initialIdentityHash);
if (constants.stringDataSegments) {
// Initialize string contents from passive data segment.
w.DataSegment segment;
Uint8List bytes;
if (isOneByte) {
segment = constants.oneByteStringSegment;
bytes = Uint8List.fromList(constant.value.codeUnits);
} else {
assert(Endian.host == Endian.little);
segment = constants.twoByteStringSegment;
bytes = Uint16List.fromList(constant.value.codeUnits)
.buffer
.asUint8List();
}
int offset = segment.length;
segment.append(bytes);
b.i32_const(offset);
b.i32_const(constant.value.length);
translator.array_init_from_data(b, arrayType, segment);
} else {
// Initialize string contents from i32 constants on the stack.
for (int charCode in constant.value.codeUnits) {
b.i32_const(charCode);
}
translator.array_init(b, arrayType, constant.value.length);
}
translator.struct_new(b, info);
}
});
}
@override
ConstantInfo? visitInstanceConstant(InstanceConstant constant) {
Class cls = constant.classNode;
ClassInfo info = translator.classInfo[cls]!;
translator.functions.allocateClass(info.classId);
w.RefType type = info.nonNullableType;
// Collect sub-constants for field values.
const int baseFieldCount = 2;
int fieldCount = info.struct.fields.length;
List<Constant?> subConstants = List.filled(fieldCount, null);
constant.fieldValues.forEach((reference, subConstant) {
int index = translator.fieldIndex[reference.asField]!;
assert(subConstants[index] == null);
subConstants[index] = subConstant;
ensureConstant(subConstant);
});
// Collect sub-constants for type arguments.
Map<TypeParameter, DartType> substitution = {};
List<DartType> args = constant.typeArguments;
while (true) {
for (int i = 0; i < cls.typeParameters.length; i++) {
TypeParameter parameter = cls.typeParameters[i];
DartType arg = substitute(args[i], substitution);
substitution[parameter] = arg;
int index = translator.typeParameterIndex[parameter]!;
Constant typeArgConstant = TypeLiteralConstant(arg);
subConstants[index] = typeArgConstant;
ensureConstant(typeArgConstant);
}
Supertype? supertype = cls.supertype;
if (supertype == null) break;
cls = supertype.classNode;
args = supertype.typeArguments;
}
return createConstant(constant, type, (function, b) {
b.i32_const(info.classId);
b.i32_const(initialIdentityHash);
for (int i = baseFieldCount; i < fieldCount; i++) {
Constant subConstant = subConstants[i]!;
constants.instantiateConstant(
function, b, subConstant, info.struct.fields[i].type.unpacked);
}
translator.struct_new(b, info);
});
}
@override
ConstantInfo? visitListConstant(ListConstant constant) {
Constant typeArgConstant = TypeLiteralConstant(constant.typeArgument);
ensureConstant(typeArgConstant);
for (Constant subConstant in constant.entries) {
ensureConstant(subConstant);
}
ClassInfo info = translator.classInfo[translator.immutableListClass]!;
translator.functions.allocateClass(info.classId);
w.RefType type = info.nonNullableType;
return createConstant(constant, type, (function, b) {
w.RefType refType = info.struct.fields.last.type.unpacked as w.RefType;
w.ArrayType arrayType = refType.heapType as w.ArrayType;
w.ValueType elementType = arrayType.elementType.type.unpacked;
int length = constant.entries.length;
b.i32_const(info.classId);
b.i32_const(initialIdentityHash);
constants.instantiateConstant(
function, b, typeArgConstant, constants.typeInfo.nullableType);
b.i64_const(length);
if (lazyConstants) {
// Allocate array and set each entry to the corresponding sub-constant.
w.Local arrayLocal = function!.addLocal(
refType.withNullability(!translator.options.localNullability));
b.i32_const(length);
translator.array_new_default(b, arrayType);
b.local_set(arrayLocal);
for (int i = 0; i < length; i++) {
b.local_get(arrayLocal);
b.i32_const(i);
constants.instantiateConstant(
function, b, constant.entries[i], elementType);
b.array_set(arrayType);
}
b.local_get(arrayLocal);
if (arrayLocal.type.nullable) {
b.ref_as_non_null();
}
} else {
// Push all sub-constants on the stack and initialize array from them.
for (int i = 0; i < length; i++) {
constants.instantiateConstant(
function, b, constant.entries[i], elementType);
}
translator.array_init(b, arrayType, length);
}
translator.struct_new(b, info);
});
}
@override
ConstantInfo? visitMapConstant(MapConstant constant) {
Constant keyTypeConstant = TypeLiteralConstant(constant.keyType);
ensureConstant(keyTypeConstant);
Constant valueTypeConstant = TypeLiteralConstant(constant.valueType);
ensureConstant(valueTypeConstant);
List<Constant> dataElements =
List.generate(constant.entries.length * 2, (i) {
ConstantMapEntry entry = constant.entries[i >> 1];
return i.isEven ? entry.key : entry.value;
});
ListConstant dataList = ListConstant(const DynamicType(), dataElements);
ensureConstant(dataList);
ClassInfo info = translator.classInfo[translator.immutableMapClass]!;
translator.functions.allocateClass(info.classId);
w.RefType type = info.nonNullableType;
return createConstant(constant, type, (function, b) {
w.RefType indexType =
info.struct.fields[FieldIndex.hashBaseIndex].type as w.RefType;
w.RefType dataType =
info.struct.fields[FieldIndex.hashBaseData].type as w.RefType;
b.i32_const(info.classId);
b.i32_const(initialIdentityHash);
b.ref_null(indexType.heapType); // _index
b.i64_const(_computeHashMask(constant.entries.length)); // _hashMask
constants.instantiateConstant(function, b, dataList, dataType); // _data
b.i64_const(dataElements.length); // _usedData
b.i64_const(0); // _deletedKeys
constants.instantiateConstant(
function, b, keyTypeConstant, constants.typeInfo.nullableType);
constants.instantiateConstant(
function, b, valueTypeConstant, constants.typeInfo.nullableType);
translator.struct_new(b, info);
});
}
@override
ConstantInfo? visitSetConstant(SetConstant constant) {
Constant elementTypeConstant = TypeLiteralConstant(constant.typeArgument);
ensureConstant(elementTypeConstant);
ListConstant dataList = ListConstant(const DynamicType(), constant.entries);
ensureConstant(dataList);
ClassInfo info = translator.classInfo[translator.immutableSetClass]!;
translator.functions.allocateClass(info.classId);
w.RefType type = info.nonNullableType;
return createConstant(constant, type, (function, b) {
w.RefType indexType =
info.struct.fields[FieldIndex.hashBaseIndex].type as w.RefType;
w.RefType dataType =
info.struct.fields[FieldIndex.hashBaseData].type as w.RefType;
b.i32_const(info.classId);
b.i32_const(initialIdentityHash);
b.ref_null(indexType.heapType); // _index
b.i64_const(_computeHashMask(constant.entries.length)); // _hashMask
constants.instantiateConstant(function, b, dataList, dataType); // _data
b.i64_const(constant.entries.length); // _usedData
b.i64_const(0); // _deletedKeys
constants.instantiateConstant(
function, b, elementTypeConstant, constants.typeInfo.nullableType);
translator.struct_new(b, info);
});
}
int _computeHashMask(int entries) {
// This computation of the hash mask follows the computations in
// [_ImmutableLinkedHashMapMixin._createIndex],
// [_ImmutableLinkedHashSetMixin._createIndex] and
// [_HashBase._indexSizeToHashMask].
const int initialIndexSize = 8;
final int indexSize = max(entries * 2, initialIndexSize);
final int hashMask = (1 << (31 - (indexSize - 1).bitLength)) - 1;
return hashMask;
}
@override
ConstantInfo? visitStaticTearOffConstant(StaticTearOffConstant constant) {
w.DefinedFunction closureFunction =
translator.getTearOffFunction(constant.targetReference.asProcedure);
int parameterCount = closureFunction.type.inputs.length - 1;
w.StructType struct = translator.closureStructType(parameterCount);
w.RefType type = w.RefType.def(struct, nullable: false);
return createConstant(constant, type, (function, b) {
ClassInfo info = translator.classInfo[translator.functionClass]!;
translator.functions.allocateClass(info.classId);
b.i32_const(info.classId);
b.i32_const(initialIdentityHash);
b.global_get(translator.globals.dummyGlobal); // Dummy context
if (lazyConstants) {
w.DefinedGlobal global = translator.makeFunctionRef(closureFunction);
b.global_get(global);
} else {
b.ref_func(closureFunction);
}
translator.struct_new(b, parameterCount);
});
}
ConstantInfo? _makeInterfaceType(
TypeLiteralConstant constant, InterfaceType type, ClassInfo info) {
ListConstant typeArgs = constants.makeTypeList(type.typeArguments);
ensureConstant(typeArgs);
return createConstant(constant, info.nonNullableType, (function, b) {
ClassInfo typeInfo = translator.classInfo[type.classNode]!;
w.ValueType typeListExpectedType = info
.struct.fields[FieldIndex.interfaceTypeTypeArguments].type.unpacked;
b.i32_const(info.classId);
b.i32_const(initialIdentityHash);
types.encodeNullability(b, type);
b.i64_const(typeInfo.classId);
constants.instantiateConstant(
function, b, typeArgs, typeListExpectedType);
translator.struct_new(b, info);
});
}
ConstantInfo? _makeFutureOrType(
TypeLiteralConstant constant, FutureOrType type, ClassInfo info) {
TypeLiteralConstant typeArgument = TypeLiteralConstant(type.typeArgument);
ensureConstant(typeArgument);
return createConstant(constant, info.nonNullableType, (function, b) {
b.i32_const(info.classId);
b.i32_const(initialIdentityHash);
types.encodeNullability(b, type);
constants.instantiateConstant(
function, b, typeArgument, types.nonNullableTypeType);
translator.struct_new(b, info);
});
}
ConstantInfo? _makeFunctionType(
TypeLiteralConstant constant, FunctionType type, ClassInfo info) {
TypeLiteralConstant returnTypeConstant =
TypeLiteralConstant(type.returnType);
ListConstant positionalParametersConstant =
constants.makeTypeList(type.positionalParameters);
IntConstant requiredParameterCountConstant =
IntConstant(type.requiredParameterCount);
ListConstant namedParametersConstant =
constants.makeNamedParametersList(type);
ensureConstant(returnTypeConstant);
ensureConstant(positionalParametersConstant);
ensureConstant(requiredParameterCountConstant);
ensureConstant(namedParametersConstant);
return createConstant(constant, info.nonNullableType, (function, b) {
b.i32_const(info.classId);
b.i32_const(initialIdentityHash);
types.encodeNullability(b, type);
constants.instantiateConstant(
function, b, returnTypeConstant, types.nonNullableTypeType);
constants.instantiateConstant(function, b, positionalParametersConstant,
types.typeListExpectedType);
constants.instantiateConstant(
function, b, requiredParameterCountConstant, w.NumType.i64);
constants.instantiateConstant(function, b, namedParametersConstant,
types.namedParametersExpectedType);
translator.struct_new(b, info);
});
}
@override
ConstantInfo? visitTypeLiteralConstant(TypeLiteralConstant constant) {
DartType type = constant.type;
ClassInfo info = translator.classInfo[types.classForType(type)]!;
translator.functions.allocateClass(info.classId);
if (type is InterfaceType) {
return _makeInterfaceType(constant, type, info);
} else if (type is FutureOrType) {
return _makeFutureOrType(constant, type, info);
} else if (type is FunctionType) {
if (types.isGenericFunction(type)) {
// TODO(joshualitt): implement generic function types and share most of
// the logic with _makeFunctionType.
return createConstant(constant, info.nonNullableType, (function, b) {
b.i32_const(info.classId);
b.i32_const(initialIdentityHash);
types.encodeNullability(b, type);
translator.struct_new(b, info);
});
} else {
return _makeFunctionType(constant, type, info);
}
} else if (type is TypeParameterType) {
// TODO(joshualitt): Handle generic function types.
assert(!types.isGenericFunctionTypeParameter(type));
int environmentIndex =
types.interfaceTypeEnvironment.lookup(type.parameter);
return createConstant(constant, info.nonNullableType, (function, b) {
b.i32_const(info.classId);
b.i32_const(initialIdentityHash);
types.encodeNullability(b, type);
b.i32_const(environmentIndex);
translator.struct_new(b, info);
});
} else {
assert(type is VoidType ||
type is NeverType ||
type is NullType ||
type is DynamicType);
return createConstant(constant, info.nonNullableType, (function, b) {
b.i32_const(info.classId);
b.i32_const(initialIdentityHash);
types.encodeNullability(b, type);
translator.struct_new(b, info);
});
}
}
}