mirror of
https://github.com/dart-lang/sdk
synced 2024-09-19 14:51:30 +00:00
[dart2wasm] Enable RTI tests for oddball and interface types.
Change-Id: I4b92d4cf30779b78f3eab1acd18193fdd58ab452 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/249641 Reviewed-by: Aske Simon Christensen <askesc@google.com> Commit-Queue: Joshua Litt <joshualitt@google.com>
This commit is contained in:
parent
a04e69716c
commit
f6f29df4b5
|
@ -297,7 +297,8 @@ class JsUtilWasmOptimizer extends Transformer {
|
||||||
Expression getObjectOffGlobalThis(Procedure node, List<String> selectors) {
|
Expression getObjectOffGlobalThis(Procedure node, List<String> selectors) {
|
||||||
Expression currentTarget = _globalThis;
|
Expression currentTarget = _globalThis;
|
||||||
for (String selector in selectors) {
|
for (String selector in selectors) {
|
||||||
currentTarget = _getProperty(node, currentTarget, selector);
|
currentTarget = _getProperty(node, currentTarget, selector,
|
||||||
|
typeArgument: _nonNullableObjectType);
|
||||||
}
|
}
|
||||||
return currentTarget;
|
return currentTarget;
|
||||||
}
|
}
|
||||||
|
@ -315,8 +316,9 @@ class JsUtilWasmOptimizer extends Transformer {
|
||||||
type: _nonNullableObjectType);
|
type: _nonNullableObjectType);
|
||||||
body.add(object);
|
body.add(object);
|
||||||
for (VariableDeclaration variable in node.function.namedParameters) {
|
for (VariableDeclaration variable in node.function.namedParameters) {
|
||||||
body.add(ExpressionStatement(
|
body.add(ExpressionStatement(_setProperty(
|
||||||
_setProperty(node, VariableGet(object), variable.name!, variable)));
|
node, VariableGet(object), variable.name!, variable,
|
||||||
|
typeArgument: variable.type)));
|
||||||
}
|
}
|
||||||
body.add(ReturnStatement(VariableGet(object)));
|
body.add(ReturnStatement(VariableGet(object)));
|
||||||
return Block(body);
|
return Block(body);
|
||||||
|
@ -349,12 +351,12 @@ class JsUtilWasmOptimizer extends Transformer {
|
||||||
///
|
///
|
||||||
/// The new [Expression] is equivalent to:
|
/// The new [Expression] is equivalent to:
|
||||||
/// `js_util.getProperty([object], [getterName])`.
|
/// `js_util.getProperty([object], [getterName])`.
|
||||||
Expression _getProperty(
|
Expression _getProperty(Procedure node, Expression object, String getterName,
|
||||||
Procedure node, Expression object, String getterName) =>
|
{DartType? typeArgument}) =>
|
||||||
StaticInvocation(
|
StaticInvocation(
|
||||||
_getPropertyTarget,
|
_getPropertyTarget,
|
||||||
Arguments([object, StringLiteral(getterName)],
|
Arguments([object, StringLiteral(getterName)],
|
||||||
types: [node.function.returnType]))
|
types: [typeArgument ?? node.function.returnType]))
|
||||||
..fileOffset = node.fileOffset;
|
..fileOffset = node.fileOffset;
|
||||||
|
|
||||||
/// Returns a new function body for the given [node] external getter.
|
/// Returns a new function body for the given [node] external getter.
|
||||||
|
@ -377,11 +379,11 @@ class JsUtilWasmOptimizer extends Transformer {
|
||||||
/// The new [Expression] is equivalent to:
|
/// The new [Expression] is equivalent to:
|
||||||
/// `js_util.setProperty([object], [setterName], [value])`.
|
/// `js_util.setProperty([object], [setterName], [value])`.
|
||||||
Expression _setProperty(Procedure node, Expression object, String setterName,
|
Expression _setProperty(Procedure node, Expression object, String setterName,
|
||||||
VariableDeclaration value) =>
|
VariableDeclaration value, {DartType? typeArgument}) =>
|
||||||
StaticInvocation(
|
StaticInvocation(
|
||||||
_setPropertyTarget,
|
_setPropertyTarget,
|
||||||
Arguments([object, StringLiteral(setterName), VariableGet(value)],
|
Arguments([object, StringLiteral(setterName), VariableGet(value)],
|
||||||
types: [node.function.returnType]))
|
types: [typeArgument ?? node.function.returnType]))
|
||||||
..fileOffset = node.fileOffset;
|
..fileOffset = node.fileOffset;
|
||||||
|
|
||||||
/// Returns a new function body for the given [node] external setter.
|
/// Returns a new function body for the given [node] external setter.
|
||||||
|
|
|
@ -803,8 +803,14 @@ class ConstantCreator extends ConstantVisitor<ConstantInfo?> {
|
||||||
return createConstant(constant, info.nonNullableType, (function, b) {
|
return createConstant(constant, info.nonNullableType, (function, b) {
|
||||||
b.i32_const(info.classId);
|
b.i32_const(info.classId);
|
||||||
b.i32_const(initialIdentityHash);
|
b.i32_const(initialIdentityHash);
|
||||||
types.encodeNullability(b, type);
|
|
||||||
b.i32_const(environmentIndex);
|
// A type parameter's type nullability is undetermined when it's
|
||||||
|
// syntactically not declared nullable and the bound of the type
|
||||||
|
// parameter is nullable. Because we are encoding the declared
|
||||||
|
// nullability, we only declare a type parameter to be nullable if it is
|
||||||
|
// explicitly declared to be nullabe.
|
||||||
|
b.i32_const(type.declaredNullability == Nullability.nullable ? 1 : 0);
|
||||||
|
b.i64_const(environmentIndex);
|
||||||
translator.struct_new(b, info);
|
translator.struct_new(b, info);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -593,7 +593,8 @@ class Intrinsifier {
|
||||||
if (className != null) {
|
if (className != null) {
|
||||||
List<String> libAndClass = className.split("#");
|
List<String> libAndClass = className.split("#");
|
||||||
Class cls = translator.libraries
|
Class cls = translator.libraries
|
||||||
.firstWhere((l) => l.name == libAndClass[0])
|
.firstWhere(
|
||||||
|
(l) => l.name == libAndClass[0] && l.importUri.scheme == 'dart')
|
||||||
.classes
|
.classes
|
||||||
.firstWhere((c) => c.name == libAndClass[1]);
|
.firstWhere((c) => c.name == libAndClass[1]);
|
||||||
int classId = translator.classInfo[cls]!.classId;
|
int classId = translator.classInfo[cls]!.classId;
|
||||||
|
@ -682,6 +683,8 @@ class Intrinsifier {
|
||||||
return translator.types.makeTypeRulesSupers(b);
|
return translator.types.makeTypeRulesSupers(b);
|
||||||
case "_getTypeRulesSubstitutions":
|
case "_getTypeRulesSubstitutions":
|
||||||
return translator.types.makeTypeRulesSubstitutions(b);
|
return translator.types.makeTypeRulesSubstitutions(b);
|
||||||
|
case "_getTypeNames":
|
||||||
|
return translator.types.makeTypeNames(b);
|
||||||
case "_getInterfaceTypeRuntimeType":
|
case "_getInterfaceTypeRuntimeType":
|
||||||
Expression object = node.arguments.positional[0];
|
Expression object = node.arguments.positional[0];
|
||||||
Expression typeArguments = node.arguments.positional[1];
|
Expression typeArguments = node.arguments.positional[1];
|
||||||
|
@ -793,6 +796,26 @@ class Intrinsifier {
|
||||||
return w.NumType.f64;
|
return w.NumType.f64;
|
||||||
case "getID":
|
case "getID":
|
||||||
return getID(node.arguments.positional.single);
|
return getID(node.arguments.positional.single);
|
||||||
|
case "makeListFixedLength":
|
||||||
|
ClassInfo receiverInfo =
|
||||||
|
translator.classInfo[translator.listBaseClass]!;
|
||||||
|
codeGen.wrap(
|
||||||
|
node.arguments.positional.single, receiverInfo.nonNullableType);
|
||||||
|
w.Local receiverLocal =
|
||||||
|
codeGen.function.addLocal(receiverInfo.nullableType);
|
||||||
|
b.local_tee(receiverLocal);
|
||||||
|
// We ignore the type argument and just update the classID of the
|
||||||
|
// receiver.
|
||||||
|
// TODO(joshualitt): If the amount of free space is significant, it
|
||||||
|
// might be worth doing a copy here.
|
||||||
|
ClassInfo topInfo = translator.topInfo;
|
||||||
|
ClassInfo fixedLengthListInfo =
|
||||||
|
translator.classInfo[translator.fixedLengthListClass]!;
|
||||||
|
b.i32_const(fixedLengthListInfo.classId);
|
||||||
|
b.struct_set(topInfo.struct, FieldIndex.classId);
|
||||||
|
b.local_get(receiverLocal);
|
||||||
|
b.ref_as_non_null();
|
||||||
|
return fixedLengthListInfo.nonNullableType;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -127,6 +127,7 @@ class Translator {
|
||||||
late final Procedure hashImmutableIndexNullable;
|
late final Procedure hashImmutableIndexNullable;
|
||||||
late final Procedure isSubtype;
|
late final Procedure isSubtype;
|
||||||
late final Procedure objectRuntimeType;
|
late final Procedure objectRuntimeType;
|
||||||
|
late final Procedure typeAsNullable;
|
||||||
late final Map<Class, w.StorageType> builtinTypes;
|
late final Map<Class, w.StorageType> builtinTypes;
|
||||||
late final Map<w.ValueType, Class> boxedClasses;
|
late final Map<w.ValueType, Class> boxedClasses;
|
||||||
|
|
||||||
|
@ -272,6 +273,9 @@ class Translator {
|
||||||
objectRuntimeType = lookupCore("Object")
|
objectRuntimeType = lookupCore("Object")
|
||||||
.procedures
|
.procedures
|
||||||
.firstWhere((p) => p.name.text == "_runtimeType");
|
.firstWhere((p) => p.name.text == "_runtimeType");
|
||||||
|
typeAsNullable = lookupCore("_Type")
|
||||||
|
.procedures
|
||||||
|
.firstWhere((p) => p.name.text == "asNullable");
|
||||||
builtinTypes = {
|
builtinTypes = {
|
||||||
coreTypes.boolClass: w.NumType.i32,
|
coreTypes.boolClass: w.NumType.i32,
|
||||||
coreTypes.intClass: w.NumType.i64,
|
coreTypes.intClass: w.NumType.i64,
|
||||||
|
|
|
@ -63,6 +63,9 @@ class Types {
|
||||||
late final List<List<List<DartType>>> typeRulesSubstitutions =
|
late final List<List<List<DartType>>> typeRulesSubstitutions =
|
||||||
_buildTypeRulesSubstitutions();
|
_buildTypeRulesSubstitutions();
|
||||||
|
|
||||||
|
/// A list which maps class ID to the classes [String] name.
|
||||||
|
late final List<String> typeNames = _buildTypeNames();
|
||||||
|
|
||||||
Types(this.translator);
|
Types(this.translator);
|
||||||
|
|
||||||
w.ValueType classAndFieldToType(Class cls, int fieldIndex) =>
|
w.ValueType classAndFieldToType(Class cls, int fieldIndex) =>
|
||||||
|
@ -100,8 +103,14 @@ class Types {
|
||||||
if (superclassInfo.cls == null ||
|
if (superclassInfo.cls == null ||
|
||||||
superclassInfo.cls == coreTypes.objectClass) continue;
|
superclassInfo.cls == coreTypes.objectClass) continue;
|
||||||
Class superclass = superclassInfo.cls!;
|
Class superclass = superclassInfo.cls!;
|
||||||
Iterable<Class> subclasses =
|
|
||||||
_getConcreteSubtypes(superclass).where((cls) => cls != superclass);
|
// TODO(joshualitt): This includes abstract types that can't be
|
||||||
|
// instantiated, but might be needed for subtype checks. The majority of
|
||||||
|
// abstract classes are probably unnecessary though. We should filter
|
||||||
|
// these cases to reduce the size of the type rules.
|
||||||
|
Iterable<Class> subclasses = translator.subtypes
|
||||||
|
.getSubtypesOf(superclass)
|
||||||
|
.where((cls) => cls != superclass);
|
||||||
Iterable<InterfaceType> subtypes = subclasses.map(
|
Iterable<InterfaceType> subtypes = subclasses.map(
|
||||||
(Class cls) => cls.getThisType(coreTypes, Nullability.nonNullable));
|
(Class cls) => cls.getThisType(coreTypes, Nullability.nonNullable));
|
||||||
for (InterfaceType subtype in subtypes) {
|
for (InterfaceType subtype in subtypes) {
|
||||||
|
@ -144,6 +153,17 @@ class Types {
|
||||||
return typeRulesSubstitutions;
|
return typeRulesSubstitutions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<String> _buildTypeNames() {
|
||||||
|
// This logic assumes `translator.classes` returns the classes indexed by
|
||||||
|
// class ID. If we ever change that logic, we will need to change this code.
|
||||||
|
List<String> typeNames = [];
|
||||||
|
for (ClassInfo classInfo in translator.classes) {
|
||||||
|
String className = classInfo.cls?.name ?? '';
|
||||||
|
typeNames.add(className);
|
||||||
|
}
|
||||||
|
return typeNames;
|
||||||
|
}
|
||||||
|
|
||||||
/// Builds a map of subclasses to the transitive set of superclasses they
|
/// Builds a map of subclasses to the transitive set of superclasses they
|
||||||
/// implement.
|
/// implement.
|
||||||
/// TODO(joshualitt): This implementation is just temporary. Eventually we
|
/// TODO(joshualitt): This implementation is just temporary. Eventually we
|
||||||
|
@ -205,6 +225,25 @@ class Types {
|
||||||
return expectedType;
|
return expectedType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a list of string type names for pretty printing types.
|
||||||
|
w.ValueType makeTypeNames(w.Instructions b) {
|
||||||
|
w.ValueType expectedType =
|
||||||
|
translator.classInfo[translator.immutableListClass]!.nonNullableType;
|
||||||
|
DartType stringType = InterfaceType(
|
||||||
|
translator.stringBaseClass,
|
||||||
|
Nullability.nonNullable,
|
||||||
|
[translator.coreTypes.stringNonNullableRawType]);
|
||||||
|
List<StringConstant> listStringConstant = [];
|
||||||
|
for (String name in typeNames) {
|
||||||
|
listStringConstant.add(StringConstant(name));
|
||||||
|
}
|
||||||
|
DartType listStringType = InterfaceType(
|
||||||
|
translator.immutableListClass, Nullability.nonNullable, [stringType]);
|
||||||
|
translator.constants.instantiateConstant(null, b,
|
||||||
|
ListConstant(listStringType, listStringConstant), expectedType);
|
||||||
|
return expectedType;
|
||||||
|
}
|
||||||
|
|
||||||
bool isGenericFunction(FunctionType type) => type.typeParameters.isNotEmpty;
|
bool isGenericFunction(FunctionType type) => type.typeParameters.isNotEmpty;
|
||||||
|
|
||||||
bool isGenericFunctionTypeParameter(TypeParameterType type) =>
|
bool isGenericFunctionTypeParameter(TypeParameterType type) =>
|
||||||
|
@ -358,7 +397,11 @@ class Types {
|
||||||
type is FutureOrType ||
|
type is FutureOrType ||
|
||||||
type is FunctionType);
|
type is FunctionType);
|
||||||
if (type is TypeParameterType) {
|
if (type is TypeParameterType) {
|
||||||
return codeGen.instantiateTypeParameter(type.parameter);
|
codeGen.instantiateTypeParameter(type.parameter);
|
||||||
|
if (type.declaredNullability == Nullability.nullable) {
|
||||||
|
codeGen.call(translator.typeAsNullable.reference);
|
||||||
|
}
|
||||||
|
return nonNullableTypeType;
|
||||||
}
|
}
|
||||||
ClassInfo info = translator.classInfo[classForType(type)]!;
|
ClassInfo info = translator.classInfo[classForType(type)]!;
|
||||||
translator.functions.allocateClass(info.classId);
|
translator.functions.allocateClass(info.classId);
|
||||||
|
@ -390,16 +433,20 @@ class Types {
|
||||||
void emitTypeTest(CodeGenerator codeGen, DartType type, DartType operandType,
|
void emitTypeTest(CodeGenerator codeGen, DartType type, DartType operandType,
|
||||||
TreeNode node) {
|
TreeNode node) {
|
||||||
w.Instructions b = codeGen.b;
|
w.Instructions b = codeGen.b;
|
||||||
if (type is! InterfaceType) {
|
if (type is FunctionType) {
|
||||||
// TODO(joshualitt): We can enable this after fixing `.runtimeType`.
|
// TODO(joshualitt): We can enable type tests for [FunctionType] after
|
||||||
// makeType(codeGen, type);
|
// enabling `.runtimeType` for [FunctionType].
|
||||||
// codeGen.call(translator.isSubtype.reference);
|
print("Not implemented: Type test with function type $type"
|
||||||
print("Not implemented: Type test with non-interface type $type"
|
|
||||||
" at ${node.location}");
|
" at ${node.location}");
|
||||||
b.drop();
|
b.drop();
|
||||||
b.i32_const(1);
|
b.i32_const(1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (type is! InterfaceType) {
|
||||||
|
makeType(codeGen, type);
|
||||||
|
codeGen.call(translator.isSubtype.reference);
|
||||||
|
return;
|
||||||
|
}
|
||||||
bool isPotentiallyNullable = operandType.isPotentiallyNullable;
|
bool isPotentiallyNullable = operandType.isPotentiallyNullable;
|
||||||
w.Label? resultLabel;
|
w.Label? resultLabel;
|
||||||
if (isPotentiallyNullable) {
|
if (isPotentiallyNullable) {
|
||||||
|
@ -412,6 +459,15 @@ class Types {
|
||||||
b.local_get(operand);
|
b.local_get(operand);
|
||||||
b.br_on_null(nullLabel);
|
b.br_on_null(nullLabel);
|
||||||
}
|
}
|
||||||
|
void _endPotentiallyNullableBlock() {
|
||||||
|
if (isPotentiallyNullable) {
|
||||||
|
b.br(resultLabel!);
|
||||||
|
b.end(); // nullLabel
|
||||||
|
encodeNullability(b, type);
|
||||||
|
b.end(); // resultLabel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (type.typeArguments.any((t) => t is! DynamicType)) {
|
if (type.typeArguments.any((t) => t is! DynamicType)) {
|
||||||
// If the tested-against type as an instance of the static operand type
|
// If the tested-against type as an instance of the static operand type
|
||||||
// has the same type arguments as the static operand type, it is not
|
// has the same type arguments as the static operand type, it is not
|
||||||
|
@ -421,8 +477,10 @@ class Types {
|
||||||
.getTypeAsInstanceOf(type, cls, codeGen.member.enclosingLibrary)
|
.getTypeAsInstanceOf(type, cls, codeGen.member.enclosingLibrary)
|
||||||
?.withDeclaredNullability(operandType.declaredNullability);
|
?.withDeclaredNullability(operandType.declaredNullability);
|
||||||
if (base != operandType) {
|
if (base != operandType) {
|
||||||
print("Not implemented: Type test with type arguments"
|
makeType(codeGen, type);
|
||||||
" at ${node.location}");
|
codeGen.call(translator.isSubtype.reference);
|
||||||
|
_endPotentiallyNullableBlock();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
List<Class> concrete = _getConcreteSubtypes(type.classNode).toList();
|
List<Class> concrete = _getConcreteSubtypes(type.classNode).toList();
|
||||||
|
@ -454,12 +512,7 @@ class Types {
|
||||||
b.i32_const(0);
|
b.i32_const(0);
|
||||||
b.end(); // done
|
b.end(); // done
|
||||||
}
|
}
|
||||||
if (isPotentiallyNullable) {
|
_endPotentiallyNullableBlock();
|
||||||
b.br(resultLabel!);
|
|
||||||
b.end(); // nullLabel
|
|
||||||
encodeNullability(b, type);
|
|
||||||
b.end(); // resultLabel
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if a given type is nullable, and false otherwise. This
|
/// Returns true if a given type is nullable, and false otherwise. This
|
||||||
|
|
|
@ -16,10 +16,12 @@ class ClassID {
|
||||||
external static int get cidUint8ArrayView;
|
external static int get cidUint8ArrayView;
|
||||||
@pragma("wasm:class-id", "dart.core#Object")
|
@pragma("wasm:class-id", "dart.core#Object")
|
||||||
external static int get cidObject;
|
external static int get cidObject;
|
||||||
@pragma("wasm:class-id", "dart.async#Future")
|
@pragma("wasm:class-id", "dart.async#_Future")
|
||||||
external static int get cidFuture;
|
external static int get cid_Future;
|
||||||
@pragma("wasm:class-id", "dart.core#Function")
|
@pragma("wasm:class-id", "dart.core#Function")
|
||||||
external static int get cidFunction;
|
external static int get cidFunction;
|
||||||
|
@pragma("wasm:class-id", "dart.core#_Function")
|
||||||
|
external static int get cid_Function;
|
||||||
|
|
||||||
// Class IDs for RTI Types.
|
// Class IDs for RTI Types.
|
||||||
@pragma("wasm:class-id", "dart.core#_NeverType")
|
@pragma("wasm:class-id", "dart.core#_NeverType")
|
||||||
|
|
|
@ -45,6 +45,8 @@ class Object {
|
||||||
/// override [runtimeType].
|
/// override [runtimeType].
|
||||||
@patch
|
@patch
|
||||||
external Type get runtimeType;
|
external Type get runtimeType;
|
||||||
|
|
||||||
|
@pragma("wasm:entry-point")
|
||||||
_Type get _runtimeType => _getInterfaceTypeRuntimeType(this, _typeArguments);
|
_Type get _runtimeType => _getInterfaceTypeRuntimeType(this, _typeArguments);
|
||||||
|
|
||||||
@patch
|
@patch
|
||||||
|
|
|
@ -48,6 +48,7 @@ abstract class _Type implements Type {
|
||||||
|
|
||||||
@pragma("wasm:entry-point")
|
@pragma("wasm:entry-point")
|
||||||
class _NeverType extends _Type {
|
class _NeverType extends _Type {
|
||||||
|
@pragma("wasm:entry-point")
|
||||||
const _NeverType() : super(false);
|
const _NeverType() : super(false);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -63,6 +64,7 @@ class _NeverType extends _Type {
|
||||||
|
|
||||||
@pragma("wasm:entry-point")
|
@pragma("wasm:entry-point")
|
||||||
class _DynamicType extends _Type {
|
class _DynamicType extends _Type {
|
||||||
|
@pragma("wasm:entry-point")
|
||||||
const _DynamicType() : super(true);
|
const _DynamicType() : super(true);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -77,6 +79,7 @@ class _DynamicType extends _Type {
|
||||||
|
|
||||||
@pragma("wasm:entry-point")
|
@pragma("wasm:entry-point")
|
||||||
class _VoidType extends _Type {
|
class _VoidType extends _Type {
|
||||||
|
@pragma("wasm:entry-point")
|
||||||
const _VoidType() : super(true);
|
const _VoidType() : super(true);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -91,6 +94,7 @@ class _VoidType extends _Type {
|
||||||
|
|
||||||
@pragma("wasm:entry-point")
|
@pragma("wasm:entry-point")
|
||||||
class _NullType extends _Type {
|
class _NullType extends _Type {
|
||||||
|
@pragma("wasm:entry-point")
|
||||||
const _NullType() : super(true);
|
const _NullType() : super(true);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -110,6 +114,7 @@ class _NullType extends _Type {
|
||||||
class _InterfaceTypeParameterType extends _Type {
|
class _InterfaceTypeParameterType extends _Type {
|
||||||
final int environmentIndex;
|
final int environmentIndex;
|
||||||
|
|
||||||
|
@pragma("wasm:entry-point")
|
||||||
const _InterfaceTypeParameterType(super.isNullable, this.environmentIndex);
|
const _InterfaceTypeParameterType(super.isNullable, this.environmentIndex);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -121,7 +126,7 @@ class _InterfaceTypeParameterType extends _Type {
|
||||||
throw 'Type parameter should have been substituted already.';
|
throw 'Type parameter should have been substituted already.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => '$environmentIndex';
|
String toString() => 'T$environmentIndex';
|
||||||
}
|
}
|
||||||
|
|
||||||
@pragma("wasm:entry-point")
|
@pragma("wasm:entry-point")
|
||||||
|
@ -140,7 +145,7 @@ class _GenericFunctionTypeParameterType extends _Type {
|
||||||
throw 'Type parameter should have been substituted already.';
|
throw 'Type parameter should have been substituted already.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => '$environmentIndex';
|
String toString() => 'G$environmentIndex';
|
||||||
}
|
}
|
||||||
|
|
||||||
@pragma("wasm:entry-point")
|
@pragma("wasm:entry-point")
|
||||||
|
@ -151,7 +156,7 @@ class _FutureOrType extends _Type {
|
||||||
const _FutureOrType(bool isNullable, this.typeArgument) : super(isNullable);
|
const _FutureOrType(bool isNullable, this.typeArgument) : super(isNullable);
|
||||||
|
|
||||||
_InterfaceType get asFuture =>
|
_InterfaceType get asFuture =>
|
||||||
_InterfaceType(ClassID.cidFuture, isNullable, [typeArgument]);
|
_InterfaceType(ClassID.cid_Future, isNullable, [typeArgument]);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_Type get _asNonNullable {
|
_Type get _asNonNullable {
|
||||||
|
@ -231,8 +236,7 @@ class _InterfaceType extends _Type {
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
StringBuffer s = StringBuffer();
|
StringBuffer s = StringBuffer();
|
||||||
s.write("Interface");
|
s.write(_getTypeNames()[classId]);
|
||||||
s.write(classId);
|
|
||||||
if (typeArguments.isNotEmpty) {
|
if (typeArguments.isNotEmpty) {
|
||||||
s.write("<");
|
s.write("<");
|
||||||
for (int i = 0; i < typeArguments.length; i++) {
|
for (int i = 0; i < typeArguments.length; i++) {
|
||||||
|
@ -364,8 +368,8 @@ class _FunctionType extends _Type {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(joshualitt): Implement. This should probably extend _FunctionType.
|
// TODO(joshualitt): Implement. This should probably extend _FunctionType.
|
||||||
@pragma("wasm:entry-point")
|
|
||||||
class _GenericFunctionType extends _Type {
|
class _GenericFunctionType extends _Type {
|
||||||
|
@pragma("wasm:entry-point")
|
||||||
const _GenericFunctionType(bool isNullable) : super(isNullable);
|
const _GenericFunctionType(bool isNullable) : super(isNullable);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -380,6 +384,7 @@ class _GenericFunctionType extends _Type {
|
||||||
|
|
||||||
external List<List<int>> _getTypeRulesSupers();
|
external List<List<int>> _getTypeRulesSupers();
|
||||||
external List<List<List<_Type>>> _getTypeRulesSubstitutions();
|
external List<List<List<_Type>>> _getTypeRulesSubstitutions();
|
||||||
|
external List<String> _getTypeNames();
|
||||||
|
|
||||||
class _Environment {
|
class _Environment {
|
||||||
List<List<_Type>> scopes = [];
|
List<List<_Type>> scopes = [];
|
||||||
|
@ -450,7 +455,8 @@ class _TypeUniverse {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isFunctionType(_Type t) =>
|
bool isFunctionType(_Type t) =>
|
||||||
isSpecificInterfaceType(t, ClassID.cidFunction);
|
isSpecificInterfaceType(t, ClassID.cidFunction) ||
|
||||||
|
isSpecificInterfaceType(t, ClassID.cid_Function);
|
||||||
|
|
||||||
bool areTypeArgumentsSubtypes(List<_Type> sArgs, _Environment? sEnv,
|
bool areTypeArgumentsSubtypes(List<_Type> sArgs, _Environment? sEnv,
|
||||||
List<_Type> tArgs, _Environment? tEnv) {
|
List<_Type> tArgs, _Environment? tEnv) {
|
||||||
|
@ -484,13 +490,22 @@ class _TypeUniverse {
|
||||||
|
|
||||||
List<_Type> substitutions = typeRulesSubstitutions[sId][sSuperIndexOfT];
|
List<_Type> substitutions = typeRulesSubstitutions[sId][sSuperIndexOfT];
|
||||||
|
|
||||||
|
// If we have empty type arguments then create a list of dynamic type
|
||||||
|
// arguments.
|
||||||
|
List<_Type> sTypeArguments = s.typeArguments;
|
||||||
|
if (substitutions.isNotEmpty && sTypeArguments.isEmpty) {
|
||||||
|
sTypeArguments = List<_Type>.generate(
|
||||||
|
substitutions.length, (int index) => const _DynamicType(),
|
||||||
|
growable: false);
|
||||||
|
}
|
||||||
|
|
||||||
// If [sEnv] is null, then create a new environment. Otherwise, we are doing
|
// If [sEnv] is null, then create a new environment. Otherwise, we are doing
|
||||||
// a recursive type check, so extend the existing environment with [s]'s
|
// a recursive type check, so extend the existing environment with [s]'s
|
||||||
// type arguments.
|
// type arguments.
|
||||||
if (sEnv == null) {
|
if (sEnv == null) {
|
||||||
sEnv = _Environment.from(s.typeArguments);
|
sEnv = _Environment.from(sTypeArguments);
|
||||||
} else {
|
} else {
|
||||||
sEnv.push(s.typeArguments);
|
sEnv.push(sTypeArguments);
|
||||||
}
|
}
|
||||||
bool result =
|
bool result =
|
||||||
areTypeArgumentsSubtypes(substitutions, sEnv, t.typeArguments, tEnv);
|
areTypeArgumentsSubtypes(substitutions, sEnv, t.typeArguments, tEnv);
|
||||||
|
@ -562,6 +577,7 @@ class _TypeUniverse {
|
||||||
}
|
}
|
||||||
while (sIndex < sNamedLength) {
|
while (sIndex < sNamedLength) {
|
||||||
if (sNamed[sIndex].isRequired) return false;
|
if (sNamed[sIndex].isRequired) return false;
|
||||||
|
sIndex++;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -606,7 +622,7 @@ class _TypeUniverse {
|
||||||
if (!isSubtype(sFutureOr.typeArgument, sEnv, t, tEnv)) {
|
if (!isSubtype(sFutureOr.typeArgument, sEnv, t, tEnv)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return _isSubtype(sFutureOr.asFuture, t);
|
return isSubtype(sFutureOr.asFuture, sEnv, t, tEnv);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Left Nullable:
|
// Left Nullable:
|
||||||
|
@ -643,6 +659,12 @@ class _TypeUniverse {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(joshualitt): This is not correct, but it is necessary until we fully
|
||||||
|
// implement RTI for FunctionTypes.
|
||||||
|
if (isFunctionType(s) && (t.isFunction || t.isGenericFunction)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// Positional Function Types + Named Function Types:
|
// Positional Function Types + Named Function Types:
|
||||||
if (s.isGenericFunction && t.isGenericFunction) {
|
if (s.isGenericFunction && t.isGenericFunction) {
|
||||||
// TODO(joshualitt): Implement case.
|
// TODO(joshualitt): Implement case.
|
||||||
|
|
|
@ -196,6 +196,7 @@ class _FutureListener<S, T> {
|
||||||
bool shouldChain(Future<dynamic> value) => value is Future<T> || value is! T;
|
bool shouldChain(Future<dynamic> value) => value is Future<T> || value is! T;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@pragma("wasm:entry-point")
|
||||||
class _Future<T> implements Future<T> {
|
class _Future<T> implements Future<T> {
|
||||||
/// Initial state, waiting for a result. In this state, the
|
/// Initial state, waiting for a result. In this state, the
|
||||||
/// [_resultOrListeners] field holds a single-linked list of
|
/// [_resultOrListeners] field holds a single-linked list of
|
||||||
|
|
Loading…
Reference in a new issue