mirror of
https://github.com/dart-lang/sdk
synced 2024-09-05 00:13:50 +00:00
[dart2wasm] Special runtime type classes for special interface types
Adds special representations of runtime types for the interface types `Object`, `Function` and `Record`. With this change, all types with non-interface subtypes have special representations, which avoids some special cases down the line. Change-Id: I61b4da20fa1cc62d42e1770278a3272028c9e2a0 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/318681 Reviewed-by: Joshua Litt <joshualitt@google.com>
This commit is contained in:
parent
166c5d7b41
commit
0c1bb28ad4
|
@ -506,6 +506,11 @@ class ClassInfoCollector {
|
||||||
void collect() {
|
void collect() {
|
||||||
_initializeTop();
|
_initializeTop();
|
||||||
|
|
||||||
|
// Initialize the record base class early to give enough space for special
|
||||||
|
// values in the type category table before the first masquerade class
|
||||||
|
// (which is `Type`).
|
||||||
|
_initialize(translator.coreTypes.recordClass);
|
||||||
|
|
||||||
// Subclasses of the `_Closure` class are generated on the fly as fields
|
// Subclasses of the `_Closure` class are generated on the fly as fields
|
||||||
// with function types are encountered. Therefore, `_Closure` class must
|
// with function types are encountered. Therefore, `_Closure` class must
|
||||||
// be early in the initialization order.
|
// be early in the initialization order.
|
||||||
|
@ -526,11 +531,6 @@ class ClassInfoCollector {
|
||||||
_initialize(cls);
|
_initialize(cls);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize the record base class if we have record classes.
|
|
||||||
if (translator.recordClasses.isNotEmpty) {
|
|
||||||
_initialize(translator.coreTypes.recordClass);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Library library in translator.component.libraries) {
|
for (Library library in translator.component.libraries) {
|
||||||
for (Class cls in library.classes) {
|
for (Class cls in library.classes) {
|
||||||
if (cls.superclass == translator.coreTypes.recordClass) {
|
if (cls.superclass == translator.coreTypes.recordClass) {
|
||||||
|
|
|
@ -801,7 +801,7 @@ class ConstantCreator extends ConstantVisitor<ConstantInfo?> {
|
||||||
|
|
||||||
ClassInfo info = translator.classInfo[types.classForType(type)]!;
|
ClassInfo info = translator.classInfo[types.classForType(type)]!;
|
||||||
translator.functions.allocateClass(info.classId);
|
translator.functions.allocateClass(info.classId);
|
||||||
if (type is InterfaceType) {
|
if (type is InterfaceType && !types.isSpecializedClass(type.classNode)) {
|
||||||
return _makeInterfaceType(constant, type, info);
|
return _makeInterfaceType(constant, type, info);
|
||||||
} else if (type is FutureOrType) {
|
} else if (type is FutureOrType) {
|
||||||
return _makeFutureOrType(constant, type, info);
|
return _makeFutureOrType(constant, type, info);
|
||||||
|
@ -861,7 +861,8 @@ class ConstantCreator extends ConstantVisitor<ConstantInfo?> {
|
||||||
assert(type is VoidType ||
|
assert(type is VoidType ||
|
||||||
type is NeverType ||
|
type is NeverType ||
|
||||||
type is NullType ||
|
type is NullType ||
|
||||||
type is DynamicType);
|
type is DynamicType ||
|
||||||
|
type is InterfaceType && types.isSpecializedClass(type.classNode));
|
||||||
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);
|
||||||
|
|
|
@ -1149,6 +1149,7 @@ class Intrinsifier {
|
||||||
w.Label notMasqueraded = b.block();
|
w.Label notMasqueraded = b.block();
|
||||||
w.Label recordType = b.block();
|
w.Label recordType = b.block();
|
||||||
w.Label functionType = b.block();
|
w.Label functionType = b.block();
|
||||||
|
w.Label objectType = b.block();
|
||||||
w.Label abstractClass = b.block();
|
w.Label abstractClass = b.block();
|
||||||
|
|
||||||
// Look up the type category by class ID and switch on it.
|
// Look up the type category by class ID and switch on it.
|
||||||
|
@ -1161,6 +1162,7 @@ class Intrinsifier {
|
||||||
b.local_tee(resultClassId);
|
b.local_tee(resultClassId);
|
||||||
b.br_table([
|
b.br_table([
|
||||||
abstractClass,
|
abstractClass,
|
||||||
|
objectType,
|
||||||
functionType,
|
functionType,
|
||||||
recordType,
|
recordType,
|
||||||
if (masqueraded) notMasqueraded
|
if (masqueraded) notMasqueraded
|
||||||
|
@ -1170,6 +1172,16 @@ class Intrinsifier {
|
||||||
// We should never see class IDs for abstract types.
|
// We should never see class IDs for abstract types.
|
||||||
b.unreachable();
|
b.unreachable();
|
||||||
|
|
||||||
|
b.end(); // objectType
|
||||||
|
ClassInfo objectTypeInfo =
|
||||||
|
translator.classInfo[translator.objectTypeClass]!;
|
||||||
|
b.i32_const(objectTypeInfo.classId);
|
||||||
|
b.i32_const(initialIdentityHash);
|
||||||
|
// Runtime types are never nullable.
|
||||||
|
b.i32_const(0);
|
||||||
|
b.struct_new(objectTypeInfo.struct);
|
||||||
|
b.return_();
|
||||||
|
|
||||||
b.end(); // functionType
|
b.end(); // functionType
|
||||||
w.StructType closureBase = translator.closureLayouter.closureBaseStruct;
|
w.StructType closureBase = translator.closureLayouter.closureBaseStruct;
|
||||||
b.local_get(object);
|
b.local_get(object);
|
||||||
|
|
|
@ -68,6 +68,9 @@ mixin KernelNodes {
|
||||||
late final Class typeClass = index.getClass("dart:core", "_Type");
|
late final Class typeClass = index.getClass("dart:core", "_Type");
|
||||||
late final Class dynamicTypeClass =
|
late final Class dynamicTypeClass =
|
||||||
index.getClass("dart:core", "_DynamicType");
|
index.getClass("dart:core", "_DynamicType");
|
||||||
|
late final Class objectTypeClass = index.getClass("dart:core", "_ObjectType");
|
||||||
|
late final Class abstractFunctionTypeClass =
|
||||||
|
index.getClass("dart:core", "_AbstractFunctionType");
|
||||||
late final Class functionTypeClass =
|
late final Class functionTypeClass =
|
||||||
index.getClass("dart:core", "_FunctionType");
|
index.getClass("dart:core", "_FunctionType");
|
||||||
late final Class functionTypeParameterTypeClass =
|
late final Class functionTypeParameterTypeClass =
|
||||||
|
@ -86,6 +89,8 @@ mixin KernelNodes {
|
||||||
late final Class stackTraceClass = index.getClass("dart:core", "StackTrace");
|
late final Class stackTraceClass = index.getClass("dart:core", "StackTrace");
|
||||||
late final Class typeUniverseClass =
|
late final Class typeUniverseClass =
|
||||||
index.getClass("dart:core", "_TypeUniverse");
|
index.getClass("dart:core", "_TypeUniverse");
|
||||||
|
late final Class abstractRecordTypeClass =
|
||||||
|
index.getClass("dart:core", "_AbstractRecordType");
|
||||||
late final Class recordTypeClass = index.getClass("dart:core", "_RecordType");
|
late final Class recordTypeClass = index.getClass("dart:core", "_RecordType");
|
||||||
|
|
||||||
// dart:core sync* support classes
|
// dart:core sync* support classes
|
||||||
|
|
|
@ -18,10 +18,11 @@ import 'package:wasm_builder/wasm_builder.dart' as w;
|
||||||
/// the class ID of the masquerade.
|
/// the class ID of the masquerade.
|
||||||
class TypeCategory {
|
class TypeCategory {
|
||||||
static const abstractClass = 0;
|
static const abstractClass = 0;
|
||||||
static const function = 1;
|
static const object = 1;
|
||||||
static const record = 2;
|
static const function = 2;
|
||||||
static const notMasqueraded = 3;
|
static const record = 3;
|
||||||
static const minMasqueradeClassId = 4;
|
static const notMasqueraded = 4;
|
||||||
|
static const minMasqueradeClassId = 5;
|
||||||
static const maxMasqueradeClassId = 63; // Leaves 2 unused bits for future use
|
static const maxMasqueradeClassId = 63; // Leaves 2 unused bits for future use
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -279,6 +280,8 @@ class Types {
|
||||||
int category;
|
int category;
|
||||||
if (cls == null || cls.isAbstract) {
|
if (cls == null || cls.isAbstract) {
|
||||||
category = TypeCategory.abstractClass;
|
category = TypeCategory.abstractClass;
|
||||||
|
} else if (cls == coreTypes.objectClass) {
|
||||||
|
category = TypeCategory.object;
|
||||||
} else if (cls == translator.closureClass) {
|
} else if (cls == translator.closureClass) {
|
||||||
category = TypeCategory.function;
|
category = TypeCategory.function;
|
||||||
} else if (recordClasses.contains(cls)) {
|
} else if (recordClasses.contains(cls)) {
|
||||||
|
@ -355,6 +358,15 @@ class Types {
|
||||||
} else if (type is FutureOrType) {
|
} else if (type is FutureOrType) {
|
||||||
return translator.futureOrTypeClass;
|
return translator.futureOrTypeClass;
|
||||||
} else if (type is InterfaceType) {
|
} else if (type is InterfaceType) {
|
||||||
|
if (type.classNode == coreTypes.objectClass) {
|
||||||
|
return translator.objectTypeClass;
|
||||||
|
}
|
||||||
|
if (type.classNode == coreTypes.functionClass) {
|
||||||
|
return translator.abstractFunctionTypeClass;
|
||||||
|
}
|
||||||
|
if (type.classNode == coreTypes.recordClass) {
|
||||||
|
return translator.abstractRecordTypeClass;
|
||||||
|
}
|
||||||
return translator.interfaceTypeClass;
|
return translator.interfaceTypeClass;
|
||||||
} else if (type is FunctionType) {
|
} else if (type is FunctionType) {
|
||||||
return translator.functionTypeClass;
|
return translator.functionTypeClass;
|
||||||
|
@ -372,6 +384,12 @@ class Types {
|
||||||
throw "Unexpected DartType: $type";
|
throw "Unexpected DartType: $type";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isSpecializedClass(Class cls) {
|
||||||
|
return cls == coreTypes.objectClass ||
|
||||||
|
cls == coreTypes.functionClass ||
|
||||||
|
cls == coreTypes.recordClass;
|
||||||
|
}
|
||||||
|
|
||||||
/// Allocates a `List<_Type>` from [types] and pushes it to the stack.
|
/// Allocates a `List<_Type>` from [types] and pushes it to the stack.
|
||||||
void _makeTypeList(CodeGenerator codeGen, Iterable<DartType> types) {
|
void _makeTypeList(CodeGenerator codeGen, Iterable<DartType> types) {
|
||||||
if (types.every(_isTypeConstant)) {
|
if (types.every(_isTypeConstant)) {
|
||||||
|
|
|
@ -58,12 +58,18 @@ class ClassID {
|
||||||
external static int get cidFutureOrType;
|
external static int get cidFutureOrType;
|
||||||
@pragma("wasm:class-id", "dart.core#_InterfaceType")
|
@pragma("wasm:class-id", "dart.core#_InterfaceType")
|
||||||
external static int get cidInterfaceType;
|
external static int get cidInterfaceType;
|
||||||
|
@pragma("wasm:class-id", "dart.core#_ObjectType")
|
||||||
|
external static int get cidObjectType;
|
||||||
|
@pragma("wasm:class-id", "dart.core#_AbstractFunctionType")
|
||||||
|
external static int get cidAbstractFunctionType;
|
||||||
@pragma("wasm:class-id", "dart.core#_FunctionType")
|
@pragma("wasm:class-id", "dart.core#_FunctionType")
|
||||||
external static int get cidFunctionType;
|
external static int get cidFunctionType;
|
||||||
@pragma("wasm:class-id", "dart.core#_FunctionTypeParameterType")
|
@pragma("wasm:class-id", "dart.core#_FunctionTypeParameterType")
|
||||||
external static int get cidFunctionTypeParameterType;
|
external static int get cidFunctionTypeParameterType;
|
||||||
@pragma("wasm:class-id", "dart.core#_InterfaceTypeParameterType")
|
@pragma("wasm:class-id", "dart.core#_InterfaceTypeParameterType")
|
||||||
external static int get cidInterfaceTypeParameterType;
|
external static int get cidInterfaceTypeParameterType;
|
||||||
|
@pragma("wasm:class-id", "dart.core#_AbstractRecordType")
|
||||||
|
external static int get cidAbstractRecordType;
|
||||||
@pragma("wasm:class-id", "dart.core#_RecordType")
|
@pragma("wasm:class-id", "dart.core#_RecordType")
|
||||||
external static int get cidRecordType;
|
external static int get cidRecordType;
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,6 @@ part of 'core_patch.dart';
|
||||||
// file:
|
// file:
|
||||||
// * [_Type.asNonNullable]
|
// * [_Type.asNonNullable]
|
||||||
// * [_FutureOrType.asFuture].
|
// * [_FutureOrType.asFuture].
|
||||||
// TODO(joshualitt): Make `Function` a canonical type.
|
|
||||||
abstract class _Type implements Type {
|
abstract class _Type implements Type {
|
||||||
final bool isDeclaredNullable;
|
final bool isDeclaredNullable;
|
||||||
|
|
||||||
|
@ -30,7 +29,10 @@ abstract class _Type implements Type {
|
||||||
_testID(ClassID.cidInterfaceTypeParameterType);
|
_testID(ClassID.cidInterfaceTypeParameterType);
|
||||||
bool get isFunctionTypeParameterType =>
|
bool get isFunctionTypeParameterType =>
|
||||||
_testID(ClassID.cidFunctionTypeParameterType);
|
_testID(ClassID.cidFunctionTypeParameterType);
|
||||||
|
bool get isObject => _testID(ClassID.cidObjectType);
|
||||||
|
bool get isAbstractFunction => _testID(ClassID.cidAbstractFunctionType);
|
||||||
bool get isFunction => _testID(ClassID.cidFunctionType);
|
bool get isFunction => _testID(ClassID.cidFunctionType);
|
||||||
|
bool get isAbstractRecord => _testID(ClassID.cidAbstractRecordType);
|
||||||
bool get isRecord => _testID(ClassID.cidRecordType);
|
bool get isRecord => _testID(ClassID.cidRecordType);
|
||||||
|
|
||||||
T as<T>() => unsafeCast<T>(this);
|
T as<T>() => unsafeCast<T>(this);
|
||||||
|
@ -308,6 +310,57 @@ class _NamedParameter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _ObjectType extends _Type {
|
||||||
|
@pragma("wasm:entry-point")
|
||||||
|
const _ObjectType(super.isDeclaredNullable);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_Type get _asNonNullable => _ObjectType(false);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_Type get _asNullable => _ObjectType(true);
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object o) {
|
||||||
|
if (!(super == o)) return false;
|
||||||
|
_ObjectType other = unsafeCast<_ObjectType>(o);
|
||||||
|
return isDeclaredNullable == other.isDeclaredNullable;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode {
|
||||||
|
int hash = super.hashCode;
|
||||||
|
return mix64(hash ^ (isDeclaredNullable ? 1 : 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
StringBuffer s = StringBuffer();
|
||||||
|
s.write("Object");
|
||||||
|
if (isDeclaredNullable) s.write("?");
|
||||||
|
return s.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _AbstractFunctionType extends _Type {
|
||||||
|
@pragma("wasm:entry-point")
|
||||||
|
const _AbstractFunctionType(super.isDeclaredNullable);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_Type get _asNonNullable => _AbstractFunctionType(false);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_Type get _asNullable => _AbstractFunctionType(true);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
StringBuffer s = StringBuffer();
|
||||||
|
s.write("Function");
|
||||||
|
if (isDeclaredNullable) s.write("?");
|
||||||
|
return s.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class _FunctionType extends _Type {
|
class _FunctionType extends _Type {
|
||||||
// TODO(askesc): The [typeParameterOffset] is 0 except in the rare case where
|
// TODO(askesc): The [typeParameterOffset] is 0 except in the rare case where
|
||||||
// the function type contains a nested generic function type that contains a
|
// the function type contains a nested generic function type that contains a
|
||||||
|
@ -438,6 +491,25 @@ class _FunctionType extends _Type {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _AbstractRecordType extends _Type {
|
||||||
|
@pragma("wasm:entry-point")
|
||||||
|
const _AbstractRecordType(super.isDeclaredNullable);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_Type get _asNonNullable => _AbstractRecordType(false);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_Type get _asNullable => _AbstractRecordType(true);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
StringBuffer s = StringBuffer();
|
||||||
|
s.write("Record");
|
||||||
|
if (isDeclaredNullable) s.write("?");
|
||||||
|
return s.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class _RecordType extends _Type {
|
class _RecordType extends _Type {
|
||||||
final List<String> names;
|
final List<String> names;
|
||||||
final List<_Type> fieldTypes;
|
final List<_Type> fieldTypes;
|
||||||
|
@ -575,30 +647,20 @@ class _TypeUniverse {
|
||||||
return _TypeUniverse._(_getTypeRulesSupers(), _getTypeRulesSubstitutions());
|
return _TypeUniverse._(_getTypeRulesSupers(), _getTypeRulesSubstitutions());
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool isSpecificInterfaceType(_Type t, int classId) {
|
static bool isTopType(_Type type) {
|
||||||
if (!t.isInterface) return false;
|
return type.isObject && type.isDeclaredNullable ||
|
||||||
_InterfaceType type = t.as<_InterfaceType>();
|
type.isDynamic ||
|
||||||
return type.classId == classId;
|
type.isVoid;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool isObjectQuestionType(_Type t) =>
|
static bool isObjectOrTopType(_Type type) {
|
||||||
isObjectType(t) && t.isDeclaredNullable;
|
return type.isObject || type.isDynamic || type.isVoid;
|
||||||
|
|
||||||
static bool isObjectType(_Type t) =>
|
|
||||||
isSpecificInterfaceType(t, ClassID.cidObject);
|
|
||||||
|
|
||||||
static bool isTopType(_Type type) {
|
|
||||||
return isObjectQuestionType(type) || type.isDynamic || type.isVoid;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool isBottomType(_Type type) {
|
static bool isBottomType(_Type type) {
|
||||||
return type.isNever;
|
return type.isNever;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool isFunctionType(_Type t) =>
|
|
||||||
isSpecificInterfaceType(t, ClassID.cidFunction) ||
|
|
||||||
isSpecificInterfaceType(t, ClassID.cid_Closure);
|
|
||||||
|
|
||||||
static _Type substituteInterfaceTypeParameter(
|
static _Type substituteInterfaceTypeParameter(
|
||||||
_InterfaceTypeParameterType typeParameter, List<_Type> substitutions) {
|
_InterfaceTypeParameterType typeParameter, List<_Type> substitutions) {
|
||||||
// If the type parameter is non-nullable, or the substitution type is
|
// If the type parameter is non-nullable, or the substitution type is
|
||||||
|
@ -643,7 +705,11 @@ class _TypeUniverse {
|
||||||
/// substituted.
|
/// substituted.
|
||||||
static _Type substituteTypeArgument(
|
static _Type substituteTypeArgument(
|
||||||
_Type type, List<_Type> substitutions, _FunctionType? rootFunction) {
|
_Type type, List<_Type> substitutions, _FunctionType? rootFunction) {
|
||||||
if (type.isNever || type.isDynamic || type.isVoid || type.isNull) {
|
if (type.isNever ||
|
||||||
|
type.isDynamic ||
|
||||||
|
type.isVoid ||
|
||||||
|
type.isNull ||
|
||||||
|
type.isObject) {
|
||||||
return type;
|
return type;
|
||||||
} else if (type.isFutureOr) {
|
} else if (type.isFutureOr) {
|
||||||
return createNormalizedFutureOrType(
|
return createNormalizedFutureOrType(
|
||||||
|
@ -725,7 +791,7 @@ class _TypeUniverse {
|
||||||
|
|
||||||
static _Type createNormalizedFutureOrType(
|
static _Type createNormalizedFutureOrType(
|
||||||
bool isDeclaredNullable, _Type typeArgument) {
|
bool isDeclaredNullable, _Type typeArgument) {
|
||||||
if (isTopType(typeArgument) || isObjectType(typeArgument)) {
|
if (isObjectOrTopType(typeArgument)) {
|
||||||
return typeArgument;
|
return typeArgument;
|
||||||
} else if (typeArgument.isNever) {
|
} else if (typeArgument.isNever) {
|
||||||
return _InterfaceType(
|
return _InterfaceType(
|
||||||
|
@ -940,7 +1006,7 @@ class _TypeUniverse {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Right Object:
|
// Right Object:
|
||||||
if (isObjectType(t)) {
|
if (t.isObject) {
|
||||||
if (s.isFutureOr) {
|
if (s.isFutureOr) {
|
||||||
return isSubtype(s.as<_FutureOrType>().typeArgument, sEnv, t, tEnv);
|
return isSubtype(s.as<_FutureOrType>().typeArgument, sEnv, t, tEnv);
|
||||||
}
|
}
|
||||||
|
@ -984,7 +1050,7 @@ class _TypeUniverse {
|
||||||
// Left Promoted Variable does not apply at runtime.
|
// Left Promoted Variable does not apply at runtime.
|
||||||
|
|
||||||
// Function Type / Function:
|
// Function Type / Function:
|
||||||
if (s.isFunction && isFunctionType(t)) {
|
if (s.isFunction && t.isAbstractFunction) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1000,9 +1066,8 @@ class _TypeUniverse {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Records are subtypes of the `Record` type:
|
// Records are subtypes of the `Record` type:
|
||||||
if (s.isRecord && t.isInterface) {
|
if (s.isRecord && t.isAbstractRecord) {
|
||||||
final tInterfaceType = t.as<_InterfaceType>();
|
return true;
|
||||||
return tInterfaceType.classId == ClassID.cidRecord;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Interface Compositionality + Super-Interface:
|
// Interface Compositionality + Super-Interface:
|
||||||
|
|
Loading…
Reference in a new issue