mirror of
https://github.com/dart-lang/sdk
synced 2024-10-06 12:57:42 +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:kernel/ast.dart';
|
||||
import 'package:kernel/library_index.dart';
|
||||
|
||||
import 'package:wasm_builder/wasm_builder.dart' as w;
|
||||
|
||||
|
@ -217,25 +216,25 @@ class ClassInfoCollector {
|
|||
/// shape class with that many fields.
|
||||
final Map<int, w.StructType> _recordStructs = {};
|
||||
|
||||
/// Masquerades for implementation classes. For each entry of the map, all
|
||||
/// subtypes of the key masquerade as the value.
|
||||
late final Map<Class, Class> _masquerades = _computeMasquerades();
|
||||
/// Any subtype of these needs to masqueraded (modulo special js-compatibility
|
||||
/// mode semantics) or specially treated due to being from a different type
|
||||
/// (e.g. record, closure)
|
||||
late final Set<Class> masqueraded = _computeMasquerades();
|
||||
|
||||
/// Masqueraded types are mapped to these classes.
|
||||
late final Set<Class> masqueradeValues = _masquerades.values.toSet();
|
||||
|
||||
Map<Class, Class> _computeMasquerades() {
|
||||
final map = {
|
||||
translator.coreTypes.boolClass: translator.coreTypes.boolClass,
|
||||
translator.coreTypes.intClass: translator.coreTypes.intClass,
|
||||
translator.coreTypes.doubleClass: translator.coreTypes.doubleClass,
|
||||
translator.coreTypes.stringClass: translator.coreTypes.stringClass,
|
||||
translator.index.getClass("dart:core", "_Type"):
|
||||
translator.coreTypes.typeClass,
|
||||
translator.index.getClass("dart:core", "_ListBase"):
|
||||
translator.coreTypes.listClass
|
||||
Set<Class> _computeMasquerades() {
|
||||
final values = {
|
||||
translator.coreTypes.boolClass,
|
||||
translator.coreTypes.intClass,
|
||||
translator.coreTypes.doubleClass,
|
||||
translator.coreTypes.stringClass,
|
||||
translator.coreTypes.functionClass,
|
||||
translator.coreTypes.recordClass,
|
||||
translator.index.getClass("dart:core", "_Type"),
|
||||
translator.index.getClass("dart:core", "_ListBase"),
|
||||
};
|
||||
for (final name in const <String>[
|
||||
"ByteBuffer",
|
||||
"ByteData",
|
||||
"Int8List",
|
||||
"Uint8List",
|
||||
"Uint8ClampedList",
|
||||
|
@ -252,49 +251,9 @@ class ClassInfoCollector {
|
|||
"Float64x2List",
|
||||
]) {
|
||||
final Class? cls = translator.index.tryGetClass("dart:typed_data", name);
|
||||
if (cls != null) {
|
||||
map[cls] = cls;
|
||||
}
|
||||
if (cls != null) values.add(cls);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
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>();
|
||||
return values;
|
||||
}
|
||||
|
||||
/// 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
|
||||
// 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.
|
||||
// All other classes sit below their superclass.
|
||||
ClassInfo superInfo = cls == translator.coreTypes.boolClass ||
|
||||
cls == translator.coreTypes.numClass
|
||||
? topInfo
|
||||
: (!translator.options.jsCompatibility &&
|
||||
cls == translator.stringBaseClass) ||
|
||||
cls == translator.wasmStringBaseClass) ||
|
||||
cls == translator.typeClass
|
||||
? translator.classInfo[cls.implementedTypes.single.classNode]!
|
||||
: translator.classInfo[superclass]!;
|
||||
|
@ -390,29 +349,6 @@ class ClassInfoCollector {
|
|||
translator.classes[classId] = info;
|
||||
translator.classInfo[cls] = 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) {
|
||||
|
@ -505,7 +441,7 @@ class ClassInfoCollector {
|
|||
const int firstClassId = 1;
|
||||
|
||||
translator.classIdNumbering =
|
||||
ClassIdNumbering._number(translator, masqueradeValues, firstClassId);
|
||||
ClassIdNumbering._number(translator, masqueraded, firstClassId);
|
||||
final classIds = translator.classIdNumbering.classIds;
|
||||
final dfsOrder = translator.classIdNumbering.dfsOrder;
|
||||
|
||||
|
@ -560,12 +496,18 @@ class ClassIdNumbering {
|
|||
final Map<Class, List<Class>> _subclasses;
|
||||
final Map<Class, List<Class>> _implementors;
|
||||
final List<Range> _concreteSubclassIdRanges;
|
||||
final Set<Class> _masqueraded;
|
||||
|
||||
final List<Class> dfsOrder;
|
||||
final Map<Class, int> classIds;
|
||||
|
||||
ClassIdNumbering._(this._subclasses, this._implementors,
|
||||
this._concreteSubclassIdRanges, this.dfsOrder, this.classIds);
|
||||
ClassIdNumbering._(
|
||||
this._subclasses,
|
||||
this._implementors,
|
||||
this._concreteSubclassIdRanges,
|
||||
this._masqueraded,
|
||||
this.dfsOrder,
|
||||
this.classIds);
|
||||
|
||||
final Map<Class, Set<Class>> _transitiveImplementors = {};
|
||||
Set<Class> _getTransitiveImplementors(Class klass) {
|
||||
|
@ -611,8 +553,19 @@ class ClassIdNumbering {
|
|||
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(
|
||||
Translator translator, Set<Class> masqueradeValues, int firstClassId) {
|
||||
Translator translator, Set<Class> masqueraded, int firstClassId) {
|
||||
// Make graph from class to its subclasses.
|
||||
late final Class root;
|
||||
final subclasses = <Class, List<Class>>{};
|
||||
|
@ -645,7 +598,8 @@ class ClassIdNumbering {
|
|||
final fixedOrder = <Class, int>{
|
||||
translator.coreTypes.boolClass: -10,
|
||||
translator.coreTypes.numClass: -9,
|
||||
if (!translator.options.jsCompatibility) translator.stringBaseClass: -8,
|
||||
if (!translator.options.jsCompatibility)
|
||||
translator.wasmStringBaseClass: -8,
|
||||
translator.jsStringClass: -7,
|
||||
translator.typeClass: -6,
|
||||
translator.listBaseClass: -5,
|
||||
|
@ -657,7 +611,7 @@ class ClassIdNumbering {
|
|||
|
||||
final importUri = klass.enclosingLibrary.importUri.toString();
|
||||
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
|
||||
// common base class except for `Object` but most of them have similar
|
||||
// selectors.
|
||||
|
@ -700,26 +654,8 @@ class ClassIdNumbering {
|
|||
firstClassId + classCount, Range.empty(),
|
||||
growable: false);
|
||||
|
||||
// TODO: We may consider removing the type category table. But until we do
|
||||
// we have to make sure that all masqueraded types that concrete classes may
|
||||
// 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;
|
||||
int nextConcreteClassId = firstClassId;
|
||||
int nextAbstractClassId = firstClassId + concreteClassCount;
|
||||
dfs(root, (Class cls) {
|
||||
dfsOrder.add(cls);
|
||||
if (cls.isAbstract) {
|
||||
|
@ -740,11 +676,9 @@ class ClassIdNumbering {
|
|||
concreteSubclassRanges[classIds[cls]!] = range;
|
||||
}
|
||||
});
|
||||
for (Class cls in masqueradeValues) {
|
||||
assert(classIds[cls]! <= 255);
|
||||
}
|
||||
return ClassIdNumbering._(
|
||||
subclasses, implementors, concreteSubclassRanges, dfsOrder, classIds);
|
||||
|
||||
return ClassIdNumbering._(subclasses, implementors, concreteSubclassRanges,
|
||||
masqueraded, dfsOrder, classIds);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3803,3 +3803,46 @@ enum _VirtualCallKind {
|
|||
|
||||
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? depFile;
|
||||
String? outputJSRuntimeFile;
|
||||
Map<String, String> environment = const {};
|
||||
Map<String, String> environment = {};
|
||||
Map<fe.ExperimentalFlag, bool> feExperimentalFlags = const {};
|
||||
String? multiRootScheme;
|
||||
List<Uri> multiRoots = const [];
|
||||
|
|
|
@ -36,9 +36,10 @@ final List<Option> options = [
|
|||
defaultsTo: _d.translatorOptions.printKernel),
|
||||
Flag("print-wasm", (o, value) => o.translatorOptions.printWasm = value,
|
||||
defaultsTo: _d.translatorOptions.printWasm),
|
||||
Flag("js-compatibility",
|
||||
(o, value) => o.translatorOptions.jsCompatibility = value,
|
||||
defaultsTo: _d.translatorOptions.jsCompatibility),
|
||||
Flag("js-compatibility", (o, value) {
|
||||
o.translatorOptions.jsCompatibility = value;
|
||||
o.environment['dart.wasm.js_compatibility'] = 'true';
|
||||
}, defaultsTo: _d.translatorOptions.jsCompatibility),
|
||||
Flag(
|
||||
"enable-asserts", (o, value) => o.translatorOptions.enableAsserts = value,
|
||||
defaultsTo: _d.translatorOptions.enableAsserts),
|
||||
|
@ -73,7 +74,7 @@ final List<Option> options = [
|
|||
IntMultiOption(
|
||||
"watch", (o, values) => o.translatorOptions.watchPoints = values),
|
||||
StringMultiOption(
|
||||
"define", (o, values) => o.environment = processEnvironment(values),
|
||||
"define", (o, values) => o.environment.addAll(processEnvironment(values)),
|
||||
abbr: "D"),
|
||||
StringMultiOption(
|
||||
"enable-experiment",
|
||||
|
|
|
@ -404,24 +404,35 @@ class Intrinsifier {
|
|||
/// Generate inline code for a [StaticGet] if the member is an inlined
|
||||
/// intrinsic.
|
||||
w.ValueType? generateStaticGetterIntrinsic(StaticGet node) {
|
||||
Member target = node.target;
|
||||
final Member target = node.target;
|
||||
final Class? cls = target.enclosingClass;
|
||||
|
||||
// ClassID getters
|
||||
String? libAndClassName = translator.getPragma(target, "wasm:class-id");
|
||||
if (libAndClassName != null) {
|
||||
List<String> libAndClassNameParts = libAndClassName.split("#");
|
||||
final String lib = libAndClassNameParts[0];
|
||||
final String className = libAndClassNameParts[1];
|
||||
Class cls = translator.libraries
|
||||
.firstWhere((l) => l.name == lib && l.importUri.scheme == 'dart',
|
||||
orElse: () => throw 'Library $lib not found (${target.location})')
|
||||
.classes
|
||||
.firstWhere((c) => c.name == className,
|
||||
orElse: () => throw 'Class $className not found in library $lib '
|
||||
'(${target.location})');
|
||||
int classId = translator.classInfo[cls]!.classId;
|
||||
b.i64_const(classId);
|
||||
return w.NumType.i64;
|
||||
if (cls?.name == 'ClassID') {
|
||||
final libAndClassName = translator.getPragma(target, "wasm:class-id");
|
||||
if (libAndClassName != null) {
|
||||
List<String> libAndClassNameParts = libAndClassName.split("#");
|
||||
final String lib = libAndClassNameParts[0];
|
||||
final String className = libAndClassNameParts[1];
|
||||
Class cls = translator.libraries
|
||||
.firstWhere((l) => l.name == lib && l.importUri.scheme == 'dart',
|
||||
orElse: () =>
|
||||
throw 'Library $lib not found (${target.location})')
|
||||
.classes
|
||||
.firstWhere((c) => c.name == className,
|
||||
orElse: () =>
|
||||
throw 'Class $className not found in library $lib '
|
||||
'(${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
|
||||
|
@ -440,35 +451,6 @@ class Intrinsifier {
|
|||
b.i32_const(0);
|
||||
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;
|
||||
|
@ -659,16 +641,38 @@ class Intrinsifier {
|
|||
return translator.types.makeTypeRulesSubstitutions(b);
|
||||
case "_getTypeNames":
|
||||
return translator.types.makeTypeNames(b);
|
||||
case "_isRecordInstance":
|
||||
Expression o = node.arguments.positional.single;
|
||||
b.global_get(translator.types.typeCategoryTable);
|
||||
codeGen.wrap(o, translator.topInfo.nonNullableType);
|
||||
b.struct_get(translator.topInfo.struct, FieldIndex.classId);
|
||||
b.array_get_u(
|
||||
(translator.types.typeCategoryTable.type.type as w.RefType)
|
||||
.heapType as w.ArrayType);
|
||||
b.i32_const(translator.types.typeCategoryRecord);
|
||||
b.i32_eq();
|
||||
case "_isObjectClassId":
|
||||
final classId = node.arguments.positional.single;
|
||||
|
||||
final objectClassId = translator
|
||||
.classIdNumbering.classIds[translator.coreTypes.objectClass]!;
|
||||
|
||||
codeGen.wrap(classId, w.NumType.i64);
|
||||
b.i32_wrap_i64();
|
||||
b.emitClassIdRangeCheck(
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,6 +50,8 @@ mixin KernelNodes {
|
|||
index.getClass("dart:core", "_GrowableList");
|
||||
late final Class immutableListClass =
|
||||
index.getClass("dart:core", "_ImmutableList");
|
||||
late final Class wasmStringBaseClass =
|
||||
index.getClass("dart:_internal", "WasmStringBase");
|
||||
late final Class stringBaseClass =
|
||||
index.getClass("dart:_string", "StringBase");
|
||||
late final Class oneByteStringClass =
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import 'dart:math' show max;
|
||||
import 'dart:typed_data' show Uint8List;
|
||||
|
||||
import 'package:dart2wasm/class_info.dart';
|
||||
import 'package:dart2wasm/code_generator.dart';
|
||||
|
@ -101,9 +100,6 @@ class Types {
|
|||
/// parameter index range of their corresponding function type.
|
||||
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);
|
||||
|
||||
w.ValueType classAndFieldToType(Class cls, int fieldIndex) =>
|
||||
|
@ -288,77 +284,6 @@ class Types {
|
|||
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) {
|
||||
return type is DynamicType ||
|
||||
type is VoidType ||
|
||||
|
@ -727,44 +652,8 @@ class Types {
|
|||
} else {
|
||||
final ranges =
|
||||
translator.classIdNumbering.getConcreteClassIdRanges(interfaceClass);
|
||||
if (ranges.isEmpty) {
|
||||
b.drop();
|
||||
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
|
||||
}
|
||||
b.struct_get(translator.topInfo.struct, FieldIndex.classId);
|
||||
b.emitClassIdRangeCheck(codeGen, ranges);
|
||||
}
|
||||
|
||||
if (isPotentiallyNullable) {
|
||||
|
|
|
@ -26,14 +26,14 @@ class ClassID {
|
|||
external static int get cid_Int8List;
|
||||
@pragma("wasm:class-id", "dart.typed_data#_Int8ArrayView")
|
||||
external static int get cidInt8ArrayView;
|
||||
@pragma("wasm:class-id", "dart.core#Object")
|
||||
external static int get cidObject;
|
||||
@pragma("wasm:class-id", "dart.async#Future")
|
||||
external static int get cidFuture;
|
||||
@pragma("wasm:class-id", "dart.core#Function")
|
||||
external static int get cidFunction;
|
||||
@pragma("wasm:class-id", "dart.core#_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")
|
||||
external static int get cidFixedLengthList;
|
||||
@pragma("wasm:class-id", "dart.core#_ListBase")
|
||||
|
@ -69,6 +69,10 @@ class ClassID {
|
|||
@pragma("wasm:class-id", "dart.core#_NamedParameter")
|
||||
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.
|
||||
static final int numPredefinedCids = 1;
|
||||
}
|
||||
|
|
|
@ -21,7 +21,9 @@ import "dart:_internal"
|
|||
makeFixedListUnmodifiable,
|
||||
makeListFixedLength,
|
||||
patch,
|
||||
unsafeCast;
|
||||
unsafeCast,
|
||||
WasmStringBase,
|
||||
WasmTypedDataBase;
|
||||
|
||||
import "dart:_internal" as _internal show Symbol;
|
||||
|
||||
|
@ -45,7 +47,7 @@ import 'dart:convert' show Encoding, utf8;
|
|||
|
||||
import 'dart:math' show Random;
|
||||
|
||||
import "dart:typed_data" show Uint8List, Uint16List;
|
||||
import "dart:typed_data";
|
||||
|
||||
import 'dart:_object_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
|
||||
// the parts of the core library implementation where we know by construction
|
||||
// the type of a value.
|
||||
|
|
|
@ -10,8 +10,9 @@ import 'dart:_internal'
|
|||
import 'dart:collection' show ListMixin;
|
||||
import 'dart:math' as math;
|
||||
import 'dart:typed_data';
|
||||
import 'dart:_internal' show WasmTypedDataBase;
|
||||
|
||||
final class NaiveInt32x4List
|
||||
final class NaiveInt32x4List extends WasmTypedDataBase
|
||||
with ListMixin<Int32x4>, FixedLengthListMixin<Int32x4>
|
||||
implements Int32x4List {
|
||||
final Int32List _storage;
|
||||
|
@ -116,7 +117,7 @@ final class NaiveUnmodifiableInt32x4List extends NaiveInt32x4List
|
|||
ByteBuffer get buffer => UnmodifiableByteBufferView(super.buffer);
|
||||
}
|
||||
|
||||
final class NaiveFloat32x4List
|
||||
final class NaiveFloat32x4List extends WasmTypedDataBase
|
||||
with ListMixin<Float32x4>, FixedLengthListMixin<Float32x4>
|
||||
implements Float32x4List {
|
||||
final Float32List _storage;
|
||||
|
@ -222,7 +223,7 @@ final class NaiveUnmodifiableFloat32x4List extends NaiveFloat32x4List
|
|||
ByteBuffer get buffer => UnmodifiableByteBufferView(super.buffer);
|
||||
}
|
||||
|
||||
final class NaiveFloat64x2List
|
||||
final class NaiveFloat64x2List extends WasmTypedDataBase
|
||||
with ListMixin<Float64x2>, FixedLengthListMixin<Float64x2>
|
||||
implements Float64x2List {
|
||||
final Float64List _storage;
|
||||
|
@ -322,7 +323,7 @@ final class NaiveUnmodifiableFloat64x2List extends NaiveFloat64x2List
|
|||
ByteBuffer get buffer => UnmodifiableByteBufferView(super.buffer);
|
||||
}
|
||||
|
||||
final class NaiveFloat32x4 implements Float32x4 {
|
||||
final class NaiveFloat32x4 extends WasmTypedDataBase implements Float32x4 {
|
||||
final double x;
|
||||
final double y;
|
||||
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 y;
|
||||
|
||||
|
@ -688,7 +689,7 @@ final class NaiveFloat64x2 implements Float64x2 {
|
|||
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 y;
|
||||
final int z;
|
||||
|
|
|
@ -10,7 +10,8 @@ import "dart:_internal"
|
|||
ClassID,
|
||||
EfficientLengthIterable,
|
||||
makeListFixedLength,
|
||||
unsafeCast;
|
||||
unsafeCast,
|
||||
WasmStringBase;
|
||||
|
||||
import 'dart:_js_helper' show JS;
|
||||
import 'dart:_js_types' show JSStringImpl;
|
||||
|
@ -70,7 +71,7 @@ String _toLowerCase(String string) => JS<String>(
|
|||
* [StringBase] contains common methods used by concrete String
|
||||
* implementations, e.g., OneByteString.
|
||||
*/
|
||||
abstract final class StringBase implements String {
|
||||
abstract final class StringBase extends WasmStringBase {
|
||||
bool _isWhitespace(int codeUnit);
|
||||
|
||||
// Constants used by replaceAll encoding of string slices between matches.
|
||||
|
|
|
@ -515,7 +515,7 @@ class _AbstractRecordType extends _Type {
|
|||
|
||||
@override
|
||||
bool _checkInstance(Object o) {
|
||||
return _isRecordInstance(o);
|
||||
return _isRecordClassId(ClassID.getID(o));
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -537,7 +537,7 @@ class _RecordType extends _Type {
|
|||
|
||||
@override
|
||||
bool _checkInstance(Object o) {
|
||||
if (!_isRecordInstance(o)) return false;
|
||||
if (!_isRecordClassId(ClassID.getID(o))) return false;
|
||||
return unsafeCast<Record>(o)._checkRecordType(fieldTypes, names);
|
||||
}
|
||||
|
||||
|
@ -605,13 +605,6 @@ external WasmArray<WasmArray<WasmI32>> _getTypeRulesSupers();
|
|||
external WasmArray<WasmArray<WasmArray<_Type>>> _getTypeRulesSubstitutions();
|
||||
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.
|
||||
///
|
||||
/// In the case of nested function types, the environment refers to the
|
||||
|
@ -1299,20 +1292,14 @@ void _checkClosureType(
|
|||
|
||||
_Type _getActualRuntimeType(Object 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));
|
||||
}
|
||||
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));
|
||||
}
|
||||
|
||||
|
@ -1323,28 +1310,98 @@ _Type _getActualRuntimeTypeNullable(Object? object) =>
|
|||
@pragma("wasm:entry-point")
|
||||
_Type _getMasqueradedRuntimeType(Object 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));
|
||||
}
|
||||
if (category == _typeCategoryFunction.toIntUnsigned()) {
|
||||
return _Closure._getClosureRuntimeType(unsafeCast<_Closure>(object));
|
||||
}
|
||||
if (category == _typeCategoryRecord.toIntUnsigned()) {
|
||||
|
||||
if (_isObjectClassId(classId)) return _literal<Object>();
|
||||
if (_isRecordClassId(classId)) {
|
||||
return Record._getMasqueradedRecordRuntimeType(unsafeCast<Record>(object));
|
||||
}
|
||||
if (category == _typeCategoryObject.toIntUnsigned()) {
|
||||
return _literal<Object>();
|
||||
if (_isClosureClassId(classId)) {
|
||||
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")
|
||||
_Type _getMasqueradedRuntimeTypeNullable(Object? 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,
|
||||
TakeWhileIterable,
|
||||
unsafeCast,
|
||||
WasmTypedDataBase,
|
||||
WhereIterable,
|
||||
WhereTypeIterable;
|
||||
import 'dart:_simd';
|
||||
|
@ -101,7 +102,7 @@ final class _TypedListIterator<E> implements Iterator<E> {
|
|||
/// and [_setUint8Unchecked] methods. Implementations should implement these
|
||||
/// methods and override get/set methods for elements matching the buffer
|
||||
/// element type to provide fast access.
|
||||
abstract class ByteDataBase implements ByteData {
|
||||
abstract class ByteDataBase extends WasmTypedDataBase implements ByteData {
|
||||
final int offsetInBytes;
|
||||
final int lengthInBytes;
|
||||
|
||||
|
@ -964,7 +965,7 @@ class _UnmodifiableF64ByteData extends _F64ByteData
|
|||
/// Base class for [ByteBuffer] implementations. Returns slow lists in all
|
||||
/// methods. Implementations should override relevant methods to return fast
|
||||
/// lists when possible and implement [asByteData].
|
||||
abstract class ByteBufferBase extends ByteBuffer {
|
||||
abstract class ByteBufferBase extends WasmTypedDataBase implements ByteBuffer {
|
||||
final int lengthInBytes;
|
||||
final bool _mutable;
|
||||
|
||||
|
@ -1332,7 +1333,8 @@ class _F64ByteBuffer extends ByteBufferBase {
|
|||
}
|
||||
}
|
||||
|
||||
class UnmodifiableByteBuffer implements UnmodifiableByteBufferView {
|
||||
class UnmodifiableByteBuffer extends WasmTypedDataBase
|
||||
implements UnmodifiableByteBufferView {
|
||||
final ByteBufferBase _buffer;
|
||||
|
||||
UnmodifiableByteBuffer(ByteBufferBase buffer) : _buffer = buffer._immutable();
|
||||
|
@ -2242,7 +2244,7 @@ mixin _UnmodifiableDoubleListMixin {
|
|||
// Fast lists
|
||||
//
|
||||
|
||||
abstract class _WasmI8ArrayBase {
|
||||
abstract class _WasmI8ArrayBase extends WasmTypedDataBase {
|
||||
final WasmArray<WasmI8> _data;
|
||||
final int _offsetInElements;
|
||||
final int length;
|
||||
|
@ -2260,7 +2262,7 @@ abstract class _WasmI8ArrayBase {
|
|||
ByteBuffer get buffer => _I8ByteBuffer(_data);
|
||||
}
|
||||
|
||||
abstract class _WasmI16ArrayBase {
|
||||
abstract class _WasmI16ArrayBase extends WasmTypedDataBase {
|
||||
final WasmArray<WasmI16> _data;
|
||||
final int _offsetInElements;
|
||||
final int length;
|
||||
|
@ -2278,7 +2280,7 @@ abstract class _WasmI16ArrayBase {
|
|||
ByteBuffer get buffer => _I16ByteBuffer(_data);
|
||||
}
|
||||
|
||||
abstract class _WasmI32ArrayBase {
|
||||
abstract class _WasmI32ArrayBase extends WasmTypedDataBase {
|
||||
final WasmArray<WasmI32> _data;
|
||||
final int _offsetInElements;
|
||||
final int length;
|
||||
|
@ -2296,7 +2298,7 @@ abstract class _WasmI32ArrayBase {
|
|||
ByteBuffer get buffer => _I32ByteBuffer(_data);
|
||||
}
|
||||
|
||||
abstract class _WasmI64ArrayBase {
|
||||
abstract class _WasmI64ArrayBase extends WasmTypedDataBase {
|
||||
final WasmArray<WasmI64> _data;
|
||||
final int _offsetInElements;
|
||||
final int length;
|
||||
|
@ -2314,7 +2316,7 @@ abstract class _WasmI64ArrayBase {
|
|||
ByteBuffer get buffer => _I64ByteBuffer(_data);
|
||||
}
|
||||
|
||||
abstract class _WasmF32ArrayBase {
|
||||
abstract class _WasmF32ArrayBase extends WasmTypedDataBase {
|
||||
final WasmArray<WasmF32> _data;
|
||||
final int _offsetInElements;
|
||||
final int length;
|
||||
|
@ -2332,7 +2334,7 @@ abstract class _WasmF32ArrayBase {
|
|||
ByteBuffer get buffer => _F32ByteBuffer(_data);
|
||||
}
|
||||
|
||||
abstract class _WasmF64ArrayBase {
|
||||
abstract class _WasmF64ArrayBase extends WasmTypedDataBase {
|
||||
final WasmArray<WasmF64> _data;
|
||||
final int _offsetInElements;
|
||||
final int length;
|
||||
|
@ -2931,7 +2933,7 @@ class UnmodifiableF64List extends F64List
|
|||
// Slow lists
|
||||
//
|
||||
|
||||
class _SlowListBase {
|
||||
class _SlowListBase extends WasmTypedDataBase {
|
||||
final ByteBuffer buffer;
|
||||
final int offsetInBytes;
|
||||
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 = [
|
||||
"--target=dart2wasm",
|
||||
"--no-defines",
|
||||
"dart:core",
|
||||
"--nnbd-strong",
|
||||
]
|
||||
|
@ -71,7 +70,7 @@ compile_platform("compile_dart2wasm_js_compatibility_platform") {
|
|||
|
||||
args = [
|
||||
"--target=dart2wasm_js_compatibility",
|
||||
"--no-defines",
|
||||
"-Ddart.wasm.js_compatibility=true",
|
||||
"dart:core",
|
||||
"--nnbd-strong",
|
||||
]
|
||||
|
|
Loading…
Reference in a new issue