mirror of
https://github.com/dart-lang/sdk
synced 2024-07-08 12:06:26 +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() {
|
||||
_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
|
||||
// with function types are encountered. Therefore, `_Closure` class must
|
||||
// be early in the initialization order.
|
||||
|
@ -526,11 +531,6 @@ class ClassInfoCollector {
|
|||
_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 (Class cls in library.classes) {
|
||||
if (cls.superclass == translator.coreTypes.recordClass) {
|
||||
|
|
|
@ -801,7 +801,7 @@ class ConstantCreator extends ConstantVisitor<ConstantInfo?> {
|
|||
|
||||
ClassInfo info = translator.classInfo[types.classForType(type)]!;
|
||||
translator.functions.allocateClass(info.classId);
|
||||
if (type is InterfaceType) {
|
||||
if (type is InterfaceType && !types.isSpecializedClass(type.classNode)) {
|
||||
return _makeInterfaceType(constant, type, info);
|
||||
} else if (type is FutureOrType) {
|
||||
return _makeFutureOrType(constant, type, info);
|
||||
|
@ -861,7 +861,8 @@ class ConstantCreator extends ConstantVisitor<ConstantInfo?> {
|
|||
assert(type is VoidType ||
|
||||
type is NeverType ||
|
||||
type is NullType ||
|
||||
type is DynamicType);
|
||||
type is DynamicType ||
|
||||
type is InterfaceType && types.isSpecializedClass(type.classNode));
|
||||
return createConstant(constant, info.nonNullableType, (function, b) {
|
||||
b.i32_const(info.classId);
|
||||
b.i32_const(initialIdentityHash);
|
||||
|
|
|
@ -1149,6 +1149,7 @@ class Intrinsifier {
|
|||
w.Label notMasqueraded = b.block();
|
||||
w.Label recordType = b.block();
|
||||
w.Label functionType = b.block();
|
||||
w.Label objectType = b.block();
|
||||
w.Label abstractClass = b.block();
|
||||
|
||||
// Look up the type category by class ID and switch on it.
|
||||
|
@ -1161,6 +1162,7 @@ class Intrinsifier {
|
|||
b.local_tee(resultClassId);
|
||||
b.br_table([
|
||||
abstractClass,
|
||||
objectType,
|
||||
functionType,
|
||||
recordType,
|
||||
if (masqueraded) notMasqueraded
|
||||
|
@ -1170,6 +1172,16 @@ class Intrinsifier {
|
|||
// We should never see class IDs for abstract types.
|
||||
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
|
||||
w.StructType closureBase = translator.closureLayouter.closureBaseStruct;
|
||||
b.local_get(object);
|
||||
|
|
|
@ -68,6 +68,9 @@ mixin KernelNodes {
|
|||
late final Class typeClass = index.getClass("dart:core", "_Type");
|
||||
late final Class dynamicTypeClass =
|
||||
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 =
|
||||
index.getClass("dart:core", "_FunctionType");
|
||||
late final Class functionTypeParameterTypeClass =
|
||||
|
@ -86,6 +89,8 @@ mixin KernelNodes {
|
|||
late final Class stackTraceClass = index.getClass("dart:core", "StackTrace");
|
||||
late final Class typeUniverseClass =
|
||||
index.getClass("dart:core", "_TypeUniverse");
|
||||
late final Class abstractRecordTypeClass =
|
||||
index.getClass("dart:core", "_AbstractRecordType");
|
||||
late final Class recordTypeClass = index.getClass("dart:core", "_RecordType");
|
||||
|
||||
// dart:core sync* support classes
|
||||
|
|
|
@ -18,10 +18,11 @@ import 'package:wasm_builder/wasm_builder.dart' as w;
|
|||
/// the class ID of the masquerade.
|
||||
class TypeCategory {
|
||||
static const abstractClass = 0;
|
||||
static const function = 1;
|
||||
static const record = 2;
|
||||
static const notMasqueraded = 3;
|
||||
static const minMasqueradeClassId = 4;
|
||||
static const object = 1;
|
||||
static const function = 2;
|
||||
static const record = 3;
|
||||
static const notMasqueraded = 4;
|
||||
static const minMasqueradeClassId = 5;
|
||||
static const maxMasqueradeClassId = 63; // Leaves 2 unused bits for future use
|
||||
}
|
||||
|
||||
|
@ -279,6 +280,8 @@ class Types {
|
|||
int category;
|
||||
if (cls == null || cls.isAbstract) {
|
||||
category = TypeCategory.abstractClass;
|
||||
} else if (cls == coreTypes.objectClass) {
|
||||
category = TypeCategory.object;
|
||||
} else if (cls == translator.closureClass) {
|
||||
category = TypeCategory.function;
|
||||
} else if (recordClasses.contains(cls)) {
|
||||
|
@ -355,6 +358,15 @@ class Types {
|
|||
} else if (type is FutureOrType) {
|
||||
return translator.futureOrTypeClass;
|
||||
} 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;
|
||||
} else if (type is FunctionType) {
|
||||
return translator.functionTypeClass;
|
||||
|
@ -372,6 +384,12 @@ class Types {
|
|||
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.
|
||||
void _makeTypeList(CodeGenerator codeGen, Iterable<DartType> types) {
|
||||
if (types.every(_isTypeConstant)) {
|
||||
|
|
|
@ -58,12 +58,18 @@ class ClassID {
|
|||
external static int get cidFutureOrType;
|
||||
@pragma("wasm:class-id", "dart.core#_InterfaceType")
|
||||
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")
|
||||
external static int get cidFunctionType;
|
||||
@pragma("wasm:class-id", "dart.core#_FunctionTypeParameterType")
|
||||
external static int get cidFunctionTypeParameterType;
|
||||
@pragma("wasm:class-id", "dart.core#_InterfaceTypeParameterType")
|
||||
external static int get cidInterfaceTypeParameterType;
|
||||
@pragma("wasm:class-id", "dart.core#_AbstractRecordType")
|
||||
external static int get cidAbstractRecordType;
|
||||
@pragma("wasm:class-id", "dart.core#_RecordType")
|
||||
external static int get cidRecordType;
|
||||
|
||||
|
|
|
@ -13,7 +13,6 @@ part of 'core_patch.dart';
|
|||
// file:
|
||||
// * [_Type.asNonNullable]
|
||||
// * [_FutureOrType.asFuture].
|
||||
// TODO(joshualitt): Make `Function` a canonical type.
|
||||
abstract class _Type implements Type {
|
||||
final bool isDeclaredNullable;
|
||||
|
||||
|
@ -30,7 +29,10 @@ abstract class _Type implements Type {
|
|||
_testID(ClassID.cidInterfaceTypeParameterType);
|
||||
bool get isFunctionTypeParameterType =>
|
||||
_testID(ClassID.cidFunctionTypeParameterType);
|
||||
bool get isObject => _testID(ClassID.cidObjectType);
|
||||
bool get isAbstractFunction => _testID(ClassID.cidAbstractFunctionType);
|
||||
bool get isFunction => _testID(ClassID.cidFunctionType);
|
||||
bool get isAbstractRecord => _testID(ClassID.cidAbstractRecordType);
|
||||
bool get isRecord => _testID(ClassID.cidRecordType);
|
||||
|
||||
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 {
|
||||
// TODO(askesc): The [typeParameterOffset] is 0 except in the rare case where
|
||||
// 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 {
|
||||
final List<String> names;
|
||||
final List<_Type> fieldTypes;
|
||||
|
@ -575,30 +647,20 @@ class _TypeUniverse {
|
|||
return _TypeUniverse._(_getTypeRulesSupers(), _getTypeRulesSubstitutions());
|
||||
}
|
||||
|
||||
static bool isSpecificInterfaceType(_Type t, int classId) {
|
||||
if (!t.isInterface) return false;
|
||||
_InterfaceType type = t.as<_InterfaceType>();
|
||||
return type.classId == classId;
|
||||
static bool isTopType(_Type type) {
|
||||
return type.isObject && type.isDeclaredNullable ||
|
||||
type.isDynamic ||
|
||||
type.isVoid;
|
||||
}
|
||||
|
||||
static bool isObjectQuestionType(_Type t) =>
|
||||
isObjectType(t) && t.isDeclaredNullable;
|
||||
|
||||
static bool isObjectType(_Type t) =>
|
||||
isSpecificInterfaceType(t, ClassID.cidObject);
|
||||
|
||||
static bool isTopType(_Type type) {
|
||||
return isObjectQuestionType(type) || type.isDynamic || type.isVoid;
|
||||
static bool isObjectOrTopType(_Type type) {
|
||||
return type.isObject || type.isDynamic || type.isVoid;
|
||||
}
|
||||
|
||||
static bool isBottomType(_Type type) {
|
||||
return type.isNever;
|
||||
}
|
||||
|
||||
static bool isFunctionType(_Type t) =>
|
||||
isSpecificInterfaceType(t, ClassID.cidFunction) ||
|
||||
isSpecificInterfaceType(t, ClassID.cid_Closure);
|
||||
|
||||
static _Type substituteInterfaceTypeParameter(
|
||||
_InterfaceTypeParameterType typeParameter, List<_Type> substitutions) {
|
||||
// If the type parameter is non-nullable, or the substitution type is
|
||||
|
@ -643,7 +705,11 @@ class _TypeUniverse {
|
|||
/// substituted.
|
||||
static _Type substituteTypeArgument(
|
||||
_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;
|
||||
} else if (type.isFutureOr) {
|
||||
return createNormalizedFutureOrType(
|
||||
|
@ -725,7 +791,7 @@ class _TypeUniverse {
|
|||
|
||||
static _Type createNormalizedFutureOrType(
|
||||
bool isDeclaredNullable, _Type typeArgument) {
|
||||
if (isTopType(typeArgument) || isObjectType(typeArgument)) {
|
||||
if (isObjectOrTopType(typeArgument)) {
|
||||
return typeArgument;
|
||||
} else if (typeArgument.isNever) {
|
||||
return _InterfaceType(
|
||||
|
@ -940,7 +1006,7 @@ class _TypeUniverse {
|
|||
}
|
||||
|
||||
// Right Object:
|
||||
if (isObjectType(t)) {
|
||||
if (t.isObject) {
|
||||
if (s.isFutureOr) {
|
||||
return isSubtype(s.as<_FutureOrType>().typeArgument, sEnv, t, tEnv);
|
||||
}
|
||||
|
@ -984,7 +1050,7 @@ class _TypeUniverse {
|
|||
// Left Promoted Variable does not apply at runtime.
|
||||
|
||||
// Function Type / Function:
|
||||
if (s.isFunction && isFunctionType(t)) {
|
||||
if (s.isFunction && t.isAbstractFunction) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1000,9 +1066,8 @@ class _TypeUniverse {
|
|||
}
|
||||
|
||||
// Records are subtypes of the `Record` type:
|
||||
if (s.isRecord && t.isInterface) {
|
||||
final tInterfaceType = t.as<_InterfaceType>();
|
||||
return tInterfaceType.classId == ClassID.cidRecord;
|
||||
if (s.isRecord && t.isAbstractRecord) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Interface Compositionality + Super-Interface:
|
||||
|
|
Loading…
Reference in New Issue
Block a user