mirror of
https://github.com/dart-lang/sdk
synced 2024-07-19 20:17:27 +00:00
[dart2wasm] Remove usage of type category table
The type category table is a O(number-of-classes) sized byte array that maps from class-id to either a type category (function, record, ...) or a masqueraded class-id. * for flute this table takes up around 1 KB. * this prevents from making concrete class-ids come before all abstract class ids After recent changes the core RTT implementation no longer involves masqueraded types (i.e. `<obj> is/as <type>` and `<type> <: <type>` queries don't trigger masquerading functionality) This CL removes the type category table, the special casing in the class-id assignment and the compiler support for building the table. Instead we move the logic to pure dart code, which can use normal `is` checks to perform its function. We add one optimization: The compiler will provide the class-id from which one only non-masqueraded classes come. This makes the masquerading function have a fast path. * We use `Wasm{TypedData,String}Base` marker interfaces i `dart:_internal` to check for wasm-backed implementations * We use `-Ddart.wasm.js_compatibility` to provide JSCM mode We add a test that actually exercises the 2 modes. Change-Id: I051c35b17878950402a1336df871a686b649f732 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/349641 Reviewed-by: Slava Egorov <vegorov@google.com> Commit-Queue: Martin Kustermann <kustermann@google.com>
This commit is contained in:
parent
5b980d6995
commit
0bc92a4333
|
@ -7,7 +7,6 @@ import 'dart:math';
|
||||||
import 'package:dart2wasm/translator.dart';
|
import 'package:dart2wasm/translator.dart';
|
||||||
|
|
||||||
import 'package:kernel/ast.dart';
|
import 'package:kernel/ast.dart';
|
||||||
import 'package:kernel/library_index.dart';
|
|
||||||
|
|
||||||
import 'package:wasm_builder/wasm_builder.dart' as w;
|
import 'package:wasm_builder/wasm_builder.dart' as w;
|
||||||
|
|
||||||
|
@ -217,25 +216,25 @@ class ClassInfoCollector {
|
||||||
/// shape class with that many fields.
|
/// shape class with that many fields.
|
||||||
final Map<int, w.StructType> _recordStructs = {};
|
final Map<int, w.StructType> _recordStructs = {};
|
||||||
|
|
||||||
/// Masquerades for implementation classes. For each entry of the map, all
|
/// Any subtype of these needs to masqueraded (modulo special js-compatibility
|
||||||
/// subtypes of the key masquerade as the value.
|
/// mode semantics) or specially treated due to being from a different type
|
||||||
late final Map<Class, Class> _masquerades = _computeMasquerades();
|
/// (e.g. record, closure)
|
||||||
|
late final Set<Class> masqueraded = _computeMasquerades();
|
||||||
|
|
||||||
/// Masqueraded types are mapped to these classes.
|
Set<Class> _computeMasquerades() {
|
||||||
late final Set<Class> masqueradeValues = _masquerades.values.toSet();
|
final values = {
|
||||||
|
translator.coreTypes.boolClass,
|
||||||
Map<Class, Class> _computeMasquerades() {
|
translator.coreTypes.intClass,
|
||||||
final map = {
|
translator.coreTypes.doubleClass,
|
||||||
translator.coreTypes.boolClass: translator.coreTypes.boolClass,
|
translator.coreTypes.stringClass,
|
||||||
translator.coreTypes.intClass: translator.coreTypes.intClass,
|
translator.coreTypes.functionClass,
|
||||||
translator.coreTypes.doubleClass: translator.coreTypes.doubleClass,
|
translator.coreTypes.recordClass,
|
||||||
translator.coreTypes.stringClass: translator.coreTypes.stringClass,
|
translator.index.getClass("dart:core", "_Type"),
|
||||||
translator.index.getClass("dart:core", "_Type"):
|
translator.index.getClass("dart:core", "_ListBase"),
|
||||||
translator.coreTypes.typeClass,
|
|
||||||
translator.index.getClass("dart:core", "_ListBase"):
|
|
||||||
translator.coreTypes.listClass
|
|
||||||
};
|
};
|
||||||
for (final name in const <String>[
|
for (final name in const <String>[
|
||||||
|
"ByteBuffer",
|
||||||
|
"ByteData",
|
||||||
"Int8List",
|
"Int8List",
|
||||||
"Uint8List",
|
"Uint8List",
|
||||||
"Uint8ClampedList",
|
"Uint8ClampedList",
|
||||||
|
@ -252,49 +251,9 @@ class ClassInfoCollector {
|
||||||
"Float64x2List",
|
"Float64x2List",
|
||||||
]) {
|
]) {
|
||||||
final Class? cls = translator.index.tryGetClass("dart:typed_data", name);
|
final Class? cls = translator.index.tryGetClass("dart:typed_data", name);
|
||||||
if (cls != null) {
|
if (cls != null) values.add(cls);
|
||||||
map[cls] = cls;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return map;
|
return values;
|
||||||
}
|
|
||||||
|
|
||||||
late final Set<Class> _neverMasquerades = _computeNeverMasquerades();
|
|
||||||
|
|
||||||
/// These types switch from properly reified non-masquerading types in regular
|
|
||||||
/// Dart2Wasm mode to masquerading types in js compatibility mode.
|
|
||||||
final Set<String> jsCompatibilityTypes = {
|
|
||||||
"JSStringImpl",
|
|
||||||
"JSArrayBufferImpl",
|
|
||||||
"JSDataViewImpl",
|
|
||||||
"JSInt8ArrayImpl",
|
|
||||||
"JSUint8ArrayImpl",
|
|
||||||
"JSUint8ClampedArrayImpl",
|
|
||||||
"JSInt16ArrayImpl",
|
|
||||||
"JSUint16ArrayImpl",
|
|
||||||
"JSInt32ArrayImpl",
|
|
||||||
"JSInt32x4ArrayImpl",
|
|
||||||
"JSUint32ArrayImpl",
|
|
||||||
"JSBigUint64ArrayImpl",
|
|
||||||
"JSBigInt64ArrayImpl",
|
|
||||||
"JSFloat32ArrayImpl",
|
|
||||||
"JSFloat32x4ArrayImpl",
|
|
||||||
"JSFloat64ArrayImpl",
|
|
||||||
"JSFloat64x2ArrayImpl",
|
|
||||||
};
|
|
||||||
|
|
||||||
Set<Class> _computeNeverMasquerades() {
|
|
||||||
// The JS types do not masquerade in regular Dart2Wasm, but they aren't
|
|
||||||
// always used so we have to construct this set programmatically.
|
|
||||||
final jsTypesLibraryIndex =
|
|
||||||
LibraryIndex(translator.component, ["dart:_js_types"]);
|
|
||||||
final neverMasquerades = [
|
|
||||||
if (!translator.options.jsCompatibility) ...jsCompatibilityTypes,
|
|
||||||
]
|
|
||||||
.map((name) => jsTypesLibraryIndex.tryGetClass("dart:_js_types", name))
|
|
||||||
.toSet();
|
|
||||||
neverMasquerades.removeWhere((c) => c == null);
|
|
||||||
return neverMasquerades.cast<Class>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wasm field type for fields with type [_Type]. Fields of this type are
|
/// Wasm field type for fields with type [_Type]. Fields of this type are
|
||||||
|
@ -341,14 +300,14 @@ class ClassInfoCollector {
|
||||||
}
|
}
|
||||||
|
|
||||||
// In the Wasm type hierarchy, Object, bool and num sit directly below
|
// In the Wasm type hierarchy, Object, bool and num sit directly below
|
||||||
// the Top type. The implementation classes _StringBase and _Type sit
|
// the Top type. The implementation classes WasmStringBase and _Type sit
|
||||||
// directly below the public classes they implement.
|
// directly below the public classes they implement.
|
||||||
// All other classes sit below their superclass.
|
// All other classes sit below their superclass.
|
||||||
ClassInfo superInfo = cls == translator.coreTypes.boolClass ||
|
ClassInfo superInfo = cls == translator.coreTypes.boolClass ||
|
||||||
cls == translator.coreTypes.numClass
|
cls == translator.coreTypes.numClass
|
||||||
? topInfo
|
? topInfo
|
||||||
: (!translator.options.jsCompatibility &&
|
: (!translator.options.jsCompatibility &&
|
||||||
cls == translator.stringBaseClass) ||
|
cls == translator.wasmStringBaseClass) ||
|
||||||
cls == translator.typeClass
|
cls == translator.typeClass
|
||||||
? translator.classInfo[cls.implementedTypes.single.classNode]!
|
? translator.classInfo[cls.implementedTypes.single.classNode]!
|
||||||
: translator.classInfo[superclass]!;
|
: translator.classInfo[superclass]!;
|
||||||
|
@ -390,29 +349,6 @@ class ClassInfoCollector {
|
||||||
translator.classes[classId] = info;
|
translator.classes[classId] = info;
|
||||||
translator.classInfo[cls] = info;
|
translator.classInfo[cls] = info;
|
||||||
translator.classForHeapType.putIfAbsent(info.struct, () => info!);
|
translator.classForHeapType.putIfAbsent(info.struct, () => info!);
|
||||||
|
|
||||||
ClassInfo? computeMasquerade() {
|
|
||||||
if (_neverMasquerades.contains(cls)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (info!.superInfo?.masquerade != null) {
|
|
||||||
return info.superInfo!.masquerade;
|
|
||||||
}
|
|
||||||
for (Supertype implemented in cls.implementedTypes) {
|
|
||||||
ClassInfo? implementedMasquerade =
|
|
||||||
translator.classInfo[implemented.classNode]!.masquerade;
|
|
||||||
if (implementedMasquerade != null) {
|
|
||||||
return implementedMasquerade;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Class? selfMasquerade = _masquerades[cls];
|
|
||||||
if (selfMasquerade != null) {
|
|
||||||
return translator.classInfo[selfMasquerade]!;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
info.masquerade = computeMasquerade();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _createStructForRecordClass(Map<Class, int> classIds, Class cls) {
|
void _createStructForRecordClass(Map<Class, int> classIds, Class cls) {
|
||||||
|
@ -505,7 +441,7 @@ class ClassInfoCollector {
|
||||||
const int firstClassId = 1;
|
const int firstClassId = 1;
|
||||||
|
|
||||||
translator.classIdNumbering =
|
translator.classIdNumbering =
|
||||||
ClassIdNumbering._number(translator, masqueradeValues, firstClassId);
|
ClassIdNumbering._number(translator, masqueraded, firstClassId);
|
||||||
final classIds = translator.classIdNumbering.classIds;
|
final classIds = translator.classIdNumbering.classIds;
|
||||||
final dfsOrder = translator.classIdNumbering.dfsOrder;
|
final dfsOrder = translator.classIdNumbering.dfsOrder;
|
||||||
|
|
||||||
|
@ -560,12 +496,18 @@ class ClassIdNumbering {
|
||||||
final Map<Class, List<Class>> _subclasses;
|
final Map<Class, List<Class>> _subclasses;
|
||||||
final Map<Class, List<Class>> _implementors;
|
final Map<Class, List<Class>> _implementors;
|
||||||
final List<Range> _concreteSubclassIdRanges;
|
final List<Range> _concreteSubclassIdRanges;
|
||||||
|
final Set<Class> _masqueraded;
|
||||||
|
|
||||||
final List<Class> dfsOrder;
|
final List<Class> dfsOrder;
|
||||||
final Map<Class, int> classIds;
|
final Map<Class, int> classIds;
|
||||||
|
|
||||||
ClassIdNumbering._(this._subclasses, this._implementors,
|
ClassIdNumbering._(
|
||||||
this._concreteSubclassIdRanges, this.dfsOrder, this.classIds);
|
this._subclasses,
|
||||||
|
this._implementors,
|
||||||
|
this._concreteSubclassIdRanges,
|
||||||
|
this._masqueraded,
|
||||||
|
this.dfsOrder,
|
||||||
|
this.classIds);
|
||||||
|
|
||||||
final Map<Class, Set<Class>> _transitiveImplementors = {};
|
final Map<Class, Set<Class>> _transitiveImplementors = {};
|
||||||
Set<Class> _getTransitiveImplementors(Class klass) {
|
Set<Class> _getTransitiveImplementors(Class klass) {
|
||||||
|
@ -611,8 +553,19 @@ class ClassIdNumbering {
|
||||||
return _concreteClassIdRanges[klass] = ranges;
|
return _concreteClassIdRanges[klass] = ranges;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
late final int firstNonMasqueradedInterfaceClassCid = (() {
|
||||||
|
int lastMasqueradedClassId = 0;
|
||||||
|
for (final cls in _masqueraded) {
|
||||||
|
final ranges = getConcreteClassIdRanges(cls);
|
||||||
|
if (ranges.isNotEmpty) {
|
||||||
|
lastMasqueradedClassId = max(lastMasqueradedClassId, ranges.last.end);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return lastMasqueradedClassId + 1;
|
||||||
|
})();
|
||||||
|
|
||||||
static ClassIdNumbering _number(
|
static ClassIdNumbering _number(
|
||||||
Translator translator, Set<Class> masqueradeValues, int firstClassId) {
|
Translator translator, Set<Class> masqueraded, int firstClassId) {
|
||||||
// Make graph from class to its subclasses.
|
// Make graph from class to its subclasses.
|
||||||
late final Class root;
|
late final Class root;
|
||||||
final subclasses = <Class, List<Class>>{};
|
final subclasses = <Class, List<Class>>{};
|
||||||
|
@ -645,7 +598,8 @@ class ClassIdNumbering {
|
||||||
final fixedOrder = <Class, int>{
|
final fixedOrder = <Class, int>{
|
||||||
translator.coreTypes.boolClass: -10,
|
translator.coreTypes.boolClass: -10,
|
||||||
translator.coreTypes.numClass: -9,
|
translator.coreTypes.numClass: -9,
|
||||||
if (!translator.options.jsCompatibility) translator.stringBaseClass: -8,
|
if (!translator.options.jsCompatibility)
|
||||||
|
translator.wasmStringBaseClass: -8,
|
||||||
translator.jsStringClass: -7,
|
translator.jsStringClass: -7,
|
||||||
translator.typeClass: -6,
|
translator.typeClass: -6,
|
||||||
translator.listBaseClass: -5,
|
translator.listBaseClass: -5,
|
||||||
|
@ -657,7 +611,7 @@ class ClassIdNumbering {
|
||||||
|
|
||||||
final importUri = klass.enclosingLibrary.importUri.toString();
|
final importUri = klass.enclosingLibrary.importUri.toString();
|
||||||
if (importUri.startsWith('dart:')) {
|
if (importUri.startsWith('dart:')) {
|
||||||
if (masqueradeValues.contains(klass)) return -1;
|
if (masqueraded.contains(klass)) return -1;
|
||||||
// Bundle the typed data and collection together, they may not have
|
// Bundle the typed data and collection together, they may not have
|
||||||
// common base class except for `Object` but most of them have similar
|
// common base class except for `Object` but most of them have similar
|
||||||
// selectors.
|
// selectors.
|
||||||
|
@ -700,26 +654,8 @@ class ClassIdNumbering {
|
||||||
firstClassId + classCount, Range.empty(),
|
firstClassId + classCount, Range.empty(),
|
||||||
growable: false);
|
growable: false);
|
||||||
|
|
||||||
// TODO: We may consider removing the type category table. But until we do
|
int nextConcreteClassId = firstClassId;
|
||||||
// we have to make sure that all masqueraded types that concrete classes may
|
int nextAbstractClassId = firstClassId + concreteClassCount;
|
||||||
// be mapped to have class ids that are <=255.
|
|
||||||
//
|
|
||||||
// We still want to maintain that all classes's concrete subclasses form a
|
|
||||||
// single continious class-id range.
|
|
||||||
//
|
|
||||||
// => So we move the abstract masqeraded classes before all the concrete
|
|
||||||
// ones.
|
|
||||||
int nextAbstractClassId = firstClassId;
|
|
||||||
for (Class cls in masqueradeValues) {
|
|
||||||
if (cls.isAbstract) {
|
|
||||||
assert(classIds[cls] == null);
|
|
||||||
classIds[cls] = nextAbstractClassId++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
int nextConcreteClassId = nextAbstractClassId;
|
|
||||||
nextAbstractClassId = firstClassId +
|
|
||||||
(nextAbstractClassId - firstClassId) +
|
|
||||||
concreteClassCount;
|
|
||||||
dfs(root, (Class cls) {
|
dfs(root, (Class cls) {
|
||||||
dfsOrder.add(cls);
|
dfsOrder.add(cls);
|
||||||
if (cls.isAbstract) {
|
if (cls.isAbstract) {
|
||||||
|
@ -740,11 +676,9 @@ class ClassIdNumbering {
|
||||||
concreteSubclassRanges[classIds[cls]!] = range;
|
concreteSubclassRanges[classIds[cls]!] = range;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
for (Class cls in masqueradeValues) {
|
|
||||||
assert(classIds[cls]! <= 255);
|
return ClassIdNumbering._(subclasses, implementors, concreteSubclassRanges,
|
||||||
}
|
masqueraded, dfsOrder, classIds);
|
||||||
return ClassIdNumbering._(
|
|
||||||
subclasses, implementors, concreteSubclassRanges, dfsOrder, classIds);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3803,3 +3803,46 @@ enum _VirtualCallKind {
|
||||||
|
|
||||||
bool get isSetter => this == _VirtualCallKind.Set;
|
bool get isSetter => this == _VirtualCallKind.Set;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension MacroAssembler on w.InstructionsBuilder {
|
||||||
|
// Expects there to be a i32 on the stack, will consume it and leave
|
||||||
|
// true/false on the stack.
|
||||||
|
void emitClassIdRangeCheck(CodeGenerator codeGen, List<Range> ranges) {
|
||||||
|
if (ranges.isEmpty) {
|
||||||
|
drop();
|
||||||
|
i32_const(0);
|
||||||
|
} else if (ranges.length == 1) {
|
||||||
|
final range = ranges[0];
|
||||||
|
|
||||||
|
i32_const(range.start);
|
||||||
|
if (range.length == 1) {
|
||||||
|
i32_eq();
|
||||||
|
} else {
|
||||||
|
i32_sub();
|
||||||
|
i32_const(range.length);
|
||||||
|
i32_lt_u();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
w.Local idLocal = codeGen.addLocal(w.NumType.i32);
|
||||||
|
local_set(idLocal);
|
||||||
|
w.Label done = block(const [], const [w.NumType.i32]);
|
||||||
|
i32_const(1);
|
||||||
|
|
||||||
|
for (Range range in ranges) {
|
||||||
|
local_get(idLocal);
|
||||||
|
i32_const(range.start);
|
||||||
|
if (range.length == 1) {
|
||||||
|
i32_eq();
|
||||||
|
} else {
|
||||||
|
i32_sub();
|
||||||
|
i32_const(range.length);
|
||||||
|
i32_lt_u();
|
||||||
|
}
|
||||||
|
br_if(done);
|
||||||
|
}
|
||||||
|
drop();
|
||||||
|
i32_const(0);
|
||||||
|
end(); // done
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ class WasmCompilerOptions {
|
||||||
String outputFile;
|
String outputFile;
|
||||||
String? depFile;
|
String? depFile;
|
||||||
String? outputJSRuntimeFile;
|
String? outputJSRuntimeFile;
|
||||||
Map<String, String> environment = const {};
|
Map<String, String> environment = {};
|
||||||
Map<fe.ExperimentalFlag, bool> feExperimentalFlags = const {};
|
Map<fe.ExperimentalFlag, bool> feExperimentalFlags = const {};
|
||||||
String? multiRootScheme;
|
String? multiRootScheme;
|
||||||
List<Uri> multiRoots = const [];
|
List<Uri> multiRoots = const [];
|
||||||
|
|
|
@ -36,9 +36,10 @@ final List<Option> options = [
|
||||||
defaultsTo: _d.translatorOptions.printKernel),
|
defaultsTo: _d.translatorOptions.printKernel),
|
||||||
Flag("print-wasm", (o, value) => o.translatorOptions.printWasm = value,
|
Flag("print-wasm", (o, value) => o.translatorOptions.printWasm = value,
|
||||||
defaultsTo: _d.translatorOptions.printWasm),
|
defaultsTo: _d.translatorOptions.printWasm),
|
||||||
Flag("js-compatibility",
|
Flag("js-compatibility", (o, value) {
|
||||||
(o, value) => o.translatorOptions.jsCompatibility = value,
|
o.translatorOptions.jsCompatibility = value;
|
||||||
defaultsTo: _d.translatorOptions.jsCompatibility),
|
o.environment['dart.wasm.js_compatibility'] = 'true';
|
||||||
|
}, defaultsTo: _d.translatorOptions.jsCompatibility),
|
||||||
Flag(
|
Flag(
|
||||||
"enable-asserts", (o, value) => o.translatorOptions.enableAsserts = value,
|
"enable-asserts", (o, value) => o.translatorOptions.enableAsserts = value,
|
||||||
defaultsTo: _d.translatorOptions.enableAsserts),
|
defaultsTo: _d.translatorOptions.enableAsserts),
|
||||||
|
@ -73,7 +74,7 @@ final List<Option> options = [
|
||||||
IntMultiOption(
|
IntMultiOption(
|
||||||
"watch", (o, values) => o.translatorOptions.watchPoints = values),
|
"watch", (o, values) => o.translatorOptions.watchPoints = values),
|
||||||
StringMultiOption(
|
StringMultiOption(
|
||||||
"define", (o, values) => o.environment = processEnvironment(values),
|
"define", (o, values) => o.environment.addAll(processEnvironment(values)),
|
||||||
abbr: "D"),
|
abbr: "D"),
|
||||||
StringMultiOption(
|
StringMultiOption(
|
||||||
"enable-experiment",
|
"enable-experiment",
|
||||||
|
|
|
@ -404,24 +404,35 @@ class Intrinsifier {
|
||||||
/// Generate inline code for a [StaticGet] if the member is an inlined
|
/// Generate inline code for a [StaticGet] if the member is an inlined
|
||||||
/// intrinsic.
|
/// intrinsic.
|
||||||
w.ValueType? generateStaticGetterIntrinsic(StaticGet node) {
|
w.ValueType? generateStaticGetterIntrinsic(StaticGet node) {
|
||||||
Member target = node.target;
|
final Member target = node.target;
|
||||||
|
final Class? cls = target.enclosingClass;
|
||||||
|
|
||||||
// ClassID getters
|
// ClassID getters
|
||||||
String? libAndClassName = translator.getPragma(target, "wasm:class-id");
|
if (cls?.name == 'ClassID') {
|
||||||
if (libAndClassName != null) {
|
final libAndClassName = translator.getPragma(target, "wasm:class-id");
|
||||||
List<String> libAndClassNameParts = libAndClassName.split("#");
|
if (libAndClassName != null) {
|
||||||
final String lib = libAndClassNameParts[0];
|
List<String> libAndClassNameParts = libAndClassName.split("#");
|
||||||
final String className = libAndClassNameParts[1];
|
final String lib = libAndClassNameParts[0];
|
||||||
Class cls = translator.libraries
|
final String className = libAndClassNameParts[1];
|
||||||
.firstWhere((l) => l.name == lib && l.importUri.scheme == 'dart',
|
Class cls = translator.libraries
|
||||||
orElse: () => throw 'Library $lib not found (${target.location})')
|
.firstWhere((l) => l.name == lib && l.importUri.scheme == 'dart',
|
||||||
.classes
|
orElse: () =>
|
||||||
.firstWhere((c) => c.name == className,
|
throw 'Library $lib not found (${target.location})')
|
||||||
orElse: () => throw 'Class $className not found in library $lib '
|
.classes
|
||||||
'(${target.location})');
|
.firstWhere((c) => c.name == className,
|
||||||
int classId = translator.classInfo[cls]!.classId;
|
orElse: () =>
|
||||||
b.i64_const(classId);
|
throw 'Class $className not found in library $lib '
|
||||||
return w.NumType.i64;
|
'(${target.location})');
|
||||||
|
int classId = translator.classInfo[cls]!.classId;
|
||||||
|
b.i64_const(classId);
|
||||||
|
return w.NumType.i64;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target.name.text == 'firstNonMasqueradedInterfaceClassCid') {
|
||||||
|
b.i64_const(
|
||||||
|
translator.classIdNumbering.firstNonMasqueradedInterfaceClassCid);
|
||||||
|
return w.NumType.i64;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// nullptr
|
// nullptr
|
||||||
|
@ -440,35 +451,6 @@ class Intrinsifier {
|
||||||
b.i32_const(0);
|
b.i32_const(0);
|
||||||
return w.NumType.i32;
|
return w.NumType.i32;
|
||||||
}
|
}
|
||||||
if (node.target.enclosingLibrary == translator.coreTypes.coreLibrary) {
|
|
||||||
switch (node.target.name.text) {
|
|
||||||
case "_typeCategoryAbstractClass":
|
|
||||||
translator.types.typeCategoryTable;
|
|
||||||
b.i32_const(translator.types.typeCategoryAbstractClass);
|
|
||||||
return w.NumType.i32;
|
|
||||||
case "_typeCategoryObject":
|
|
||||||
translator.types.typeCategoryTable;
|
|
||||||
b.i32_const(translator.types.typeCategoryObject);
|
|
||||||
return w.NumType.i32;
|
|
||||||
case "_typeCategoryFunction":
|
|
||||||
translator.types.typeCategoryTable;
|
|
||||||
b.i32_const(translator.types.typeCategoryFunction);
|
|
||||||
return w.NumType.i32;
|
|
||||||
case "_typeCategoryRecord":
|
|
||||||
translator.types.typeCategoryTable;
|
|
||||||
b.i32_const(translator.types.typeCategoryRecord);
|
|
||||||
return w.NumType.i32;
|
|
||||||
case "_typeCategoryNotMasqueraded":
|
|
||||||
translator.types.typeCategoryTable;
|
|
||||||
b.i32_const(translator.types.typeCategoryNotMasqueraded);
|
|
||||||
return w.NumType.i32;
|
|
||||||
|
|
||||||
case "_typeCategoryTable":
|
|
||||||
translator.types.typeCategoryTable;
|
|
||||||
b.global_get(translator.types.typeCategoryTable);
|
|
||||||
return translator.types.typeCategoryTable.type.type;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
@ -659,16 +641,38 @@ class Intrinsifier {
|
||||||
return translator.types.makeTypeRulesSubstitutions(b);
|
return translator.types.makeTypeRulesSubstitutions(b);
|
||||||
case "_getTypeNames":
|
case "_getTypeNames":
|
||||||
return translator.types.makeTypeNames(b);
|
return translator.types.makeTypeNames(b);
|
||||||
case "_isRecordInstance":
|
case "_isObjectClassId":
|
||||||
Expression o = node.arguments.positional.single;
|
final classId = node.arguments.positional.single;
|
||||||
b.global_get(translator.types.typeCategoryTable);
|
|
||||||
codeGen.wrap(o, translator.topInfo.nonNullableType);
|
final objectClassId = translator
|
||||||
b.struct_get(translator.topInfo.struct, FieldIndex.classId);
|
.classIdNumbering.classIds[translator.coreTypes.objectClass]!;
|
||||||
b.array_get_u(
|
|
||||||
(translator.types.typeCategoryTable.type.type as w.RefType)
|
codeGen.wrap(classId, w.NumType.i64);
|
||||||
.heapType as w.ArrayType);
|
b.i32_wrap_i64();
|
||||||
b.i32_const(translator.types.typeCategoryRecord);
|
b.emitClassIdRangeCheck(
|
||||||
b.i32_eq();
|
codeGen, [Range(objectClassId, objectClassId)]);
|
||||||
|
return w.NumType.i32;
|
||||||
|
case "_isClosureClassId":
|
||||||
|
final classId = node.arguments.positional.single;
|
||||||
|
|
||||||
|
final ranges = translator.classIdNumbering
|
||||||
|
.getConcreteClassIdRanges(translator.coreTypes.functionClass);
|
||||||
|
assert(ranges.length <= 1);
|
||||||
|
|
||||||
|
codeGen.wrap(classId, w.NumType.i64);
|
||||||
|
b.i32_wrap_i64();
|
||||||
|
b.emitClassIdRangeCheck(codeGen, ranges);
|
||||||
|
return w.NumType.i32;
|
||||||
|
case "_isRecordClassId":
|
||||||
|
final classId = node.arguments.positional.single;
|
||||||
|
|
||||||
|
final ranges = translator.classIdNumbering
|
||||||
|
.getConcreteClassIdRanges(translator.coreTypes.recordClass);
|
||||||
|
assert(ranges.length <= 1);
|
||||||
|
|
||||||
|
codeGen.wrap(classId, w.NumType.i64);
|
||||||
|
b.i32_wrap_i64();
|
||||||
|
b.emitClassIdRangeCheck(codeGen, ranges);
|
||||||
return w.NumType.i32;
|
return w.NumType.i32;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,6 +50,8 @@ mixin KernelNodes {
|
||||||
index.getClass("dart:core", "_GrowableList");
|
index.getClass("dart:core", "_GrowableList");
|
||||||
late final Class immutableListClass =
|
late final Class immutableListClass =
|
||||||
index.getClass("dart:core", "_ImmutableList");
|
index.getClass("dart:core", "_ImmutableList");
|
||||||
|
late final Class wasmStringBaseClass =
|
||||||
|
index.getClass("dart:_internal", "WasmStringBase");
|
||||||
late final Class stringBaseClass =
|
late final Class stringBaseClass =
|
||||||
index.getClass("dart:_string", "StringBase");
|
index.getClass("dart:_string", "StringBase");
|
||||||
late final Class oneByteStringClass =
|
late final Class oneByteStringClass =
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
// BSD-style license that can be found in the LICENSE file.
|
||||||
|
|
||||||
import 'dart:math' show max;
|
import 'dart:math' show max;
|
||||||
import 'dart:typed_data' show Uint8List;
|
|
||||||
|
|
||||||
import 'package:dart2wasm/class_info.dart';
|
import 'package:dart2wasm/class_info.dart';
|
||||||
import 'package:dart2wasm/code_generator.dart';
|
import 'package:dart2wasm/code_generator.dart';
|
||||||
|
@ -101,9 +100,6 @@ class Types {
|
||||||
/// parameter index range of their corresponding function type.
|
/// parameter index range of their corresponding function type.
|
||||||
Map<StructuralParameter, int> functionTypeParameterIndex = Map.identity();
|
Map<StructuralParameter, int> functionTypeParameterIndex = Map.identity();
|
||||||
|
|
||||||
/// An `i8` array of type category values, indexed by class ID.
|
|
||||||
late final w.Global typeCategoryTable = _buildTypeCategoryTable();
|
|
||||||
|
|
||||||
Types(this.translator);
|
Types(this.translator);
|
||||||
|
|
||||||
w.ValueType classAndFieldToType(Class cls, int fieldIndex) =>
|
w.ValueType classAndFieldToType(Class cls, int fieldIndex) =>
|
||||||
|
@ -288,77 +284,6 @@ class Types {
|
||||||
return typeNamesType;
|
return typeNamesType;
|
||||||
}
|
}
|
||||||
|
|
||||||
// None of these integers belong to masqueraded types like Uint8List.
|
|
||||||
late final int typeCategoryAbstractClass;
|
|
||||||
late final int typeCategoryObject;
|
|
||||||
late final int typeCategoryFunction;
|
|
||||||
late final int typeCategoryRecord;
|
|
||||||
late final int typeCategoryNotMasqueraded;
|
|
||||||
|
|
||||||
/// Build a global array of byte values used to categorize runtime types.
|
|
||||||
w.Global _buildTypeCategoryTable() {
|
|
||||||
// Find 5 class ids whose classes are not masqueraded.
|
|
||||||
final typeCategories = <int>[];
|
|
||||||
for (int i = 0; i < translator.classes.length; i++) {
|
|
||||||
final info = translator.classes[i];
|
|
||||||
if (!translator.classInfoCollector.masqueradeValues.contains(info.cls)) {
|
|
||||||
typeCategories.add(i);
|
|
||||||
if (typeCategories.length == 5) break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assert(typeCategories.length == 5 && typeCategories.last <= 255);
|
|
||||||
typeCategoryAbstractClass = typeCategories[0];
|
|
||||||
typeCategoryObject = typeCategories[1];
|
|
||||||
typeCategoryFunction = typeCategories[2];
|
|
||||||
typeCategoryRecord = typeCategories[3];
|
|
||||||
typeCategoryNotMasqueraded = typeCategories[4];
|
|
||||||
|
|
||||||
Set<Class> recordClasses = Set.from(translator.recordClasses.values);
|
|
||||||
Uint8List table = Uint8List(translator.classes.length);
|
|
||||||
for (int i = 0; i < translator.classes.length; i++) {
|
|
||||||
ClassInfo info = translator.classes[i];
|
|
||||||
ClassInfo? masquerade = info.masquerade;
|
|
||||||
Class? cls = info.cls;
|
|
||||||
int category;
|
|
||||||
if (cls == null || cls.isAbstract) {
|
|
||||||
category = typeCategoryAbstractClass;
|
|
||||||
} else if (cls == coreTypes.objectClass) {
|
|
||||||
category = typeCategoryObject;
|
|
||||||
} else if (cls == translator.closureClass) {
|
|
||||||
category = typeCategoryFunction;
|
|
||||||
} else if (recordClasses.contains(cls)) {
|
|
||||||
category = typeCategoryRecord;
|
|
||||||
} else if (masquerade == null || masquerade.classId == i) {
|
|
||||||
category = typeCategoryNotMasqueraded;
|
|
||||||
} else {
|
|
||||||
// Masqueraded class
|
|
||||||
assert(cls.enclosingLibrary.importUri.scheme == "dart");
|
|
||||||
assert(!typeCategories.contains(masquerade.classId));
|
|
||||||
assert(masquerade.classId <= 255);
|
|
||||||
category = masquerade.classId;
|
|
||||||
}
|
|
||||||
table[i] = category;
|
|
||||||
}
|
|
||||||
|
|
||||||
final segment = translator.m.dataSegments.define(table);
|
|
||||||
w.ArrayType arrayType = translator.arrayTypeForDartType(
|
|
||||||
InterfaceType(translator.wasmI8Class, Nullability.nonNullable));
|
|
||||||
final global = translator.m.globals
|
|
||||||
.define(w.GlobalType(w.RefType.def(arrayType, nullable: false)));
|
|
||||||
// Initialize the global to a dummy array, since `array.new_data` is not
|
|
||||||
// a constant instruction and thus can't be used in the initializer.
|
|
||||||
global.initializer.array_new_fixed(arrayType, 0);
|
|
||||||
global.initializer.end();
|
|
||||||
// Create the actual table in the init function.
|
|
||||||
final b = translator.initFunction.body;
|
|
||||||
b.i32_const(0);
|
|
||||||
b.i32_const(table.length);
|
|
||||||
b.array_new_data(arrayType, segment);
|
|
||||||
b.global_set(global);
|
|
||||||
|
|
||||||
return global;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _isTypeConstant(DartType type) {
|
bool _isTypeConstant(DartType type) {
|
||||||
return type is DynamicType ||
|
return type is DynamicType ||
|
||||||
type is VoidType ||
|
type is VoidType ||
|
||||||
|
@ -727,44 +652,8 @@ class Types {
|
||||||
} else {
|
} else {
|
||||||
final ranges =
|
final ranges =
|
||||||
translator.classIdNumbering.getConcreteClassIdRanges(interfaceClass);
|
translator.classIdNumbering.getConcreteClassIdRanges(interfaceClass);
|
||||||
if (ranges.isEmpty) {
|
b.struct_get(translator.topInfo.struct, FieldIndex.classId);
|
||||||
b.drop();
|
b.emitClassIdRangeCheck(codeGen, ranges);
|
||||||
b.i32_const(0);
|
|
||||||
} else if (ranges.length == 1) {
|
|
||||||
final range = ranges[0];
|
|
||||||
|
|
||||||
b.struct_get(translator.topInfo.struct, FieldIndex.classId);
|
|
||||||
b.i32_const(range.start);
|
|
||||||
if (range.length == 1) {
|
|
||||||
b.i32_eq();
|
|
||||||
} else {
|
|
||||||
b.i32_sub();
|
|
||||||
b.i32_const(range.length);
|
|
||||||
b.i32_lt_u();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
w.Local idLocal = codeGen.addLocal(w.NumType.i32);
|
|
||||||
b.struct_get(translator.topInfo.struct, FieldIndex.classId);
|
|
||||||
b.local_set(idLocal);
|
|
||||||
w.Label done = b.block(const [], const [w.NumType.i32]);
|
|
||||||
b.i32_const(1);
|
|
||||||
|
|
||||||
for (Range range in ranges) {
|
|
||||||
b.local_get(idLocal);
|
|
||||||
b.i32_const(range.start);
|
|
||||||
if (range.length == 1) {
|
|
||||||
b.i32_eq();
|
|
||||||
} else {
|
|
||||||
b.i32_sub();
|
|
||||||
b.i32_const(range.length);
|
|
||||||
b.i32_lt_u();
|
|
||||||
}
|
|
||||||
b.br_if(done);
|
|
||||||
}
|
|
||||||
b.drop();
|
|
||||||
b.i32_const(0);
|
|
||||||
b.end(); // done
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isPotentiallyNullable) {
|
if (isPotentiallyNullable) {
|
||||||
|
|
|
@ -26,14 +26,14 @@ class ClassID {
|
||||||
external static int get cid_Int8List;
|
external static int get cid_Int8List;
|
||||||
@pragma("wasm:class-id", "dart.typed_data#_Int8ArrayView")
|
@pragma("wasm:class-id", "dart.typed_data#_Int8ArrayView")
|
||||||
external static int get cidInt8ArrayView;
|
external static int get cidInt8ArrayView;
|
||||||
@pragma("wasm:class-id", "dart.core#Object")
|
|
||||||
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 cidFuture;
|
||||||
@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#_Closure")
|
@pragma("wasm:class-id", "dart.core#_Closure")
|
||||||
external static int get cid_Closure;
|
external static int get cid_Closure;
|
||||||
|
@pragma("wasm:class-id", "dart.core#List")
|
||||||
|
external static int get cidList;
|
||||||
@pragma("wasm:class-id", "dart.core#_List")
|
@pragma("wasm:class-id", "dart.core#_List")
|
||||||
external static int get cidFixedLengthList;
|
external static int get cidFixedLengthList;
|
||||||
@pragma("wasm:class-id", "dart.core#_ListBase")
|
@pragma("wasm:class-id", "dart.core#_ListBase")
|
||||||
|
@ -69,6 +69,10 @@ class ClassID {
|
||||||
@pragma("wasm:class-id", "dart.core#_NamedParameter")
|
@pragma("wasm:class-id", "dart.core#_NamedParameter")
|
||||||
external static int get cidNamedParameter;
|
external static int get cidNamedParameter;
|
||||||
|
|
||||||
|
// From this class id onwards, all concrete classes are interface classes and
|
||||||
|
// do not need to be masqueraded.
|
||||||
|
external static int get firstNonMasqueradedInterfaceClassCid;
|
||||||
|
|
||||||
// Dummy, only used by VM-specific hash table code.
|
// Dummy, only used by VM-specific hash table code.
|
||||||
static final int numPredefinedCids = 1;
|
static final int numPredefinedCids = 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,9 @@ import "dart:_internal"
|
||||||
makeFixedListUnmodifiable,
|
makeFixedListUnmodifiable,
|
||||||
makeListFixedLength,
|
makeListFixedLength,
|
||||||
patch,
|
patch,
|
||||||
unsafeCast;
|
unsafeCast,
|
||||||
|
WasmStringBase,
|
||||||
|
WasmTypedDataBase;
|
||||||
|
|
||||||
import "dart:_internal" as _internal show Symbol;
|
import "dart:_internal" as _internal show Symbol;
|
||||||
|
|
||||||
|
@ -45,7 +47,7 @@ import 'dart:convert' show Encoding, utf8;
|
||||||
|
|
||||||
import 'dart:math' show Random;
|
import 'dart:math' show Random;
|
||||||
|
|
||||||
import "dart:typed_data" show Uint8List, Uint16List;
|
import "dart:typed_data";
|
||||||
|
|
||||||
import 'dart:_object_helper';
|
import 'dart:_object_helper';
|
||||||
import 'dart:_string_helper';
|
import 'dart:_string_helper';
|
||||||
|
|
|
@ -36,6 +36,12 @@ class Lists {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Base class for any wasm-backed typed data implementation class.
|
||||||
|
abstract class WasmTypedDataBase {}
|
||||||
|
|
||||||
|
// Base class for any wasm-backed string implementation class.
|
||||||
|
abstract class WasmStringBase implements String {}
|
||||||
|
|
||||||
// This function can be used to skip implicit or explicit checked down casts in
|
// This function can be used to skip implicit or explicit checked down casts in
|
||||||
// the parts of the core library implementation where we know by construction
|
// the parts of the core library implementation where we know by construction
|
||||||
// the type of a value.
|
// the type of a value.
|
||||||
|
|
|
@ -10,8 +10,9 @@ import 'dart:_internal'
|
||||||
import 'dart:collection' show ListMixin;
|
import 'dart:collection' show ListMixin;
|
||||||
import 'dart:math' as math;
|
import 'dart:math' as math;
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
import 'dart:_internal' show WasmTypedDataBase;
|
||||||
|
|
||||||
final class NaiveInt32x4List
|
final class NaiveInt32x4List extends WasmTypedDataBase
|
||||||
with ListMixin<Int32x4>, FixedLengthListMixin<Int32x4>
|
with ListMixin<Int32x4>, FixedLengthListMixin<Int32x4>
|
||||||
implements Int32x4List {
|
implements Int32x4List {
|
||||||
final Int32List _storage;
|
final Int32List _storage;
|
||||||
|
@ -116,7 +117,7 @@ final class NaiveUnmodifiableInt32x4List extends NaiveInt32x4List
|
||||||
ByteBuffer get buffer => UnmodifiableByteBufferView(super.buffer);
|
ByteBuffer get buffer => UnmodifiableByteBufferView(super.buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
final class NaiveFloat32x4List
|
final class NaiveFloat32x4List extends WasmTypedDataBase
|
||||||
with ListMixin<Float32x4>, FixedLengthListMixin<Float32x4>
|
with ListMixin<Float32x4>, FixedLengthListMixin<Float32x4>
|
||||||
implements Float32x4List {
|
implements Float32x4List {
|
||||||
final Float32List _storage;
|
final Float32List _storage;
|
||||||
|
@ -222,7 +223,7 @@ final class NaiveUnmodifiableFloat32x4List extends NaiveFloat32x4List
|
||||||
ByteBuffer get buffer => UnmodifiableByteBufferView(super.buffer);
|
ByteBuffer get buffer => UnmodifiableByteBufferView(super.buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
final class NaiveFloat64x2List
|
final class NaiveFloat64x2List extends WasmTypedDataBase
|
||||||
with ListMixin<Float64x2>, FixedLengthListMixin<Float64x2>
|
with ListMixin<Float64x2>, FixedLengthListMixin<Float64x2>
|
||||||
implements Float64x2List {
|
implements Float64x2List {
|
||||||
final Float64List _storage;
|
final Float64List _storage;
|
||||||
|
@ -322,7 +323,7 @@ final class NaiveUnmodifiableFloat64x2List extends NaiveFloat64x2List
|
||||||
ByteBuffer get buffer => UnmodifiableByteBufferView(super.buffer);
|
ByteBuffer get buffer => UnmodifiableByteBufferView(super.buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
final class NaiveFloat32x4 implements Float32x4 {
|
final class NaiveFloat32x4 extends WasmTypedDataBase implements Float32x4 {
|
||||||
final double x;
|
final double x;
|
||||||
final double y;
|
final double y;
|
||||||
final double z;
|
final double z;
|
||||||
|
@ -614,7 +615,7 @@ final class NaiveFloat32x4 implements Float32x4 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final class NaiveFloat64x2 implements Float64x2 {
|
final class NaiveFloat64x2 extends WasmTypedDataBase implements Float64x2 {
|
||||||
final double x;
|
final double x;
|
||||||
final double y;
|
final double y;
|
||||||
|
|
||||||
|
@ -688,7 +689,7 @@ final class NaiveFloat64x2 implements Float64x2 {
|
||||||
Float64x2 sqrt() => NaiveFloat64x2._doubles(math.sqrt(x), math.sqrt(y));
|
Float64x2 sqrt() => NaiveFloat64x2._doubles(math.sqrt(x), math.sqrt(y));
|
||||||
}
|
}
|
||||||
|
|
||||||
final class NaiveInt32x4 implements Int32x4 {
|
final class NaiveInt32x4 extends WasmTypedDataBase implements Int32x4 {
|
||||||
final int x;
|
final int x;
|
||||||
final int y;
|
final int y;
|
||||||
final int z;
|
final int z;
|
||||||
|
|
|
@ -10,7 +10,8 @@ import "dart:_internal"
|
||||||
ClassID,
|
ClassID,
|
||||||
EfficientLengthIterable,
|
EfficientLengthIterable,
|
||||||
makeListFixedLength,
|
makeListFixedLength,
|
||||||
unsafeCast;
|
unsafeCast,
|
||||||
|
WasmStringBase;
|
||||||
|
|
||||||
import 'dart:_js_helper' show JS;
|
import 'dart:_js_helper' show JS;
|
||||||
import 'dart:_js_types' show JSStringImpl;
|
import 'dart:_js_types' show JSStringImpl;
|
||||||
|
@ -70,7 +71,7 @@ String _toLowerCase(String string) => JS<String>(
|
||||||
* [StringBase] contains common methods used by concrete String
|
* [StringBase] contains common methods used by concrete String
|
||||||
* implementations, e.g., OneByteString.
|
* implementations, e.g., OneByteString.
|
||||||
*/
|
*/
|
||||||
abstract final class StringBase implements String {
|
abstract final class StringBase extends WasmStringBase {
|
||||||
bool _isWhitespace(int codeUnit);
|
bool _isWhitespace(int codeUnit);
|
||||||
|
|
||||||
// Constants used by replaceAll encoding of string slices between matches.
|
// Constants used by replaceAll encoding of string slices between matches.
|
||||||
|
|
|
@ -515,7 +515,7 @@ class _AbstractRecordType extends _Type {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool _checkInstance(Object o) {
|
bool _checkInstance(Object o) {
|
||||||
return _isRecordInstance(o);
|
return _isRecordClassId(ClassID.getID(o));
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -537,7 +537,7 @@ class _RecordType extends _Type {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool _checkInstance(Object o) {
|
bool _checkInstance(Object o) {
|
||||||
if (!_isRecordInstance(o)) return false;
|
if (!_isRecordClassId(ClassID.getID(o))) return false;
|
||||||
return unsafeCast<Record>(o)._checkRecordType(fieldTypes, names);
|
return unsafeCast<Record>(o)._checkRecordType(fieldTypes, names);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -605,13 +605,6 @@ external WasmArray<WasmArray<WasmI32>> _getTypeRulesSupers();
|
||||||
external WasmArray<WasmArray<WasmArray<_Type>>> _getTypeRulesSubstitutions();
|
external WasmArray<WasmArray<WasmArray<_Type>>> _getTypeRulesSubstitutions();
|
||||||
external WasmArray<String>? _getTypeNames();
|
external WasmArray<String>? _getTypeNames();
|
||||||
|
|
||||||
external WasmArray<WasmI8> get _typeCategoryTable;
|
|
||||||
external WasmI32 get _typeCategoryAbstractClass;
|
|
||||||
external WasmI32 get _typeCategoryObject;
|
|
||||||
external WasmI32 get _typeCategoryFunction;
|
|
||||||
external WasmI32 get _typeCategoryRecord;
|
|
||||||
external WasmI32 get _typeCategoryNotMasqueraded;
|
|
||||||
|
|
||||||
/// Type parameter environment used while comparing function types.
|
/// Type parameter environment used while comparing function types.
|
||||||
///
|
///
|
||||||
/// In the case of nested function types, the environment refers to the
|
/// In the case of nested function types, the environment refers to the
|
||||||
|
@ -1299,20 +1292,14 @@ void _checkClosureType(
|
||||||
|
|
||||||
_Type _getActualRuntimeType(Object object) {
|
_Type _getActualRuntimeType(Object object) {
|
||||||
final classId = ClassID.getID(object);
|
final classId = ClassID.getID(object);
|
||||||
final category = _typeCategoryTable.readUnsigned(classId);
|
|
||||||
|
|
||||||
if (category == _typeCategoryFunction.toIntUnsigned()) {
|
if (_isObjectClassId(classId)) return _literal<Object>();
|
||||||
|
if (_isRecordClassId(classId)) {
|
||||||
|
return Record._getMasqueradedRecordRuntimeType(unsafeCast<Record>(object));
|
||||||
|
}
|
||||||
|
if (_isClosureClassId(classId)) {
|
||||||
return _Closure._getClosureRuntimeType(unsafeCast<_Closure>(object));
|
return _Closure._getClosureRuntimeType(unsafeCast<_Closure>(object));
|
||||||
}
|
}
|
||||||
if (category == _typeCategoryRecord.toIntUnsigned()) {
|
|
||||||
return Record._getRecordRuntimeType(unsafeCast<Record>(object));
|
|
||||||
}
|
|
||||||
if (category == _typeCategoryObject.toIntUnsigned()) {
|
|
||||||
return _literal<Object>();
|
|
||||||
}
|
|
||||||
if (category == _typeCategoryAbstractClass.toIntUnsigned()) {
|
|
||||||
throw 'unreachable';
|
|
||||||
}
|
|
||||||
return _InterfaceType(classId, false, Object._getTypeArguments(object));
|
return _InterfaceType(classId, false, Object._getTypeArguments(object));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1323,28 +1310,98 @@ _Type _getActualRuntimeTypeNullable(Object? object) =>
|
||||||
@pragma("wasm:entry-point")
|
@pragma("wasm:entry-point")
|
||||||
_Type _getMasqueradedRuntimeType(Object object) {
|
_Type _getMasqueradedRuntimeType(Object object) {
|
||||||
final classId = ClassID.getID(object);
|
final classId = ClassID.getID(object);
|
||||||
final category = _typeCategoryTable.readUnsigned(classId);
|
|
||||||
|
|
||||||
if (category == _typeCategoryNotMasqueraded.toIntUnsigned()) {
|
// Fast path: Most usages of `.runtimeType` may be on user-defined classes
|
||||||
|
// (e.g. `Widget.runtimeType`, ...)
|
||||||
|
if (ClassID.firstNonMasqueradedInterfaceClassCid <= classId) {
|
||||||
|
// Non-masqueraded interface type.
|
||||||
return _InterfaceType(classId, false, Object._getTypeArguments(object));
|
return _InterfaceType(classId, false, Object._getTypeArguments(object));
|
||||||
}
|
}
|
||||||
if (category == _typeCategoryFunction.toIntUnsigned()) {
|
|
||||||
return _Closure._getClosureRuntimeType(unsafeCast<_Closure>(object));
|
if (_isObjectClassId(classId)) return _literal<Object>();
|
||||||
}
|
if (_isRecordClassId(classId)) {
|
||||||
if (category == _typeCategoryRecord.toIntUnsigned()) {
|
|
||||||
return Record._getMasqueradedRecordRuntimeType(unsafeCast<Record>(object));
|
return Record._getMasqueradedRecordRuntimeType(unsafeCast<Record>(object));
|
||||||
}
|
}
|
||||||
if (category == _typeCategoryObject.toIntUnsigned()) {
|
if (_isClosureClassId(classId)) {
|
||||||
return _literal<Object>();
|
return _Closure._getClosureRuntimeType(unsafeCast<_Closure>(object));
|
||||||
}
|
}
|
||||||
if (category == _typeCategoryAbstractClass.toIntUnsigned()) {
|
|
||||||
throw 'unreachable';
|
// The object is a normal instance of a class (not function or record).
|
||||||
|
|
||||||
|
const bool isJsCompatibility =
|
||||||
|
bool.fromEnvironment('dart.wasm.js_compatibility');
|
||||||
|
|
||||||
|
// This method is not used in the RTT implementation, it's purely used for
|
||||||
|
// producing `Type` objects for `<obj>.runtimeType`.
|
||||||
|
//
|
||||||
|
// => We can use normal `is` checks in here that will be desugared to class-id
|
||||||
|
// range checks.
|
||||||
|
|
||||||
|
if (object is bool) return _literal<bool>();
|
||||||
|
if (object is int) return _literal<int>();
|
||||||
|
if (object is double) return _literal<double>();
|
||||||
|
if (object is _Type) return _literal<Type>();
|
||||||
|
if (object is _ListBase) {
|
||||||
|
return _InterfaceType(
|
||||||
|
ClassID.cidList, false, Object._getTypeArguments(object));
|
||||||
}
|
}
|
||||||
return _InterfaceType(category, false, Object._getTypeArguments(object));
|
|
||||||
|
if (isJsCompatibility) {
|
||||||
|
if (object is String) return _literal<String>();
|
||||||
|
if (object is TypedData) {
|
||||||
|
if (object is ByteData) return _literal<ByteData>();
|
||||||
|
if (object is Int8List) return _literal<Int8List>();
|
||||||
|
if (object is Uint8List) return _literal<Uint8List>();
|
||||||
|
if (object is Uint8ClampedList) return _literal<Uint8ClampedList>();
|
||||||
|
if (object is Int16List) return _literal<Int16List>();
|
||||||
|
if (object is Uint16List) return _literal<Uint16List>();
|
||||||
|
if (object is Int32List) return _literal<Int32List>();
|
||||||
|
if (object is Uint32List) return _literal<Uint32List>();
|
||||||
|
if (object is Int64List) return _literal<Int64List>();
|
||||||
|
if (object is Uint64List) return _literal<Uint64List>();
|
||||||
|
if (object is Float32List) return _literal<Float32List>();
|
||||||
|
if (object is Float64List) return _literal<Float64List>();
|
||||||
|
if (object is Int32x4List) return _literal<Int32x4List>();
|
||||||
|
if (object is Float32x4List) return _literal<Float32x4List>();
|
||||||
|
if (object is Float64x2List) return _literal<Float64x2List>();
|
||||||
|
}
|
||||||
|
if (object is ByteBuffer) return _literal<ByteBuffer>();
|
||||||
|
if (object is Float32x4) return _literal<Float32x4>();
|
||||||
|
if (object is Float64x2) return _literal<Float64x2>();
|
||||||
|
if (object is Int32x4) return _literal<Int32x4>();
|
||||||
|
} else {
|
||||||
|
if (object is WasmStringBase) return _literal<String>();
|
||||||
|
if (object is WasmTypedDataBase) {
|
||||||
|
if (object is ByteData) return _literal<ByteData>();
|
||||||
|
if (object is Int8List) return _literal<Int8List>();
|
||||||
|
if (object is Uint8List) return _literal<Uint8List>();
|
||||||
|
if (object is Uint8ClampedList) return _literal<Uint8ClampedList>();
|
||||||
|
if (object is Int16List) return _literal<Int16List>();
|
||||||
|
if (object is Uint16List) return _literal<Uint16List>();
|
||||||
|
if (object is Int32List) return _literal<Int32List>();
|
||||||
|
if (object is Uint32List) return _literal<Uint32List>();
|
||||||
|
if (object is Int64List) return _literal<Int64List>();
|
||||||
|
if (object is Uint64List) return _literal<Uint64List>();
|
||||||
|
if (object is Float32List) return _literal<Float32List>();
|
||||||
|
if (object is Float64List) return _literal<Float64List>();
|
||||||
|
if (object is Int32x4List) return _literal<Int32x4List>();
|
||||||
|
if (object is Float32x4List) return _literal<Float32x4List>();
|
||||||
|
if (object is Float64x2List) return _literal<Float64x2List>();
|
||||||
|
if (object is ByteBuffer) return _literal<ByteBuffer>();
|
||||||
|
if (object is Float32x4) return _literal<Float32x4>();
|
||||||
|
if (object is Float64x2) return _literal<Float64x2>();
|
||||||
|
if (object is Int32x4) return _literal<Int32x4>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Non-masqueraded interface type.
|
||||||
|
return _InterfaceType(classId, false, Object._getTypeArguments(object));
|
||||||
}
|
}
|
||||||
|
|
||||||
@pragma("wasm:prefer-inline")
|
@pragma("wasm:prefer-inline")
|
||||||
_Type _getMasqueradedRuntimeTypeNullable(Object? object) =>
|
_Type _getMasqueradedRuntimeTypeNullable(Object? object) =>
|
||||||
object == null ? _literal<Null>() : _getMasqueradedRuntimeType(object);
|
object == null ? _literal<Null>() : _getMasqueradedRuntimeType(object);
|
||||||
|
|
||||||
external bool _isRecordInstance(Object o);
|
external bool _isObjectClassId(int classId);
|
||||||
|
external bool _isClosureClassId(int classId);
|
||||||
|
external bool _isRecordClassId(int classId);
|
||||||
|
|
|
@ -22,6 +22,7 @@ import 'dart:_internal'
|
||||||
SubListIterable,
|
SubListIterable,
|
||||||
TakeWhileIterable,
|
TakeWhileIterable,
|
||||||
unsafeCast,
|
unsafeCast,
|
||||||
|
WasmTypedDataBase,
|
||||||
WhereIterable,
|
WhereIterable,
|
||||||
WhereTypeIterable;
|
WhereTypeIterable;
|
||||||
import 'dart:_simd';
|
import 'dart:_simd';
|
||||||
|
@ -101,7 +102,7 @@ final class _TypedListIterator<E> implements Iterator<E> {
|
||||||
/// and [_setUint8Unchecked] methods. Implementations should implement these
|
/// and [_setUint8Unchecked] methods. Implementations should implement these
|
||||||
/// methods and override get/set methods for elements matching the buffer
|
/// methods and override get/set methods for elements matching the buffer
|
||||||
/// element type to provide fast access.
|
/// element type to provide fast access.
|
||||||
abstract class ByteDataBase implements ByteData {
|
abstract class ByteDataBase extends WasmTypedDataBase implements ByteData {
|
||||||
final int offsetInBytes;
|
final int offsetInBytes;
|
||||||
final int lengthInBytes;
|
final int lengthInBytes;
|
||||||
|
|
||||||
|
@ -964,7 +965,7 @@ class _UnmodifiableF64ByteData extends _F64ByteData
|
||||||
/// Base class for [ByteBuffer] implementations. Returns slow lists in all
|
/// Base class for [ByteBuffer] implementations. Returns slow lists in all
|
||||||
/// methods. Implementations should override relevant methods to return fast
|
/// methods. Implementations should override relevant methods to return fast
|
||||||
/// lists when possible and implement [asByteData].
|
/// lists when possible and implement [asByteData].
|
||||||
abstract class ByteBufferBase extends ByteBuffer {
|
abstract class ByteBufferBase extends WasmTypedDataBase implements ByteBuffer {
|
||||||
final int lengthInBytes;
|
final int lengthInBytes;
|
||||||
final bool _mutable;
|
final bool _mutable;
|
||||||
|
|
||||||
|
@ -1332,7 +1333,8 @@ class _F64ByteBuffer extends ByteBufferBase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class UnmodifiableByteBuffer implements UnmodifiableByteBufferView {
|
class UnmodifiableByteBuffer extends WasmTypedDataBase
|
||||||
|
implements UnmodifiableByteBufferView {
|
||||||
final ByteBufferBase _buffer;
|
final ByteBufferBase _buffer;
|
||||||
|
|
||||||
UnmodifiableByteBuffer(ByteBufferBase buffer) : _buffer = buffer._immutable();
|
UnmodifiableByteBuffer(ByteBufferBase buffer) : _buffer = buffer._immutable();
|
||||||
|
@ -2242,7 +2244,7 @@ mixin _UnmodifiableDoubleListMixin {
|
||||||
// Fast lists
|
// Fast lists
|
||||||
//
|
//
|
||||||
|
|
||||||
abstract class _WasmI8ArrayBase {
|
abstract class _WasmI8ArrayBase extends WasmTypedDataBase {
|
||||||
final WasmArray<WasmI8> _data;
|
final WasmArray<WasmI8> _data;
|
||||||
final int _offsetInElements;
|
final int _offsetInElements;
|
||||||
final int length;
|
final int length;
|
||||||
|
@ -2260,7 +2262,7 @@ abstract class _WasmI8ArrayBase {
|
||||||
ByteBuffer get buffer => _I8ByteBuffer(_data);
|
ByteBuffer get buffer => _I8ByteBuffer(_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class _WasmI16ArrayBase {
|
abstract class _WasmI16ArrayBase extends WasmTypedDataBase {
|
||||||
final WasmArray<WasmI16> _data;
|
final WasmArray<WasmI16> _data;
|
||||||
final int _offsetInElements;
|
final int _offsetInElements;
|
||||||
final int length;
|
final int length;
|
||||||
|
@ -2278,7 +2280,7 @@ abstract class _WasmI16ArrayBase {
|
||||||
ByteBuffer get buffer => _I16ByteBuffer(_data);
|
ByteBuffer get buffer => _I16ByteBuffer(_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class _WasmI32ArrayBase {
|
abstract class _WasmI32ArrayBase extends WasmTypedDataBase {
|
||||||
final WasmArray<WasmI32> _data;
|
final WasmArray<WasmI32> _data;
|
||||||
final int _offsetInElements;
|
final int _offsetInElements;
|
||||||
final int length;
|
final int length;
|
||||||
|
@ -2296,7 +2298,7 @@ abstract class _WasmI32ArrayBase {
|
||||||
ByteBuffer get buffer => _I32ByteBuffer(_data);
|
ByteBuffer get buffer => _I32ByteBuffer(_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class _WasmI64ArrayBase {
|
abstract class _WasmI64ArrayBase extends WasmTypedDataBase {
|
||||||
final WasmArray<WasmI64> _data;
|
final WasmArray<WasmI64> _data;
|
||||||
final int _offsetInElements;
|
final int _offsetInElements;
|
||||||
final int length;
|
final int length;
|
||||||
|
@ -2314,7 +2316,7 @@ abstract class _WasmI64ArrayBase {
|
||||||
ByteBuffer get buffer => _I64ByteBuffer(_data);
|
ByteBuffer get buffer => _I64ByteBuffer(_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class _WasmF32ArrayBase {
|
abstract class _WasmF32ArrayBase extends WasmTypedDataBase {
|
||||||
final WasmArray<WasmF32> _data;
|
final WasmArray<WasmF32> _data;
|
||||||
final int _offsetInElements;
|
final int _offsetInElements;
|
||||||
final int length;
|
final int length;
|
||||||
|
@ -2332,7 +2334,7 @@ abstract class _WasmF32ArrayBase {
|
||||||
ByteBuffer get buffer => _F32ByteBuffer(_data);
|
ByteBuffer get buffer => _F32ByteBuffer(_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class _WasmF64ArrayBase {
|
abstract class _WasmF64ArrayBase extends WasmTypedDataBase {
|
||||||
final WasmArray<WasmF64> _data;
|
final WasmArray<WasmF64> _data;
|
||||||
final int _offsetInElements;
|
final int _offsetInElements;
|
||||||
final int length;
|
final int length;
|
||||||
|
@ -2931,7 +2933,7 @@ class UnmodifiableF64List extends F64List
|
||||||
// Slow lists
|
// Slow lists
|
||||||
//
|
//
|
||||||
|
|
||||||
class _SlowListBase {
|
class _SlowListBase extends WasmTypedDataBase {
|
||||||
final ByteBuffer buffer;
|
final ByteBuffer buffer;
|
||||||
final int offsetInBytes;
|
final int offsetInBytes;
|
||||||
final int length;
|
final int length;
|
||||||
|
|
35
tests/web/wasm/masquerade_test.dart
Normal file
35
tests/web/wasm/masquerade_test.dart
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
// Copyright (c) 2024, 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:typed_data';
|
||||||
|
import 'dart:js_interop';
|
||||||
|
|
||||||
|
import 'package:expect/expect.dart';
|
||||||
|
|
||||||
|
const bool isJsCompatibility =
|
||||||
|
bool.fromEnvironment('dart.wasm.js_compatibility');
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
final uint8List = Uint8List(10);
|
||||||
|
final uint8ListView = uint8List.buffer.asUint8List(1, 2);
|
||||||
|
final uint16ListView = uint8List.buffer.asUint16List(2, 4);
|
||||||
|
|
||||||
|
Expect.equals('Uint8List', uint8List.runtimeType.toString());
|
||||||
|
Expect.equals('Uint8List', uint8ListView.runtimeType.toString());
|
||||||
|
Expect.equals('Uint16List', uint16ListView.runtimeType.toString());
|
||||||
|
|
||||||
|
final jsString = eval('"foobar"').dartify();
|
||||||
|
final jsTypedData = eval('new Uint8Array(10)').dartify();
|
||||||
|
|
||||||
|
if (isJsCompatibility) {
|
||||||
|
Expect.equals('String', jsString.runtimeType.toString());
|
||||||
|
Expect.equals('Uint8List', jsTypedData.runtimeType.toString());
|
||||||
|
} else {
|
||||||
|
Expect.equals('JSStringImpl', jsString.runtimeType.toString());
|
||||||
|
Expect.equals('JSUint8ArrayImpl', jsTypedData.runtimeType.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@JS()
|
||||||
|
external JSObject eval(String code);
|
|
@ -53,7 +53,6 @@ compile_platform("compile_dart2wasm_platform") {
|
||||||
|
|
||||||
args = [
|
args = [
|
||||||
"--target=dart2wasm",
|
"--target=dart2wasm",
|
||||||
"--no-defines",
|
|
||||||
"dart:core",
|
"dart:core",
|
||||||
"--nnbd-strong",
|
"--nnbd-strong",
|
||||||
]
|
]
|
||||||
|
@ -71,7 +70,7 @@ compile_platform("compile_dart2wasm_js_compatibility_platform") {
|
||||||
|
|
||||||
args = [
|
args = [
|
||||||
"--target=dart2wasm_js_compatibility",
|
"--target=dart2wasm_js_compatibility",
|
||||||
"--no-defines",
|
"-Ddart.wasm.js_compatibility=true",
|
||||||
"dart:core",
|
"dart:core",
|
||||||
"--nnbd-strong",
|
"--nnbd-strong",
|
||||||
]
|
]
|
||||||
|
|
Loading…
Reference in a new issue