1
0
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:
Aske Simon Christensen 2023-08-11 09:08:32 +00:00 committed by Commit Queue
parent 166c5d7b41
commit 0c1bb28ad4
7 changed files with 142 additions and 35 deletions

View File

@ -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) {

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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)) {

View File

@ -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;

View File

@ -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: