[dart2wasm] Interfaces for accessing memories
This adds the `Memory` class to `dart:_wasm`, allowing Dart code to
load and store numeric types in linear memory.
Since `dart2wasm` doesn't generate a memory instance by default, there
is no singleton instance of `Memory`. Instead, memories are defined as
`external` top-level getters annotated with a pragma like
`@pragma('wasm:memory-tyype', MemoryType(limits: Limits(1, 10)))` to
declare their type.
Interop happens in a static way: Methods on `Memory` cannot be torn-off
and, since the target memory is encoded directly in the store/load
instruction, there's also no polymorphism for memories in Dart.
Attempting to call methods on a memory instance that isn't a direct
reference to its definition is a compile-time error.
Memories can also be imported and exported through the existing
`wasm:import` and `wasm:export` pragmas.
TEST=tests/web/wasm/memory_test.dart
Change-Id: I726f33ac2ec04afab55c5a2b6bc09079d0193e02
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/470020
Commit-Queue: Martin Kustermann <kustermann@google.com>
Reviewed-by: Slava Egorov <vegorov@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
This commit is contained in:
committed by
Commit Queue
parent
47ba2f23a1
commit
7d8cd032dc
@@ -7,6 +7,9 @@ analyzer:
|
||||
implementation_imports: ignore
|
||||
library_prefixes: ignore
|
||||
|
||||
exclude:
|
||||
- test/ir_tests/*.dart
|
||||
|
||||
linter:
|
||||
rules:
|
||||
- directives_ordering
|
||||
|
||||
@@ -110,7 +110,6 @@ class DynamicSubmoduleOutputData extends ModuleOutputData {
|
||||
}
|
||||
|
||||
class DynamicMainModuleStrategy extends ModuleStrategy with KernelNodes {
|
||||
@override
|
||||
final Component component;
|
||||
@override
|
||||
final CoreTypes coreTypes;
|
||||
|
||||
@@ -12,6 +12,7 @@ import 'dispatch_table.dart';
|
||||
import 'dynamic_modules.dart';
|
||||
import 'reference_extensions.dart';
|
||||
import 'translator.dart';
|
||||
import 'util.dart' as util;
|
||||
|
||||
/// This class is responsible for collecting import and export annotations.
|
||||
/// It also creates Wasm functions for Dart members and manages the compilation
|
||||
@@ -49,33 +50,26 @@ class FunctionCollector {
|
||||
}
|
||||
|
||||
void _importOrExport(Procedure member) {
|
||||
String? importName =
|
||||
translator.getPragma(member, "wasm:import", member.name.text);
|
||||
final importName = util.getWasmImportPragma(translator.coreTypes, member);
|
||||
if (importName != null) {
|
||||
int dot = importName.indexOf('.');
|
||||
if (dot != -1) {
|
||||
assert(!member.isInstanceMember);
|
||||
String module = importName.substring(0, dot);
|
||||
String name = importName.substring(dot + 1);
|
||||
final ftype = _makeFunctionType(translator, member.reference, null,
|
||||
isImportOrExport: true);
|
||||
_functions[member.reference] = translator
|
||||
.moduleForReference(member.reference)
|
||||
.functions
|
||||
.import(module, name, ftype, "$importName (import)");
|
||||
}
|
||||
final ftype = _makeFunctionType(translator, member.reference, null,
|
||||
isImportOrExport: true);
|
||||
_functions[member.reference] = translator
|
||||
.moduleForReference(member.reference)
|
||||
.functions
|
||||
.import(importName.moduleName, importName.itemName, ftype,
|
||||
"$importName (import)");
|
||||
}
|
||||
|
||||
// Ensure any procedures marked as exported are enqueued.
|
||||
final text = member.name.text;
|
||||
String? exportName = translator.getPragma(member, "wasm:export", text);
|
||||
String? exportName = util.getWasmExportPragma(translator.coreTypes, member);
|
||||
if (exportName != null) {
|
||||
getFunction(member.reference);
|
||||
}
|
||||
|
||||
// Whether a procedure is strongly or weakly exported, we must not use its
|
||||
// name as the export name of a different function.
|
||||
exportName ??= translator.getPragma(member, "wasm:weak-export", text);
|
||||
exportName ??= util.getWasmWeakExportPragma(translator.coreTypes, member);
|
||||
if (exportName != null) {
|
||||
translator.exporter.reserveName(exportName);
|
||||
}
|
||||
@@ -83,15 +77,7 @@ class FunctionCollector {
|
||||
|
||||
/// If the member with the reference [target] is exported, get the export
|
||||
/// name.
|
||||
String? getExportName(Reference target) {
|
||||
final member = target.asMember;
|
||||
if (member.reference == target) {
|
||||
final text = member.name.text;
|
||||
return translator.getPragma(member, "wasm:export", text) ??
|
||||
translator.getPragma(member, "wasm:weak-export", text);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
String? getExportName(Reference target) => translator.getExportName(target);
|
||||
|
||||
void initialize() {
|
||||
_collectImportsAndExports();
|
||||
@@ -113,24 +99,20 @@ class FunctionCollector {
|
||||
return _functions.putIfAbsent(target, () {
|
||||
final member = target.asMember;
|
||||
|
||||
// If this function is a `@pragma('wasm:import', '<module>:<name>')` we
|
||||
// If this function is a `@pragma('wasm:import', '<module>.<name>')` we
|
||||
// import the function and return it.
|
||||
if (member.reference == target && member.annotations.isNotEmpty) {
|
||||
final importName =
|
||||
translator.getPragma(member, 'wasm:import', member.name.text);
|
||||
util.getWasmImportPragma(translator.coreTypes, member);
|
||||
|
||||
if (importName != null) {
|
||||
assert(!member.isInstanceMember);
|
||||
int dot = importName.indexOf('.');
|
||||
if (dot != -1) {
|
||||
final module = importName.substring(0, dot);
|
||||
final name = importName.substring(dot + 1);
|
||||
final ftype = _makeFunctionType(translator, member.reference, null,
|
||||
isImportOrExport: true);
|
||||
return _functions[member.reference] = translator
|
||||
.moduleForReference(member.reference)
|
||||
.functions
|
||||
.import(module, name, ftype, "$importName (import)");
|
||||
}
|
||||
final ftype = _makeFunctionType(translator, member.reference, null,
|
||||
isImportOrExport: true);
|
||||
return _functions[member.reference] = translator
|
||||
.moduleForReference(member.reference)
|
||||
.functions
|
||||
.import(importName.moduleName, importName.itemName, ftype,
|
||||
"$importName (import)");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,9 +127,8 @@ class FunctionCollector {
|
||||
// we export it under the given `<name>`
|
||||
String? exportName;
|
||||
if (member.reference == target && member.annotations.isNotEmpty) {
|
||||
exportName = translator.getPragma(
|
||||
member, 'wasm:export', member.name.text) ??
|
||||
translator.getPragma(member, 'wasm:weak-export', member.name.text);
|
||||
exportName = util.getWasmExportPragma(translator.coreTypes, member) ??
|
||||
util.getWasmWeakExportPragma(translator.coreTypes, member);
|
||||
assert(exportName == null || member is Procedure && member.isStatic);
|
||||
}
|
||||
|
||||
|
||||
@@ -194,7 +194,30 @@ enum StaticIntrinsic {
|
||||
wasmI31RefExtensionsExternalize(
|
||||
'dart:_wasm', null, 'WasmI31RefExtensions|externalize'),
|
||||
wasmI31RefExtensionsGetS('dart:_wasm', null, 'WasmI31RefExtensions|get_s'),
|
||||
wasmI31RefExtensionsGetU('dart:_wasm', null, 'WasmI31RefExtensions|get_u');
|
||||
wasmI31RefExtensionsGetU('dart:_wasm', null, 'WasmI31RefExtensions|get_u'),
|
||||
wasmMemorySize('dart:_wasm', null, 'MemoryAccessExtension|get#size'),
|
||||
wasmMemoryGrow('dart:_wasm', null, 'MemoryAccessExtension|grow'),
|
||||
wasmMemoryFill('dart:_wasm', null, 'MemoryAccessExtension|fill'),
|
||||
wasmMemoryLoadFloat32(
|
||||
'dart:_wasm', null, 'MemoryAccessExtension|loadFloat32'),
|
||||
wasmMemoryLoadFloat64(
|
||||
'dart:_wasm', null, 'MemoryAccessExtension|loadFloat64'),
|
||||
wasmMemoryLoadInt8('dart:_wasm', null, 'MemoryAccessExtension|loadInt8'),
|
||||
wasmMemoryLoadInt16('dart:_wasm', null, 'MemoryAccessExtension|loadInt16'),
|
||||
wasmMemoryLoadInt32('dart:_wasm', null, 'MemoryAccessExtension|loadInt32'),
|
||||
wasmMemoryLoadInt64('dart:_wasm', null, 'MemoryAccessExtension|loadInt64'),
|
||||
wasmMemoryLoadUint8('dart:_wasm', null, 'MemoryAccessExtension|loadUint8'),
|
||||
wasmMemoryLoadUint16('dart:_wasm', null, 'MemoryAccessExtension|loadUint16'),
|
||||
wasmMemoryLoadUint32('dart:_wasm', null, 'MemoryAccessExtension|loadUint32'),
|
||||
wasmMemoryStoreFloat32(
|
||||
'dart:_wasm', null, 'MemoryAccessExtension|storeFloat32'),
|
||||
wasmMemoryStoreFloat64(
|
||||
'dart:_wasm', null, 'MemoryAccessExtension|storeFloat64'),
|
||||
wasmMemoryStoreInt8('dart:_wasm', null, 'MemoryAccessExtension|storeInt8'),
|
||||
wasmMemoryStoreInt16('dart:_wasm', null, 'MemoryAccessExtension|storeInt16'),
|
||||
wasmMemoryStoreInt32('dart:_wasm', null, 'MemoryAccessExtension|storeInt32'),
|
||||
wasmMemoryStoreInt64('dart:_wasm', null, 'MemoryAccessExtension|storeInt64'),
|
||||
;
|
||||
|
||||
final String library;
|
||||
final String? cls;
|
||||
@@ -307,7 +330,7 @@ class Intrinsifier {
|
||||
_inlineUnaryOperatorMap = {
|
||||
intType: {
|
||||
'unary-': (c, receiver) {
|
||||
final int? intValue = _extractIntValue(receiver);
|
||||
final int? intValue = extractIntValue(receiver);
|
||||
if (intValue == null) {
|
||||
c.translateExpression(receiver, intType);
|
||||
c.b.i64_const(-1);
|
||||
@@ -317,7 +340,7 @@ class Intrinsifier {
|
||||
}
|
||||
},
|
||||
'~': (c, receiver) {
|
||||
final int? intValue = _extractIntValue(receiver);
|
||||
final int? intValue = extractIntValue(receiver);
|
||||
if (intValue == null) {
|
||||
c.translateExpression(receiver, intType);
|
||||
c.b.i64_const(-1);
|
||||
@@ -327,7 +350,7 @@ class Intrinsifier {
|
||||
}
|
||||
},
|
||||
'toDouble': (c, receiver) {
|
||||
final int? intValue = _extractIntValue(receiver);
|
||||
final int? intValue = extractIntValue(receiver);
|
||||
if (intValue == null) {
|
||||
c.translateExpression(receiver, intType);
|
||||
c.b.f64_convert_i64_s();
|
||||
@@ -1538,6 +1561,125 @@ class Intrinsifier {
|
||||
codeGen.translateExpression(value, w.RefType.i31(nullable: false));
|
||||
b.i31_get_u();
|
||||
return w.NumType.i32;
|
||||
|
||||
case StaticIntrinsic.wasmMemorySize:
|
||||
final memory = _extractMemoryFromCall(node, b);
|
||||
b.memory_size(memory);
|
||||
// Unsigned because memory sizes can't be negative.
|
||||
b.i64_extend_i32_u();
|
||||
return w.NumType.i64;
|
||||
case StaticIntrinsic.wasmMemoryGrow:
|
||||
final memory = _extractMemoryFromCall(node, b);
|
||||
codeGen.translateExpression(
|
||||
node.arguments.positional[1], w.NumType.i64);
|
||||
b.i32_wrap_i64();
|
||||
b.memory_grow(memory);
|
||||
// Signed because memory.grow returns -1 on failure.
|
||||
b.i64_extend_i32_s();
|
||||
return w.NumType.i64;
|
||||
case StaticIntrinsic.wasmMemoryFill:
|
||||
final memory = _extractMemoryFromCall(node, b);
|
||||
// The positional arguments in Dart are value, startOffset, length. The
|
||||
// stack for memory_fill needs to be [..., start, value, length] though.
|
||||
final [_, value, start, length] = node.arguments.positional;
|
||||
codeGen.translateExpression(value, w.NumType.i32);
|
||||
codeGen.translateExpression(start, w.NumType.i64);
|
||||
b.i32_wrap_i64();
|
||||
final startVar = b.addLocal(w.NumType.i32);
|
||||
final valueVar = b.addLocal(w.NumType.i32);
|
||||
|
||||
b
|
||||
..local_set(startVar)
|
||||
..local_set(valueVar)
|
||||
..local_get(startVar)
|
||||
..local_get(valueVar);
|
||||
codeGen.translateExpression(length, w.NumType.i64);
|
||||
b.i32_wrap_i64();
|
||||
b.memory_fill(memory);
|
||||
return codeGen.voidMarker;
|
||||
case StaticIntrinsic.wasmMemoryLoadFloat32:
|
||||
case StaticIntrinsic.wasmMemoryLoadFloat64:
|
||||
case StaticIntrinsic.wasmMemoryLoadInt8:
|
||||
case StaticIntrinsic.wasmMemoryLoadInt16:
|
||||
case StaticIntrinsic.wasmMemoryLoadInt32:
|
||||
case StaticIntrinsic.wasmMemoryLoadInt64:
|
||||
case StaticIntrinsic.wasmMemoryLoadUint8:
|
||||
case StaticIntrinsic.wasmMemoryLoadUint16:
|
||||
case StaticIntrinsic.wasmMemoryLoadUint32:
|
||||
final (:memory, :align, :offset) = _extractMemoryOperands(node, b);
|
||||
|
||||
codeGen.translateExpression(
|
||||
node.arguments.positional[1], w.NumType.i64);
|
||||
b.i32_wrap_i64();
|
||||
|
||||
switch (intrinsic) {
|
||||
case StaticIntrinsic.wasmMemoryLoadFloat32:
|
||||
b.f32_load(memory, offset, align);
|
||||
return w.NumType.f32;
|
||||
case StaticIntrinsic.wasmMemoryLoadFloat64:
|
||||
b.f64_load(memory, offset, align);
|
||||
return w.NumType.f64;
|
||||
case StaticIntrinsic.wasmMemoryLoadInt8:
|
||||
b.i32_load8_s(memory, offset, align);
|
||||
return w.NumType.i32;
|
||||
case StaticIntrinsic.wasmMemoryLoadInt16:
|
||||
b.i32_load16_s(memory, offset, align);
|
||||
return w.NumType.i32;
|
||||
case StaticIntrinsic.wasmMemoryLoadInt32:
|
||||
b.i32_load(memory, offset, align);
|
||||
return w.NumType.i32;
|
||||
case StaticIntrinsic.wasmMemoryLoadInt64:
|
||||
b.i64_load(memory, offset, align);
|
||||
return w.NumType.i64;
|
||||
case StaticIntrinsic.wasmMemoryLoadUint8:
|
||||
b.i32_load8_u(memory, offset, align);
|
||||
return w.NumType.i32;
|
||||
case StaticIntrinsic.wasmMemoryLoadUint16:
|
||||
b.i32_load16_u(memory, offset, align);
|
||||
return w.NumType.i32;
|
||||
case StaticIntrinsic.wasmMemoryLoadUint32:
|
||||
b.i32_load(memory, offset, align);
|
||||
return w.NumType.i32;
|
||||
default:
|
||||
throw AssertionError('unreachable');
|
||||
}
|
||||
case StaticIntrinsic.wasmMemoryStoreFloat32:
|
||||
case StaticIntrinsic.wasmMemoryStoreFloat64:
|
||||
case StaticIntrinsic.wasmMemoryStoreInt8:
|
||||
case StaticIntrinsic.wasmMemoryStoreInt16:
|
||||
case StaticIntrinsic.wasmMemoryStoreInt32:
|
||||
case StaticIntrinsic.wasmMemoryStoreInt64:
|
||||
final (:memory, :align, :offset) = _extractMemoryOperands(node, b);
|
||||
|
||||
codeGen.translateExpression(
|
||||
node.arguments.positional[1], w.NumType.i64);
|
||||
b.i32_wrap_i64();
|
||||
final valueExpression = node.arguments.positional[2];
|
||||
|
||||
switch (intrinsic) {
|
||||
case StaticIntrinsic.wasmMemoryStoreFloat32:
|
||||
codeGen.translateExpression(valueExpression, w.NumType.f32);
|
||||
b.f32_store(memory, offset, align);
|
||||
case StaticIntrinsic.wasmMemoryStoreFloat64:
|
||||
codeGen.translateExpression(valueExpression, w.NumType.f64);
|
||||
b.f64_store(memory, offset, align);
|
||||
case StaticIntrinsic.wasmMemoryStoreInt8:
|
||||
codeGen.translateExpression(valueExpression, w.NumType.i32);
|
||||
b.i32_store8(memory, offset, align);
|
||||
case StaticIntrinsic.wasmMemoryStoreInt16:
|
||||
codeGen.translateExpression(valueExpression, w.NumType.i32);
|
||||
b.i32_store16(memory, offset, align);
|
||||
case StaticIntrinsic.wasmMemoryStoreInt32:
|
||||
codeGen.translateExpression(valueExpression, w.NumType.i32);
|
||||
b.i32_store(memory, offset, align);
|
||||
case StaticIntrinsic.wasmMemoryStoreInt64:
|
||||
codeGen.translateExpression(valueExpression, w.NumType.i64);
|
||||
b.i64_store(memory, offset, align);
|
||||
default:
|
||||
throw AssertionError('unreachable');
|
||||
}
|
||||
|
||||
return codeGen.voidMarker;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2155,9 +2297,40 @@ class Intrinsifier {
|
||||
}
|
||||
return wasmType.unpacked;
|
||||
}
|
||||
|
||||
/// Extracts the memory instance for an intrinsic call on a memory extension.
|
||||
w.Memory _extractMemoryFromCall(
|
||||
Expression expr, w.InstructionsBuilder builder) {
|
||||
// All validated memory calls look like MemoryAccessExtension|size(memory)
|
||||
final memory =
|
||||
(expr as StaticInvocation).arguments.positional[0] as StaticGet;
|
||||
return translator.findMemory(
|
||||
memory.target as Procedure, builder.moduleBuilder);
|
||||
}
|
||||
|
||||
({w.Memory memory, int offset, int align}) _extractMemoryOperands(
|
||||
StaticInvocation call, w.InstructionsBuilder builder) {
|
||||
final memory = _extractMemoryFromCall(call, builder);
|
||||
var align = 0;
|
||||
var offset = 0;
|
||||
|
||||
for (final NamedExpression(:name, :value) in call.arguments.named) {
|
||||
// align and offset are verified to be compile-time constants in
|
||||
// wasm_library_checks.dart
|
||||
if (name == 'align') {
|
||||
align = extractIntValue(value)!;
|
||||
} else if (name == 'offset') {
|
||||
offset = extractIntValue(value)!;
|
||||
} else {
|
||||
throw UnsupportedError('Unhandled named argument: $name');
|
||||
}
|
||||
}
|
||||
|
||||
return (memory: memory, align: align, offset: offset);
|
||||
}
|
||||
}
|
||||
|
||||
int? _extractIntValue(Expression expr) {
|
||||
int? extractIntValue(Expression expr) {
|
||||
if (expr is IntLiteral) {
|
||||
return expr.value;
|
||||
}
|
||||
|
||||
@@ -9,8 +9,6 @@ import 'package:kernel/library_index.dart';
|
||||
/// Kernel nodes for classes and members referenced specifically by the
|
||||
/// compiler.
|
||||
mixin KernelNodes {
|
||||
Component get component;
|
||||
|
||||
LibraryIndex get index;
|
||||
|
||||
CoreTypes get coreTypes;
|
||||
@@ -202,6 +200,14 @@ mixin KernelNodes {
|
||||
late final Field wasmI64ValueField =
|
||||
index.getField("dart:_wasm", "WasmI64", "_value");
|
||||
|
||||
late final Class wasmMemoryClass = index.getClass('dart:_wasm', 'Memory');
|
||||
late final Class wasmMemoryTypeClass =
|
||||
index.getClass('dart:_wasm', 'MemoryType');
|
||||
late final Field wasmLimitsMinimum =
|
||||
index.getField('dart:_wasm', 'Limits', 'minimum');
|
||||
late final Field wasmLimitsMaximum =
|
||||
index.getField('dart:_wasm', 'Limits', 'maximum');
|
||||
|
||||
// dart:_js_helper procedures
|
||||
late final Procedure getInternalizedString =
|
||||
index.getTopLevelProcedure("dart:_js_helper", "getInternalizedString");
|
||||
|
||||
@@ -170,15 +170,12 @@ class DefaultModuleStrategy extends ModuleStrategy {
|
||||
DeferredModuleLoadingMap loadingMap) async {}
|
||||
}
|
||||
|
||||
bool _hasWasmExportPragma(CoreTypes coreTypes, Member m) =>
|
||||
hasPragma(coreTypes, m, 'wasm:export');
|
||||
|
||||
bool containsWasmExport(CoreTypes coreTypes, Library lib) {
|
||||
if (lib.members.any((m) => _hasWasmExportPragma(coreTypes, m))) {
|
||||
if (lib.members.any((m) => hasWasmExportPragma(coreTypes, m))) {
|
||||
return true;
|
||||
}
|
||||
return lib.classes
|
||||
.any((c) => c.members.any((m) => _hasWasmExportPragma(coreTypes, m)));
|
||||
.any((c) => c.members.any((m) => hasWasmExportPragma(coreTypes, m)));
|
||||
}
|
||||
|
||||
abstract class ModuleStrategy {
|
||||
|
||||
@@ -36,6 +36,8 @@ import 'ffi_native_address_transformer.dart' as wasmFfiNativeAddressTrans;
|
||||
import 'ffi_native_transformer.dart' as wasmFfiNativeTrans;
|
||||
import 'records.dart' show RecordShape;
|
||||
import 'transformers.dart' as wasmTrans;
|
||||
import 'util.dart' as util;
|
||||
import 'wasm_library_checks.dart' as wasmChecks;
|
||||
|
||||
enum Mode {
|
||||
regular,
|
||||
@@ -365,6 +367,8 @@ class WasmTarget extends Target {
|
||||
}
|
||||
|
||||
wasmTrans.transformLibraries(libraries, coreTypes, hierarchy);
|
||||
wasmChecks.checkDartWasmApiUseIfImported(
|
||||
libraries, coreTypes, diagnosticReporter);
|
||||
|
||||
awaitTrans.transformLibraries(libraries, hierarchy, coreTypes);
|
||||
}
|
||||
@@ -552,8 +556,6 @@ class WasmVerification extends Verification {
|
||||
}
|
||||
}
|
||||
|
||||
final _dartCoreUri = Uri.parse('dart:core');
|
||||
|
||||
/// Check that `wasm:import` and `wasm:export` pragmas are only used in `dart:`
|
||||
/// libraries and in tests, with the exception of
|
||||
/// `reject_import_export_pragmas` test.
|
||||
@@ -569,31 +571,15 @@ void _checkWasmImportExportPragmas(List<Library> libraries, CoreTypes coreTypes,
|
||||
}
|
||||
|
||||
for (Member member in library.members) {
|
||||
for (Expression annotation in member.annotations) {
|
||||
if (annotation is! ConstantExpression) {
|
||||
continue;
|
||||
}
|
||||
final annotationConstant = annotation.constant;
|
||||
if (annotationConstant is! InstanceConstant) {
|
||||
continue;
|
||||
}
|
||||
final cls = annotationConstant.classNode;
|
||||
if (cls.name == 'pragma' &&
|
||||
cls.enclosingLibrary.importUri == _dartCoreUri) {
|
||||
final pragmaName = annotationConstant
|
||||
.fieldValues[coreTypes.pragmaName.fieldReference];
|
||||
if (pragmaName is StringConstant) {
|
||||
if (pragmaName.value == 'wasm:import' ||
|
||||
pragmaName.value == 'wasm:export') {
|
||||
diagnosticReporter.report(
|
||||
codeWasmImportOrExportInUserCode,
|
||||
annotation.fileOffset,
|
||||
0,
|
||||
library.fileUri,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (util.hasWasmImportPragma(coreTypes, member) ||
|
||||
util.hasWasmExportPragma(coreTypes, member) ||
|
||||
util.hasWasmWeakExportPragma(coreTypes, member)) {
|
||||
diagnosticReporter.report(
|
||||
codeWasmImportOrExportInUserCode,
|
||||
member.fileOffset,
|
||||
0,
|
||||
library.fileUri,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ import 'symbols.dart';
|
||||
import 'tags.dart';
|
||||
import 'types.dart';
|
||||
import 'util.dart' as util;
|
||||
import 'wasm_annotations.dart';
|
||||
|
||||
/// Options controlling the translation.
|
||||
class TranslatorOptions {
|
||||
@@ -149,7 +150,6 @@ class Translator with KernelNodes {
|
||||
late final Exporter exporter;
|
||||
|
||||
// Kernel input and context.
|
||||
@override
|
||||
final Component component;
|
||||
final List<Library> libraries;
|
||||
@override
|
||||
@@ -249,6 +249,7 @@ class Translator with KernelNodes {
|
||||
// Lazily import FFI memory if used.
|
||||
late final w.Memory ffiMemory = mainModule.memories.import("ffi", "memory",
|
||||
options.importSharedMemory, 0, options.sharedMemoryMaxPages);
|
||||
final Map<Procedure, w.Memory> _memories = {};
|
||||
|
||||
/// Maps record shapes to the record class for the shape. Classes generated
|
||||
/// by `record_class_generator` library.
|
||||
@@ -762,6 +763,8 @@ class Translator with KernelNodes {
|
||||
|
||||
late final WasmFunctionImporter _importedFunctions =
|
||||
WasmFunctionImporter(this, 'func');
|
||||
late final WasmMemoryImporter _importedMemories =
|
||||
WasmMemoryImporter(this, 'memory');
|
||||
|
||||
/// Generates a set of instructions to call [function] adding indirection
|
||||
/// if the call crosses a module boundary. Calls the function directly if it
|
||||
@@ -2128,6 +2131,51 @@ class Translator with KernelNodes {
|
||||
_internalizedStringGlobals[(module, s)] = internalizedString;
|
||||
return internalizedString;
|
||||
}
|
||||
|
||||
w.Memory findMemory(
|
||||
Procedure topLevelExternalMemoryGetter, w.ModuleBuilder moduleBuilder) {
|
||||
final inMain = _findMemoryForMainModule(topLevelExternalMemoryGetter);
|
||||
if (moduleBuilder == mainModule) {
|
||||
return inMain;
|
||||
}
|
||||
|
||||
return _importedMemories.get(inMain, moduleBuilder);
|
||||
}
|
||||
|
||||
w.Memory _findMemoryForMainModule(Procedure topLevelExternalMemoryGetter) {
|
||||
return _memories.putIfAbsent(topLevelExternalMemoryGetter, () {
|
||||
final limits =
|
||||
MemoryLimits.readAnnotation(this, topLevelExternalMemoryGetter)!;
|
||||
final exportName = getExportName(topLevelExternalMemoryGetter.reference);
|
||||
final import =
|
||||
util.getWasmImportPragma(coreTypes, topLevelExternalMemoryGetter);
|
||||
|
||||
w.Memory memory;
|
||||
if (import != null) {
|
||||
memory = mainModule.memories.import(import.moduleName, import.itemName,
|
||||
false, limits.minSize, limits.maxSize);
|
||||
} else {
|
||||
memory =
|
||||
mainModule.memories.define(false, limits.minSize, limits.maxSize);
|
||||
}
|
||||
|
||||
if (exportName != null) {
|
||||
mainModule.exports.export(exportName, memory);
|
||||
}
|
||||
return memory;
|
||||
});
|
||||
}
|
||||
|
||||
/// If the member with the reference [target] is exported, get the export
|
||||
/// name.
|
||||
String? getExportName(Reference target) {
|
||||
final member = target.asMember;
|
||||
if (member.reference == target) {
|
||||
return util.getWasmExportPragma(coreTypes, member) ??
|
||||
util.getWasmWeakExportPragma(coreTypes, member);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
class CompilationQueue {
|
||||
@@ -3290,6 +3338,17 @@ class WasmGlobalImporter extends _WasmImporter<w.Global> {
|
||||
}
|
||||
}
|
||||
|
||||
class WasmMemoryImporter extends _WasmImporter<w.Memory> {
|
||||
WasmMemoryImporter(super._translator, super._exportPrefix);
|
||||
|
||||
@override
|
||||
w.Memory _import(w.ModuleBuilder importingModule, w.Memory definition,
|
||||
String moduleName, String importName) {
|
||||
return importingModule.memories.import(moduleName, importName,
|
||||
definition.shared, definition.minSize, definition.maxSize);
|
||||
}
|
||||
}
|
||||
|
||||
class WasmTableImporter extends _WasmImporter<w.Table> {
|
||||
WasmTableImporter(super._translator, super._exportPrefix);
|
||||
|
||||
|
||||
@@ -59,6 +59,55 @@ T? getPragma<T>(CoreTypes coreTypes, Annotatable node, String name,
|
||||
return null;
|
||||
}
|
||||
|
||||
bool hasWasmImportPragma(CoreTypes coreTypes, Member member) {
|
||||
return hasPragma(coreTypes, member, "wasm:import");
|
||||
}
|
||||
|
||||
ImportName? getWasmImportPragma(CoreTypes coreTypes, Member member) {
|
||||
String? importName = getPragma(coreTypes, member, "wasm:import");
|
||||
if (importName != null) {
|
||||
int dot = importName.indexOf('.');
|
||||
if (dot != -1) {
|
||||
assert(!member.isInstanceMember);
|
||||
String module = importName.substring(0, dot);
|
||||
String name = importName.substring(dot + 1);
|
||||
return ImportName(module, name);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
final class ImportName {
|
||||
final String moduleName;
|
||||
final String itemName;
|
||||
|
||||
ImportName(this.moduleName, this.itemName);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return '$moduleName.$itemName';
|
||||
}
|
||||
}
|
||||
|
||||
bool hasWasmExportPragma(CoreTypes coreTypes, Member member) {
|
||||
return hasPragma(coreTypes, member, "wasm:export");
|
||||
}
|
||||
|
||||
bool hasWasmWeakExportPragma(CoreTypes coreTypes, Member member) {
|
||||
return hasPragma(coreTypes, member, "wasm:weak-export");
|
||||
}
|
||||
|
||||
String? getWasmExportPragma(CoreTypes coreTypes, Member member) {
|
||||
return getPragma<String>(coreTypes, member, 'wasm:export',
|
||||
defaultValue: member.name.text);
|
||||
}
|
||||
|
||||
String? getWasmWeakExportPragma(CoreTypes coreTypes, Member member) {
|
||||
return getPragma<String>(coreTypes, member, 'wasm:weak-export',
|
||||
defaultValue: member.name.text);
|
||||
}
|
||||
|
||||
/// Add a `@pragma('wasm:entry-point')` annotation to an annotatable.
|
||||
T addWasmEntryPointPragma<T extends Annotatable>(T node, CoreTypes coreTypes) =>
|
||||
addPragma(node, 'wasm:entry-point', coreTypes);
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
// Copyright (c) 2026, 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 'package:kernel/ast.dart';
|
||||
|
||||
import 'kernel_nodes.dart';
|
||||
import 'util.dart' as util;
|
||||
|
||||
enum ExternType { memory }
|
||||
|
||||
final class MemoryLimits {
|
||||
final int minSize;
|
||||
final int? maxSize;
|
||||
|
||||
MemoryLimits({required this.minSize, this.maxSize});
|
||||
|
||||
/// Read the `MemoryType` annotation on a member.
|
||||
static MemoryLimits? readAnnotation(KernelNodes nodes, Member member) {
|
||||
final memoryType = util.getPragma<InstanceConstant>(
|
||||
nodes.coreTypes, member, 'wasm:memory-type');
|
||||
if (memoryType == null ||
|
||||
memoryType.classNode != nodes.wasmMemoryTypeClass) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final (minSize, maxSize) = _readMemoryType(nodes, memoryType);
|
||||
|
||||
return MemoryLimits(
|
||||
minSize: minSize,
|
||||
maxSize: maxSize,
|
||||
);
|
||||
}
|
||||
|
||||
static (int, int?) _readMemoryType(
|
||||
KernelNodes nodes, InstanceConstant constant) {
|
||||
final limits = constant.fieldValues.values.single;
|
||||
return _readLimits(nodes, limits as InstanceConstant);
|
||||
}
|
||||
|
||||
static (int, int?) _readLimits(KernelNodes nodes, InstanceConstant constant) {
|
||||
final minimum = (constant
|
||||
.fieldValues[nodes.wasmLimitsMinimum.fieldReference] as IntConstant)
|
||||
.value;
|
||||
final maximumConstant =
|
||||
constant.fieldValues[nodes.wasmLimitsMaximum.fieldReference];
|
||||
final maximum = switch (maximumConstant) {
|
||||
IntConstant(:final value) => value,
|
||||
_ => null,
|
||||
};
|
||||
|
||||
return (minimum, maximum);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
// Copyright (c) 2026, 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 'package:front_end/src/codes/cfe_codes.dart'
|
||||
show
|
||||
codeConstEvalNonConstantVariableGet,
|
||||
codeWasmExternMemoryMissingAnnotation,
|
||||
codeWasmExternInvalidLoad,
|
||||
codeWasmExternInvalidTarget,
|
||||
codeWasmIntrinsicTearOff;
|
||||
import 'package:kernel/ast.dart';
|
||||
import 'package:kernel/core_types.dart';
|
||||
import 'package:kernel/library_index.dart';
|
||||
import 'package:kernel/target/targets.dart';
|
||||
|
||||
import 'intrinsics.dart';
|
||||
import 'kernel_nodes.dart';
|
||||
import 'wasm_annotations.dart';
|
||||
|
||||
/// Validates (a subset of) `dart:_wasm` usages.
|
||||
///
|
||||
/// So far, we validate usages of:
|
||||
/// * `Memory` and `MemoryAccessExtension`.
|
||||
void checkDartWasmApiUseIfImported(
|
||||
Iterable<Library> libraries,
|
||||
CoreTypes coreTypes,
|
||||
DiagnosticReporter diagnosticReporter,
|
||||
) {
|
||||
final checks = _DartWasmLibraryChecks(coreTypes, diagnosticReporter);
|
||||
for (final library in libraries) {
|
||||
// Skip the check if the library doesn't import dart:_wasm.
|
||||
// TODO: This misses libraries importing dart:_wasm through an export.
|
||||
for (final dependency in library.dependencies) {
|
||||
if (!dependency.isImport) continue;
|
||||
if (dependency.targetLibrary == checks.wasmLibrary) {
|
||||
library.accept(checks);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class _DartWasmLibraryChecks extends RecursiveVisitor with KernelNodes {
|
||||
Member? _currentMember;
|
||||
|
||||
final DiagnosticReporter _diagnosticReporter;
|
||||
|
||||
@override
|
||||
final CoreTypes coreTypes;
|
||||
|
||||
@override
|
||||
LibraryIndex get index => coreTypes.index;
|
||||
|
||||
_DartWasmLibraryChecks(this.coreTypes, this._diagnosticReporter);
|
||||
|
||||
@override
|
||||
void visitLibrary(Library library) {
|
||||
if (library == wasmLibrary) {
|
||||
// The CFE generates getters to tear off extension methods, which look
|
||||
// like illegal dynamic invocations to this visitor. We verify that
|
||||
// tearoffs aren't used, but don't visit the source library to avoid
|
||||
// false-positives here.
|
||||
return;
|
||||
}
|
||||
|
||||
library.visitChildren(this);
|
||||
}
|
||||
|
||||
@override
|
||||
void defaultMember(Member node) {
|
||||
_currentMember = node;
|
||||
node.visitChildren(this);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitProcedure(Procedure node) {
|
||||
_currentMember = node;
|
||||
|
||||
if (_categorizeWasmExtern(node) == ExternType.memory) {
|
||||
final parsed = MemoryLimits.readAnnotation(this, node);
|
||||
if (parsed == null) {
|
||||
_diagnosticReporter.report(codeWasmExternMemoryMissingAnnotation,
|
||||
node.fileOffset, 1, node.fileUri);
|
||||
}
|
||||
}
|
||||
|
||||
node.visitChildren(this);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitStaticInvocation(StaticInvocation node) {
|
||||
final target = node.target;
|
||||
if (target.enclosingLibrary == wasmLibrary &&
|
||||
target.name.text.startsWith('MemoryAccessExtension|')) {
|
||||
final args = node.arguments;
|
||||
final memory = args.positional[0];
|
||||
final isTearOff = target.function.returnType is FunctionType;
|
||||
|
||||
if (isTearOff) {
|
||||
// Reference to a getter generated to implement tear offs, e.g. in
|
||||
// memory.fill (as opposed to a direct memory.fill(a, b, c) call).
|
||||
_diagnosticReporter.report(codeWasmIntrinsicTearOff, node.fileOffset, 1,
|
||||
_currentMember!.fileUri);
|
||||
}
|
||||
|
||||
if (_isWasmMemoryRef(memory)) {
|
||||
for (final positional in args.positional.skip(1)) {
|
||||
positional.accept(this);
|
||||
}
|
||||
|
||||
for (final named in args.named) {
|
||||
named.accept(this);
|
||||
|
||||
// The parameter to the align and offset method should be a compile-
|
||||
// time constant.
|
||||
if (named.name case 'align' || 'offset') {
|
||||
if (extractIntValue(named.value) == null) {
|
||||
_diagnosticReporter.report(
|
||||
codeConstEvalNonConstantVariableGet.withArguments(
|
||||
nameOKEmpty: named.name),
|
||||
named.value.fileOffset,
|
||||
1,
|
||||
_currentMember!.fileUri);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
} else {
|
||||
_diagnosticReporter.report(codeWasmExternInvalidTarget, node.fileOffset,
|
||||
0, _currentMember!.fileUri);
|
||||
}
|
||||
}
|
||||
|
||||
super.visitStaticInvocation(node);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitStaticGet(StaticGet node) {
|
||||
if (_isWasmMemoryRef(node)) {
|
||||
// The only valid use of a wasm element is to call an intrinsic extension
|
||||
// method on it, in which case an outer visit method would have skipped
|
||||
// this node. This get is invalid.
|
||||
_diagnosticReporter.report(codeWasmExternInvalidLoad, node.fileOffset, 1,
|
||||
_currentMember!.fileUri);
|
||||
}
|
||||
|
||||
super.visitStaticGet(node);
|
||||
}
|
||||
|
||||
/// Checks whether the getter defines an external WebAssembly member that can
|
||||
/// only be used through intrinsics.
|
||||
ExternType? _categorizeWasmExtern(Member getter) {
|
||||
if (getter is Procedure && getter.isExternal) {
|
||||
final type = getter.function.returnType;
|
||||
|
||||
if (type is InterfaceType) {
|
||||
if (type.classNode == wasmMemoryClass) {
|
||||
return ExternType.memory;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
bool _isWasmMemoryRef(Expression expr) {
|
||||
return expr is StaticGet &&
|
||||
_categorizeWasmExtern(expr.target) == ExternType.memory;
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,7 @@
|
||||
(result (ref $MyConstClass))))
|
||||
(table $static0-0 (export "static0-0") 2 (ref null $type0))
|
||||
(table $static1-0 (export "static1-0") 1 (ref null $type2))
|
||||
(global $"C381 \"bad\"" (ref $JSStringImpl) <...>)
|
||||
(global $"C383 \"bad\"" (ref $JSStringImpl) <...>)
|
||||
(func $"mainImpl <noInline>" (param $var0 i32)
|
||||
(local $var1 (ref $MyConstClass))
|
||||
i64.const 0
|
||||
@@ -35,7 +35,7 @@
|
||||
ref.eq
|
||||
i32.eqz
|
||||
if
|
||||
global.get $"C381 \"bad\""
|
||||
global.get $"C383 \"bad\""
|
||||
call $Error._throwWithCurrentStackTrace
|
||||
unreachable
|
||||
end
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
(result (ref $MyConstClass))))
|
||||
(global $.h0-nonshared-const (import "" "h0-nonshared-const") (ref extern))
|
||||
(table $module0.static1-0 (import "module0" "static1-0") 1 (ref null $type0))
|
||||
(global $"C503 MyConstClass" (ref $MyConstClass)
|
||||
(i32.const 118)
|
||||
(global $"C505 MyConstClass" (ref $MyConstClass)
|
||||
(i32.const 120)
|
||||
(i32.const 0)
|
||||
(i32.const 4)
|
||||
(i32.const 0)
|
||||
@@ -27,7 +27,7 @@
|
||||
(func $"modH0Use <noInline>" (param $var0 i32) (result (ref $MyConstClass))
|
||||
local.get $var0
|
||||
if (result (ref $MyConstClass))
|
||||
global.get $"C503 MyConstClass"
|
||||
global.get $"C505 MyConstClass"
|
||||
else
|
||||
i32.const 0
|
||||
call_indirect $module0.static1-0 (result (ref $MyConstClass))
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
(field $field0 i32)
|
||||
(field $field1 (mut i32)))))
|
||||
(global $.shared-const (import "" "shared-const") (ref extern))
|
||||
(global $"C501 MyConstClass" (ref $MyConstClass)
|
||||
(i32.const 118)
|
||||
(global $"C503 MyConstClass" (ref $MyConstClass)
|
||||
(i32.const 120)
|
||||
(i32.const 0)
|
||||
(i32.const 4)
|
||||
(i32.const 0)
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
(result (ref $MyConstClass))))
|
||||
(global $.h1-nonshared-const (import "" "h1-nonshared-const") (ref extern))
|
||||
(table $module0.static1-0 (import "module0" "static1-0") 1 (ref null $type0))
|
||||
(global $"C499 MyConstClass" (ref $MyConstClass)
|
||||
(i32.const 118)
|
||||
(global $"C501 MyConstClass" (ref $MyConstClass)
|
||||
(i32.const 120)
|
||||
(i32.const 0)
|
||||
(i32.const 4)
|
||||
(i32.const 0)
|
||||
@@ -27,7 +27,7 @@
|
||||
(func $"modH1Use <noInline>" (param $var0 i32) (result (ref $MyConstClass))
|
||||
local.get $var0
|
||||
if (result (ref $MyConstClass))
|
||||
global.get $"C499 MyConstClass"
|
||||
global.get $"C501 MyConstClass"
|
||||
else
|
||||
i32.const 0
|
||||
call_indirect $module0.static1-0 (result (ref $MyConstClass))
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
(type $_InterfaceType <...>)
|
||||
(type $type0 <...>)
|
||||
(table $static0-0 (export "static0-0") 1 (ref null $type0))
|
||||
(global $"C417 _InterfaceType" (ref $_InterfaceType) <...>)
|
||||
(global $"C419 _InterfaceType" (ref $_InterfaceType) <...>)
|
||||
(func $_loaded implicit getter (result (ref $_DefaultSet&_HashFieldBase&SetMixin)) <...>)
|
||||
(func $"useFoo <noInline>"
|
||||
call $"useFooAsType <noInline>"
|
||||
@@ -13,7 +13,7 @@
|
||||
call $_DefaultSet&_HashFieldBase&SetMixin&_HashBase&_OperatorEqualsAndHashCode&_LinkedHashSetMixin.contains
|
||||
i32.eqz
|
||||
if
|
||||
i32.const 50
|
||||
i32.const 51
|
||||
i32.const 0
|
||||
ref.null none
|
||||
i64.const 0
|
||||
@@ -26,7 +26,7 @@
|
||||
drop
|
||||
)
|
||||
(func $"useFooAsType <noInline>"
|
||||
global.get $"C417 _InterfaceType"
|
||||
global.get $"C419 _InterfaceType"
|
||||
call $print
|
||||
drop
|
||||
)
|
||||
|
||||
@@ -7,14 +7,14 @@
|
||||
(type $JSStringImpl <...>)
|
||||
(type $Object <...>)
|
||||
(global $".Foo called " (import "" "Foo called ") (ref extern))
|
||||
(global $"C465 \"Foo called \"" (ref $JSStringImpl)
|
||||
(global $"C467 \"Foo called \"" (ref $JSStringImpl)
|
||||
(i32.const 4)
|
||||
(i32.const 0)
|
||||
(global.get $".Foo called ")
|
||||
(struct.new $JSStringImpl))
|
||||
(func $"useFooAsObject <noInline>" (result (ref null $#Top))
|
||||
(local $var0 (ref $Foo))
|
||||
i32.const 116
|
||||
i32.const 118
|
||||
i32.const 0
|
||||
i64.const 0
|
||||
struct.new $Foo
|
||||
|
||||
@@ -15,30 +15,30 @@
|
||||
(type $_TopType <...>)
|
||||
(func $print (import "module0" "func3") (param (ref null $#Top)) (result (ref null $#Top)))
|
||||
(global $"C1 WasmArray<_Type>[0]" (import "module0" "global1") (ref $Array<_Type>))
|
||||
(global $"C317 WasmArray<_NamedParameter>[0]" (import "module0" "global4") (ref $Array<_NamedParameter>))
|
||||
(global $"C342 _TopType" (import "module0" "global2") (ref $_TopType))
|
||||
(global $"C64 WasmArray<_Type>[1]" (import "module0" "global3") (ref $Array<_Type>))
|
||||
(global $"C319 WasmArray<_NamedParameter>[0]" (import "module0" "global4") (ref $Array<_NamedParameter>))
|
||||
(global $"C344 _TopType" (import "module0" "global2") (ref $_TopType))
|
||||
(global $"C63 WasmArray<_Type>[1]" (import "module0" "global3") (ref $Array<_Type>))
|
||||
(global $.globalH0Foo (import "" "globalH0Foo") (ref extern))
|
||||
(table $module0.constant-table0 (import "module0" "constant-table0") 1 (ref null $_FunctionType))
|
||||
(global $"C476 globalH0Foo tear-off" (mut (ref null $#Closure-0-1))
|
||||
(global $"C478 globalH0Foo tear-off" (mut (ref null $#Closure-0-1))
|
||||
(ref.null none))
|
||||
(global $"C477 H0" (mut (ref null $H0))
|
||||
(global $"C479 H0" (mut (ref null $H0))
|
||||
(ref.null none))
|
||||
(global $"C478 \"globalH0Foo\"" (ref $JSStringImpl)
|
||||
(global $"C480 \"globalH0Foo\"" (ref $JSStringImpl)
|
||||
(i32.const 4)
|
||||
(i32.const 0)
|
||||
(global.get $.globalH0Foo)
|
||||
(struct.new $JSStringImpl))
|
||||
(global $global0 (ref $#DummyStruct) <...>)
|
||||
(global $global2 (ref $#Vtable-0-1) <...>)
|
||||
(func $"C477 H0 (lazy initializer)}" (result (ref $H0))
|
||||
(func $"C479 H0 (lazy initializer)}" (result (ref $H0))
|
||||
(local $var0 (ref $_FunctionType))
|
||||
(local $var1 (ref $#Closure-0-1))
|
||||
(local $var2 (ref $H0))
|
||||
i32.const 116
|
||||
i32.const 118
|
||||
i32.const 0
|
||||
block $label0 (result (ref $#Closure-0-1))
|
||||
global.get $"C476 globalH0Foo tear-off"
|
||||
global.get $"C478 globalH0Foo tear-off"
|
||||
br_on_non_null $label0
|
||||
i32.const 38
|
||||
i32.const 0
|
||||
@@ -55,10 +55,10 @@
|
||||
i64.const 0
|
||||
global.get $"C1 WasmArray<_Type>[0]"
|
||||
global.get $"C1 WasmArray<_Type>[0]"
|
||||
global.get $"C342 _TopType"
|
||||
global.get $"C64 WasmArray<_Type>[1]"
|
||||
global.get $"C344 _TopType"
|
||||
global.get $"C63 WasmArray<_Type>[1]"
|
||||
i64.const 1
|
||||
global.get $"C317 WasmArray<_NamedParameter>[0]"
|
||||
global.get $"C319 WasmArray<_NamedParameter>[0]"
|
||||
struct.new $_FunctionType
|
||||
local.tee $var0
|
||||
table.set $module0.constant-table0
|
||||
@@ -66,20 +66,20 @@
|
||||
end $label1
|
||||
struct.new $#Closure-0-1
|
||||
local.tee $var1
|
||||
global.set $"C476 globalH0Foo tear-off"
|
||||
global.set $"C478 globalH0Foo tear-off"
|
||||
local.get $var1
|
||||
end $label0
|
||||
struct.new $H0
|
||||
local.tee $var2
|
||||
global.set $"C477 H0"
|
||||
global.set $"C479 H0"
|
||||
local.get $var2
|
||||
)
|
||||
(func $"globalH0Foo tear-off trampoline" (param $var0 (ref struct)) (param $var1 (ref null $#Top)) (result (ref null $#Top))
|
||||
global.get $"C478 \"globalH0Foo\""
|
||||
global.get $"C480 \"globalH0Foo\""
|
||||
call $print
|
||||
)
|
||||
(func $globalH0Foo (param $var0 i64) (result (ref null $#Top))
|
||||
global.get $"C478 \"globalH0Foo\""
|
||||
global.get $"C480 \"globalH0Foo\""
|
||||
call $print
|
||||
)
|
||||
)
|
||||
@@ -24,55 +24,55 @@
|
||||
(func $JSStringImpl._interpolate (import "module0" "func4") (param (ref $Array<Object?>)) (result (ref $JSStringImpl)))
|
||||
(func $print (import "module0" "func3") (param (ref null $#Top)) (result (ref null $#Top)))
|
||||
(global $"C1 WasmArray<_Type>[0]" (import "module0" "global1") (ref $Array<_Type>))
|
||||
(global $"C15 _InterfaceType" (import "module0" "global7") (ref $_InterfaceType))
|
||||
(global $"C317 WasmArray<_NamedParameter>[0]" (import "module0" "global4") (ref $Array<_NamedParameter>))
|
||||
(global $"C342 _TopType" (import "module0" "global2") (ref $_TopType))
|
||||
(global $"C64 WasmArray<_Type>[1]" (import "module0" "global3") (ref $Array<_Type>))
|
||||
(global $"C8 \")\"" (import "module0" "global0") (ref $JSStringImpl))
|
||||
(global $"C21 \")\"" (import "module0" "global0") (ref $JSStringImpl))
|
||||
(global $"C28 _InterfaceType" (import "module0" "global7") (ref $_InterfaceType))
|
||||
(global $"C319 WasmArray<_NamedParameter>[0]" (import "module0" "global4") (ref $Array<_NamedParameter>))
|
||||
(global $"C344 _TopType" (import "module0" "global2") (ref $_TopType))
|
||||
(global $"C63 WasmArray<_Type>[1]" (import "module0" "global3") (ref $Array<_Type>))
|
||||
(global $.globalH1Bar< (import "" "globalH1Bar<") (ref extern))
|
||||
(table $module0.constant-table0 (import "module0" "constant-table0") 1 (ref null $_FunctionType))
|
||||
(global $"C470 _FunctionType" (ref $_FunctionType) <...>)
|
||||
(global $"C471 globalH1Foo tear-off" (mut (ref null $#Closure-1-1))
|
||||
(global $"C472 _FunctionType" (ref $_FunctionType) <...>)
|
||||
(global $"C473 globalH1Foo tear-off" (mut (ref null $#Closure-1-1))
|
||||
(ref.null none))
|
||||
(global $"C472 InstantiationConstant(globalH1Foo<int>)" (mut (ref null $#Closure-0-1))
|
||||
(global $"C474 InstantiationConstant(globalH1Foo<int>)" (mut (ref null $#Closure-0-1))
|
||||
(ref.null none))
|
||||
(global $"C473 H1" (mut (ref null $H1))
|
||||
(global $"C475 H1" (mut (ref null $H1))
|
||||
(ref.null none))
|
||||
(global $"C474 \"globalH1Bar<\"" (ref $JSStringImpl)
|
||||
(global $"C476 \"globalH1Bar<\"" (ref $JSStringImpl)
|
||||
(i32.const 4)
|
||||
(i32.const 0)
|
||||
(global.get $.globalH1Bar<)
|
||||
(struct.new $JSStringImpl))
|
||||
(global $"C475 \">(\"" (ref $JSStringImpl) <...>)
|
||||
(global $"C477 \">(\"" (ref $JSStringImpl) <...>)
|
||||
(global $global0 (ref $#DummyStruct) <...>)
|
||||
(global $global2 (ref $#Vtable-1-1) <...>)
|
||||
(func $#dummy function (ref struct) -> (ref null #Top) (param $var0 (ref struct)) (result (ref null $#Top)) <...>)
|
||||
(func $"C473 H1 (lazy initializer)}" (result (ref $H1))
|
||||
(func $"C475 H1 (lazy initializer)}" (result (ref $H1))
|
||||
(local $var0 (ref $#Closure-1-1))
|
||||
(local $var1 (ref $_FunctionType))
|
||||
(local $var2 (ref $#Closure-0-1))
|
||||
(local $var3 (ref $H1))
|
||||
i32.const 117
|
||||
i32.const 119
|
||||
i32.const 0
|
||||
block $label0 (result (ref $#Closure-0-1))
|
||||
global.get $"C472 InstantiationConstant(globalH1Foo<int>)"
|
||||
global.get $"C474 InstantiationConstant(globalH1Foo<int>)"
|
||||
br_on_non_null $label0
|
||||
i32.const 38
|
||||
i32.const 0
|
||||
block $label1 (result (ref $#Closure-1-1))
|
||||
global.get $"C471 globalH1Foo tear-off"
|
||||
global.get $"C473 globalH1Foo tear-off"
|
||||
br_on_non_null $label1
|
||||
i32.const 38
|
||||
i32.const 0
|
||||
global.get $global0
|
||||
global.get $global2
|
||||
global.get $"C470 _FunctionType"
|
||||
global.get $"C472 _FunctionType"
|
||||
struct.new $#Closure-1-1
|
||||
local.tee $var0
|
||||
global.set $"C471 globalH1Foo tear-off"
|
||||
global.set $"C473 globalH1Foo tear-off"
|
||||
local.get $var0
|
||||
end $label1
|
||||
global.get $"C15 _InterfaceType"
|
||||
global.get $"C28 _InterfaceType"
|
||||
struct.new $#InstantiationContext-1-1
|
||||
ref.func $"#dummy function (ref struct) -> (ref null #Top)"
|
||||
ref.func $"instantiation constant trampoline"
|
||||
@@ -88,10 +88,10 @@
|
||||
i64.const 0
|
||||
global.get $"C1 WasmArray<_Type>[0]"
|
||||
global.get $"C1 WasmArray<_Type>[0]"
|
||||
global.get $"C342 _TopType"
|
||||
global.get $"C64 WasmArray<_Type>[1]"
|
||||
global.get $"C344 _TopType"
|
||||
global.get $"C63 WasmArray<_Type>[1]"
|
||||
i64.const 1
|
||||
global.get $"C317 WasmArray<_NamedParameter>[0]"
|
||||
global.get $"C319 WasmArray<_NamedParameter>[0]"
|
||||
struct.new $_FunctionType
|
||||
local.tee $var1
|
||||
table.set $module0.constant-table0
|
||||
@@ -99,20 +99,20 @@
|
||||
end $label2
|
||||
struct.new $#Closure-0-1
|
||||
local.tee $var2
|
||||
global.set $"C472 InstantiationConstant(globalH1Foo<int>)"
|
||||
global.set $"C474 InstantiationConstant(globalH1Foo<int>)"
|
||||
local.get $var2
|
||||
end $label0
|
||||
struct.new $H1
|
||||
local.tee $var3
|
||||
global.set $"C473 H1"
|
||||
global.set $"C475 H1"
|
||||
local.get $var3
|
||||
)
|
||||
(func $"globalH1Foo tear-off trampoline" (param $var0 (ref struct)) (param $var1 (ref $_Type)) (param $var2 (ref null $#Top)) (result (ref null $#Top))
|
||||
global.get $"C474 \"globalH1Bar<\""
|
||||
global.get $"C476 \"globalH1Bar<\""
|
||||
local.get $var1
|
||||
global.get $"C475 \">(\""
|
||||
global.get $"C477 \">(\""
|
||||
local.get $var2
|
||||
global.get $"C8 \")\""
|
||||
global.get $"C21 \")\""
|
||||
array.new_fixed $Array<Object?> 5
|
||||
call $JSStringImpl._interpolate
|
||||
call $print
|
||||
@@ -121,21 +121,21 @@
|
||||
(func $"modH1UseH1 <noInline>" (result (ref null $#Top))
|
||||
(local $var0 (ref $#Closure-0-1))
|
||||
block $label0 (result (ref $H1))
|
||||
global.get $"C473 H1"
|
||||
global.get $"C475 H1"
|
||||
br_on_non_null $label0
|
||||
call $"C473 H1 (lazy initializer)}"
|
||||
call $"C475 H1 (lazy initializer)}"
|
||||
end $label0
|
||||
call $print
|
||||
drop
|
||||
block $label1 (result (ref $H1))
|
||||
global.get $"C473 H1"
|
||||
global.get $"C475 H1"
|
||||
br_on_non_null $label1
|
||||
call $"C473 H1 (lazy initializer)}"
|
||||
call $"C475 H1 (lazy initializer)}"
|
||||
end $label1
|
||||
struct.get $H1 $fun
|
||||
local.tee $var0
|
||||
struct.get $#Closure-0-1 $context
|
||||
i32.const 86
|
||||
i32.const 69
|
||||
i64.const 1
|
||||
struct.new $BoxedInt
|
||||
local.get $var0
|
||||
|
||||
@@ -7,17 +7,17 @@
|
||||
(global $".Foo1.doitDispatch(" (import "" "Foo1.doitDispatch(") (ref extern))
|
||||
(global $".FooBase(" (import "" "FooBase(") (ref extern))
|
||||
(table $static0-0 (export "static0-0") 1 (ref null $type0))
|
||||
(global $"C383 \"FooBase(\"" (ref $JSStringImpl)
|
||||
(global $"C385 \"FooBase(\"" (ref $JSStringImpl)
|
||||
(i32.const 4)
|
||||
(i32.const 0)
|
||||
(global.get $".FooBase(")
|
||||
(struct.new $JSStringImpl))
|
||||
(global $"C384 \"Foo1.doitDispatch(\"" (ref $JSStringImpl)
|
||||
(global $"C386 \"Foo1.doitDispatch(\"" (ref $JSStringImpl)
|
||||
(i32.const 4)
|
||||
(i32.const 0)
|
||||
(global.get $".Foo1.doitDispatch(")
|
||||
(struct.new $JSStringImpl))
|
||||
(global $"C385 \"Foo0.doitDispatch(\"" (ref $JSStringImpl)
|
||||
(global $"C387 \"Foo0.doitDispatch(\"" (ref $JSStringImpl)
|
||||
(i32.const 4)
|
||||
(i32.const 0)
|
||||
(global.get $".Foo0.doitDispatch(")
|
||||
@@ -28,7 +28,7 @@
|
||||
(func $"foo0 <noInline>"
|
||||
call $"runtimeTrue implicit getter"
|
||||
if (result (ref $Object))
|
||||
i32.const 118
|
||||
i32.const 120
|
||||
i32.const 0
|
||||
struct.new $Object
|
||||
else
|
||||
@@ -46,7 +46,7 @@
|
||||
)
|
||||
(func $runtimeTrue implicit getter (result i32) <...>)
|
||||
(func $Foo0.doitDispatch (param $var0 (ref $Object)) (param $var1 (ref null $#Top)) (result (ref null $#Top))
|
||||
global.get $"C385 \"Foo0.doitDispatch(\""
|
||||
global.get $"C387 \"Foo0.doitDispatch(\""
|
||||
local.get $var1
|
||||
global.get $"C8 \")\""
|
||||
call $JSStringImpl._interpolate3
|
||||
@@ -58,7 +58,7 @@
|
||||
)
|
||||
(func $Foo1 (result (ref $Object)) <...>)
|
||||
(func $Foo1.doitDispatch (export "func1") (param $var0 (ref $Object)) (param $var1 (ref null $#Top)) (result (ref null $#Top))
|
||||
global.get $"C384 \"Foo1.doitDispatch(\""
|
||||
global.get $"C386 \"Foo1.doitDispatch(\""
|
||||
local.get $var1
|
||||
global.get $"C8 \")\""
|
||||
call $JSStringImpl._interpolate3
|
||||
@@ -69,7 +69,7 @@
|
||||
ref.null none
|
||||
)
|
||||
(func $FooBase.doitDispatch (param $var0 (ref null $#Top))
|
||||
global.get $"C383 \"FooBase(\""
|
||||
global.get $"C385 \"FooBase(\""
|
||||
local.get $var0
|
||||
global.get $"C8 \")\""
|
||||
call $JSStringImpl._interpolate3
|
||||
|
||||
@@ -8,14 +8,14 @@
|
||||
(func $JSStringImpl._interpolate3 (import "module0" "func2") (param (ref null $#Top) (ref null $#Top) (ref null $#Top)) (result (ref $JSStringImpl)))
|
||||
(func $print (import "module0" "func3") (param (ref null $#Top)) (result (ref null $#Top)))
|
||||
(global $".Foo1.doitDevirt(" (import "" "Foo1.doitDevirt(") (ref extern))
|
||||
(global $"C315 1" (import "module0" "global1") (ref $BoxedInt))
|
||||
(global $"C344 2" (import "module0" "global3") (ref $BoxedInt))
|
||||
(global $"C383 \"FooBase(\"" (import "module0" "global5") (ref $JSStringImpl))
|
||||
(global $"C317 1" (import "module0" "global1") (ref $BoxedInt))
|
||||
(global $"C346 2" (import "module0" "global3") (ref $BoxedInt))
|
||||
(global $"C385 \"FooBase(\"" (import "module0" "global5") (ref $JSStringImpl))
|
||||
(global $"C8 \")\"" (import "module0" "global4") (ref $JSStringImpl))
|
||||
(global $baseObj (import "module0" "global0") (ref null $Object))
|
||||
(global $foo1Obj (import "module0" "global2") (ref null $Object))
|
||||
(table $module0.dispatch0 (import "module0" "dispatch0") 760 funcref)
|
||||
(global $"C502 \"Foo1.doitDevirt(\"" (ref $JSStringImpl)
|
||||
(table $module0.dispatch0 (import "module0" "dispatch0") 773 funcref)
|
||||
(global $"C504 \"Foo1.doitDevirt(\"" (ref $JSStringImpl)
|
||||
(i32.const 4)
|
||||
(i32.const 0)
|
||||
(global.get $".Foo1.doitDevirt(")
|
||||
@@ -29,10 +29,10 @@
|
||||
br $label0
|
||||
end $label1
|
||||
local.tee $var0
|
||||
global.get $"C315 1"
|
||||
global.get $"C317 1"
|
||||
local.get $var0
|
||||
struct.get $Object $field0
|
||||
i32.const 508
|
||||
i32.const 444
|
||||
i32.add
|
||||
call_indirect $module0.dispatch0 (param (ref $Object) (ref null $#Top)) (result (ref null $#Top))
|
||||
drop
|
||||
@@ -41,7 +41,7 @@
|
||||
br_on_non_null $label2
|
||||
br $label0
|
||||
end $label2
|
||||
global.get $"C344 2"
|
||||
global.get $"C346 2"
|
||||
call $Foo1.doitDispatch
|
||||
drop
|
||||
block $label3 (result (ref $Object))
|
||||
@@ -63,14 +63,14 @@
|
||||
unreachable
|
||||
)
|
||||
(func $Foo1.doitDevirt (param $var0 (ref $Object))
|
||||
global.get $"C502 \"Foo1.doitDevirt(\""
|
||||
global.get $"C315 1"
|
||||
global.get $"C504 \"Foo1.doitDevirt(\""
|
||||
global.get $"C317 1"
|
||||
global.get $"C8 \")\""
|
||||
call $JSStringImpl._interpolate3
|
||||
call $print
|
||||
drop
|
||||
global.get $"C383 \"FooBase(\""
|
||||
global.get $"C315 1"
|
||||
global.get $"C385 \"FooBase(\""
|
||||
global.get $"C317 1"
|
||||
global.get $"C8 \")\""
|
||||
call $JSStringImpl._interpolate3
|
||||
call $print
|
||||
|
||||
@@ -14,29 +14,29 @@
|
||||
(table $static2-0 (export "static2-0") 4 (ref null $type4))
|
||||
(table $static3-0 (export "static3-0") 4 (ref null $type6))
|
||||
(global $"C12 0" (ref $BoxedInt) <...>)
|
||||
(global $"C390 \"FooConstBase(\"" (ref $JSStringImpl)
|
||||
(global $"C392 \"FooConstBase(\"" (ref $JSStringImpl)
|
||||
(i32.const 4)
|
||||
(i32.const 0)
|
||||
(global.get $".FooConstBase(")
|
||||
(struct.new $JSStringImpl))
|
||||
(global $"C391 FooConst0" (ref $Object)
|
||||
(i32.const 118)
|
||||
(global $"C393 FooConst0" (ref $Object)
|
||||
(i32.const 120)
|
||||
(i32.const 0)
|
||||
(struct.new $Object))
|
||||
(global $"C392 \"FooConst0(\"" (ref $JSStringImpl)
|
||||
(global $"C394 \"FooConst0(\"" (ref $JSStringImpl)
|
||||
(i32.const 4)
|
||||
(i32.const 0)
|
||||
(global.get $".FooConst0(")
|
||||
(struct.new $JSStringImpl))
|
||||
(global $"C508 \"foo0Code(\"" (ref $JSStringImpl) <...>)
|
||||
(global $"C510 \"foo0Code(\"" (ref $JSStringImpl) <...>)
|
||||
(global $"C8 \")\"" (ref $JSStringImpl) <...>)
|
||||
(global $fooGlobal0 (mut (ref null $#Top))
|
||||
(ref.null none))
|
||||
(func $"foo0Code <noInline>" (export "func12") (param $var0 (ref null $#Top)) (result (ref null $#Top))
|
||||
global.get $"C391 FooConst0"
|
||||
global.get $"C393 FooConst0"
|
||||
call $print
|
||||
drop
|
||||
global.get $"C508 \"foo0Code(\""
|
||||
global.get $"C510 \"foo0Code(\""
|
||||
local.get $var0
|
||||
global.get $"C8 \")\""
|
||||
call $JSStringImpl._interpolate3
|
||||
@@ -47,7 +47,7 @@
|
||||
ref.null none
|
||||
)
|
||||
(func $FooConst0.doit (param $var0 (ref $Object)) (param $var1 (ref null $#Top)) (result (ref null $#Top))
|
||||
global.get $"C392 \"FooConst0(\""
|
||||
global.get $"C394 \"FooConst0(\""
|
||||
local.get $var1
|
||||
global.get $"C8 \")\""
|
||||
call $JSStringImpl._interpolate3
|
||||
@@ -60,7 +60,7 @@
|
||||
ref.null none
|
||||
)
|
||||
(func $FooConstBase.doit (export "func14") (param $var0 (ref $Object)) (param $var1 (ref null $#Top)) (result (ref null $#Top))
|
||||
global.get $"C390 \"FooConstBase(\""
|
||||
global.get $"C392 \"FooConstBase(\""
|
||||
local.get $var1
|
||||
global.get $"C8 \")\""
|
||||
call $JSStringImpl._interpolate3
|
||||
|
||||
@@ -18,25 +18,25 @@
|
||||
(func $JSStringImpl._interpolate3 (import "module0" "func10") (param (ref null $#Top) (ref null $#Top) (ref null $#Top)) (result (ref $JSStringImpl)))
|
||||
(func $print (import "module0" "func9") (param (ref null $#Top)) (result (ref null $#Top)))
|
||||
(global $".FooConst5(" (import "" "FooConst5(") (ref extern))
|
||||
(global $"C313 \"[]\"" (import "module0" "global6") (ref $JSStringImpl))
|
||||
(global $"C389 5" (import "module0" "global5") (ref $BoxedInt))
|
||||
(global $"C391 FooConst0" (import "module0" "global7") (ref $Object))
|
||||
(global $"C315 \"[]\"" (import "module0" "global6") (ref $JSStringImpl))
|
||||
(global $"C391 5" (import "module0" "global5") (ref $BoxedInt))
|
||||
(global $"C393 FooConst0" (import "module0" "global7") (ref $Object))
|
||||
(global $"C8 \")\"" (import "module0" "global4") (ref $JSStringImpl))
|
||||
(table $module0.dispatch0 (import "module0" "dispatch0") 778 funcref)
|
||||
(table $module0.dispatch0 (import "module0" "dispatch0") 789 funcref)
|
||||
(table $module0.static1-0 (import "module0" "static1-0") 4 (ref null $type2))
|
||||
(table $module0.static2-0 (import "module0" "static2-0") 4 (ref null $type0))
|
||||
(table $module0.static3-0 (import "module0" "static3-0") 4 (ref null $type4))
|
||||
(global $"C510 FooConst5" (ref $Object)
|
||||
(i32.const 123)
|
||||
(global $"C512 FooConst5" (ref $Object)
|
||||
(i32.const 125)
|
||||
(i32.const 0)
|
||||
(struct.new $Object))
|
||||
(global $"C511 \"foo5Code(\"" (ref $JSStringImpl) <...>)
|
||||
(global $"C512 \"FooConst5(\"" (ref $JSStringImpl)
|
||||
(global $"C513 \"foo5Code(\"" (ref $JSStringImpl) <...>)
|
||||
(global $"C514 \"FooConst5(\"" (ref $JSStringImpl)
|
||||
(i32.const 4)
|
||||
(i32.const 0)
|
||||
(global.get $".FooConst5(")
|
||||
(struct.new $JSStringImpl))
|
||||
(global $"C513 _InterfaceType" (ref $_InterfaceType) <...>)
|
||||
(global $"C515 _InterfaceType" (ref $_InterfaceType) <...>)
|
||||
(global $allFooConstants (mut (ref null $WasmListBase))
|
||||
(ref.null none))
|
||||
(global $fooGlobal5 (mut (ref null $#Top))
|
||||
@@ -46,16 +46,16 @@
|
||||
(local $var1 (ref $WasmListBase))
|
||||
(local $var2 (ref $Object))
|
||||
(local $var3 i64)
|
||||
global.get $"C510 FooConst5"
|
||||
global.get $"C512 FooConst5"
|
||||
call $print
|
||||
drop
|
||||
global.get $"C511 \"foo5Code(\""
|
||||
global.get $"C513 \"foo5Code(\""
|
||||
local.get $var0
|
||||
global.get $"C8 \")\""
|
||||
call $JSStringImpl._interpolate3
|
||||
call $print
|
||||
drop
|
||||
global.get $"C389 5"
|
||||
global.get $"C391 5"
|
||||
global.set $fooGlobal5
|
||||
call $"fooGlobal0 implicit getter"
|
||||
call $"foo0Code <noInline>"
|
||||
@@ -83,8 +83,8 @@
|
||||
block $label0 (result (ref $WasmListBase))
|
||||
global.get $allFooConstants
|
||||
br_on_non_null $label0
|
||||
global.get $"C513 _InterfaceType"
|
||||
global.get $"C391 FooConst0"
|
||||
global.get $"C515 _InterfaceType"
|
||||
global.get $"C393 FooConst0"
|
||||
i32.const 0
|
||||
call_indirect $module0.static3-0 (result (ref $Object))
|
||||
i32.const 1
|
||||
@@ -93,7 +93,7 @@
|
||||
call_indirect $module0.static3-0 (result (ref $Object))
|
||||
i32.const 3
|
||||
call_indirect $module0.static3-0 (result (ref $Object))
|
||||
global.get $"C510 FooConst5"
|
||||
global.get $"C512 FooConst5"
|
||||
array.new_fixed $Array<Object?> 6
|
||||
call $GrowableList._withData
|
||||
global.set $allFooConstants
|
||||
@@ -107,7 +107,7 @@
|
||||
if
|
||||
i64.const 0
|
||||
local.get $var3
|
||||
global.get $"C313 \"[]\""
|
||||
global.get $"C315 \"[]\""
|
||||
call $"_throwIndexError <noInline>"
|
||||
unreachable
|
||||
end
|
||||
@@ -120,14 +120,14 @@
|
||||
call $"fooGlobal5 implicit getter"
|
||||
local.get $var2
|
||||
struct.get $Object $field0
|
||||
i32.const 400
|
||||
i32.const 406
|
||||
i32.add
|
||||
call_indirect $module0.dispatch0 (param (ref $Object) (ref null $#Top)) (result (ref null $#Top))
|
||||
drop
|
||||
)
|
||||
(func $fooGlobal5 implicit getter (result (ref $#Top)) <...>)
|
||||
(func $FooConst5.doit (param $var0 (ref $Object)) (param $var1 (ref null $#Top)) (result (ref null $#Top))
|
||||
global.get $"C512 \"FooConst5(\""
|
||||
global.get $"C514 \"FooConst5(\""
|
||||
local.get $var1
|
||||
global.get $"C8 \")\""
|
||||
call $JSStringImpl._interpolate3
|
||||
|
||||
@@ -7,36 +7,36 @@
|
||||
(func $JSStringImpl._interpolate3 (import "module0" "func10") (param (ref null $#Top) (ref null $#Top) (ref null $#Top)) (result (ref $JSStringImpl)))
|
||||
(func $print (import "module0" "func9") (param (ref null $#Top)) (result (ref null $#Top)))
|
||||
(global $".FooConst1(" (import "" "FooConst1(") (ref extern))
|
||||
(global $"C319 1" (import "module0" "global8") (ref $BoxedInt))
|
||||
(global $"C321 1" (import "module0" "global8") (ref $BoxedInt))
|
||||
(global $"C8 \")\"" (import "module0" "global4") (ref $JSStringImpl))
|
||||
(global $"C514 FooConst1" (ref $Object)
|
||||
(i32.const 119)
|
||||
(global $"C516 FooConst1" (ref $Object)
|
||||
(i32.const 121)
|
||||
(i32.const 0)
|
||||
(struct.new $Object))
|
||||
(global $"C521 \"FooConst1(\"" (ref $JSStringImpl)
|
||||
(global $"C523 \"FooConst1(\"" (ref $JSStringImpl)
|
||||
(i32.const 4)
|
||||
(i32.const 0)
|
||||
(global.get $".FooConst1(")
|
||||
(struct.new $JSStringImpl))
|
||||
(global $"C529 \"foo1Code(\"" (ref $JSStringImpl) <...>)
|
||||
(global $"C531 \"foo1Code(\"" (ref $JSStringImpl) <...>)
|
||||
(global $fooGlobal1 (mut (ref null $#Top))
|
||||
(ref.null none))
|
||||
(func $"foo1Code <noInline>" (param $var0 (ref null $#Top)) (result (ref null $#Top))
|
||||
global.get $"C514 FooConst1"
|
||||
global.get $"C516 FooConst1"
|
||||
call $print
|
||||
drop
|
||||
global.get $"C529 \"foo1Code(\""
|
||||
global.get $"C531 \"foo1Code(\""
|
||||
local.get $var0
|
||||
global.get $"C8 \")\""
|
||||
call $JSStringImpl._interpolate3
|
||||
call $print
|
||||
drop
|
||||
global.get $"C319 1"
|
||||
global.get $"C321 1"
|
||||
global.set $fooGlobal1
|
||||
ref.null none
|
||||
)
|
||||
(func $FooConst1.doit (param $var0 (ref $Object)) (param $var1 (ref null $#Top)) (result (ref null $#Top))
|
||||
global.get $"C521 \"FooConst1(\""
|
||||
global.get $"C523 \"FooConst1(\""
|
||||
local.get $var1
|
||||
global.get $"C8 \")\""
|
||||
call $JSStringImpl._interpolate3
|
||||
|
||||
@@ -7,36 +7,36 @@
|
||||
(func $JSStringImpl._interpolate3 (import "module0" "func10") (param (ref null $#Top) (ref null $#Top) (ref null $#Top)) (result (ref $JSStringImpl)))
|
||||
(func $print (import "module0" "func9") (param (ref null $#Top)) (result (ref null $#Top)))
|
||||
(global $".FooConst2(" (import "" "FooConst2(") (ref extern))
|
||||
(global $"C348 2" (import "module0" "global12") (ref $BoxedInt))
|
||||
(global $"C350 2" (import "module0" "global12") (ref $BoxedInt))
|
||||
(global $"C8 \")\"" (import "module0" "global4") (ref $JSStringImpl))
|
||||
(global $"C515 FooConst2" (ref $Object)
|
||||
(i32.const 120)
|
||||
(global $"C517 FooConst2" (ref $Object)
|
||||
(i32.const 122)
|
||||
(i32.const 0)
|
||||
(struct.new $Object))
|
||||
(global $"C520 \"FooConst2(\"" (ref $JSStringImpl)
|
||||
(global $"C522 \"FooConst2(\"" (ref $JSStringImpl)
|
||||
(i32.const 4)
|
||||
(i32.const 0)
|
||||
(global.get $".FooConst2(")
|
||||
(struct.new $JSStringImpl))
|
||||
(global $"C528 \"foo2Code(\"" (ref $JSStringImpl) <...>)
|
||||
(global $"C530 \"foo2Code(\"" (ref $JSStringImpl) <...>)
|
||||
(global $fooGlobal2 (mut (ref null $#Top))
|
||||
(ref.null none))
|
||||
(func $"foo2Code <noInline>" (param $var0 (ref null $#Top)) (result (ref null $#Top))
|
||||
global.get $"C515 FooConst2"
|
||||
global.get $"C517 FooConst2"
|
||||
call $print
|
||||
drop
|
||||
global.get $"C528 \"foo2Code(\""
|
||||
global.get $"C530 \"foo2Code(\""
|
||||
local.get $var0
|
||||
global.get $"C8 \")\""
|
||||
call $JSStringImpl._interpolate3
|
||||
call $print
|
||||
drop
|
||||
global.get $"C348 2"
|
||||
global.get $"C350 2"
|
||||
global.set $fooGlobal2
|
||||
ref.null none
|
||||
)
|
||||
(func $FooConst2.doit (param $var0 (ref $Object)) (param $var1 (ref null $#Top)) (result (ref null $#Top))
|
||||
global.get $"C520 \"FooConst2(\""
|
||||
global.get $"C522 \"FooConst2(\""
|
||||
local.get $var1
|
||||
global.get $"C8 \")\""
|
||||
call $JSStringImpl._interpolate3
|
||||
|
||||
@@ -7,36 +7,36 @@
|
||||
(func $JSStringImpl._interpolate3 (import "module0" "func10") (param (ref null $#Top) (ref null $#Top) (ref null $#Top)) (result (ref $JSStringImpl)))
|
||||
(func $print (import "module0" "func9") (param (ref null $#Top)) (result (ref null $#Top)))
|
||||
(global $".FooConst3(" (import "" "FooConst3(") (ref extern))
|
||||
(global $"C427 3" (import "module0" "global11") (ref $BoxedInt))
|
||||
(global $"C429 3" (import "module0" "global11") (ref $BoxedInt))
|
||||
(global $"C8 \")\"" (import "module0" "global4") (ref $JSStringImpl))
|
||||
(global $"C516 FooConst3" (ref $Object)
|
||||
(i32.const 121)
|
||||
(global $"C518 FooConst3" (ref $Object)
|
||||
(i32.const 123)
|
||||
(i32.const 0)
|
||||
(struct.new $Object))
|
||||
(global $"C519 \"FooConst3(\"" (ref $JSStringImpl)
|
||||
(global $"C521 \"FooConst3(\"" (ref $JSStringImpl)
|
||||
(i32.const 4)
|
||||
(i32.const 0)
|
||||
(global.get $".FooConst3(")
|
||||
(struct.new $JSStringImpl))
|
||||
(global $"C527 \"foo3Code(\"" (ref $JSStringImpl) <...>)
|
||||
(global $"C529 \"foo3Code(\"" (ref $JSStringImpl) <...>)
|
||||
(global $fooGlobal3 (mut (ref null $#Top))
|
||||
(ref.null none))
|
||||
(func $"foo3Code <noInline>" (param $var0 (ref null $#Top)) (result (ref null $#Top))
|
||||
global.get $"C516 FooConst3"
|
||||
global.get $"C518 FooConst3"
|
||||
call $print
|
||||
drop
|
||||
global.get $"C527 \"foo3Code(\""
|
||||
global.get $"C529 \"foo3Code(\""
|
||||
local.get $var0
|
||||
global.get $"C8 \")\""
|
||||
call $JSStringImpl._interpolate3
|
||||
call $print
|
||||
drop
|
||||
global.get $"C427 3"
|
||||
global.get $"C429 3"
|
||||
global.set $fooGlobal3
|
||||
ref.null none
|
||||
)
|
||||
(func $FooConst3.doit (param $var0 (ref $Object)) (param $var1 (ref null $#Top)) (result (ref null $#Top))
|
||||
global.get $"C519 \"FooConst3(\""
|
||||
global.get $"C521 \"FooConst3(\""
|
||||
local.get $var1
|
||||
global.get $"C8 \")\""
|
||||
call $JSStringImpl._interpolate3
|
||||
|
||||
@@ -7,36 +7,36 @@
|
||||
(func $JSStringImpl._interpolate3 (import "module0" "func10") (param (ref null $#Top) (ref null $#Top) (ref null $#Top)) (result (ref $JSStringImpl)))
|
||||
(func $print (import "module0" "func9") (param (ref null $#Top)) (result (ref null $#Top)))
|
||||
(global $".FooConst4(" (import "" "FooConst4(") (ref extern))
|
||||
(global $"C367 4" (import "module0" "global10") (ref $BoxedInt))
|
||||
(global $"C369 4" (import "module0" "global10") (ref $BoxedInt))
|
||||
(global $"C8 \")\"" (import "module0" "global4") (ref $JSStringImpl))
|
||||
(global $"C517 FooConst4" (ref $Object)
|
||||
(i32.const 122)
|
||||
(global $"C519 FooConst4" (ref $Object)
|
||||
(i32.const 124)
|
||||
(i32.const 0)
|
||||
(struct.new $Object))
|
||||
(global $"C518 \"FooConst4(\"" (ref $JSStringImpl)
|
||||
(global $"C520 \"FooConst4(\"" (ref $JSStringImpl)
|
||||
(i32.const 4)
|
||||
(i32.const 0)
|
||||
(global.get $".FooConst4(")
|
||||
(struct.new $JSStringImpl))
|
||||
(global $"C526 \"foo4Code(\"" (ref $JSStringImpl) <...>)
|
||||
(global $"C528 \"foo4Code(\"" (ref $JSStringImpl) <...>)
|
||||
(global $fooGlobal4 (mut (ref null $#Top))
|
||||
(ref.null none))
|
||||
(func $"foo4Code <noInline>" (param $var0 (ref null $#Top)) (result (ref null $#Top))
|
||||
global.get $"C517 FooConst4"
|
||||
global.get $"C519 FooConst4"
|
||||
call $print
|
||||
drop
|
||||
global.get $"C526 \"foo4Code(\""
|
||||
global.get $"C528 \"foo4Code(\""
|
||||
local.get $var0
|
||||
global.get $"C8 \")\""
|
||||
call $JSStringImpl._interpolate3
|
||||
call $print
|
||||
drop
|
||||
global.get $"C367 4"
|
||||
global.get $"C369 4"
|
||||
global.set $fooGlobal4
|
||||
ref.null none
|
||||
)
|
||||
(func $FooConst4.doit (param $var0 (ref $Object)) (param $var1 (ref null $#Top)) (result (ref null $#Top))
|
||||
global.get $"C518 \"FooConst4(\""
|
||||
global.get $"C520 \"FooConst4(\""
|
||||
local.get $var1
|
||||
global.get $"C8 \")\""
|
||||
call $JSStringImpl._interpolate3
|
||||
|
||||
@@ -20,10 +20,10 @@
|
||||
(type $type2 <...>)
|
||||
(global $"C1 WasmArray<_Type>[0]" (ref $Array<_Type>) <...>)
|
||||
(global $"C28 _InterfaceType" (ref $_InterfaceType) <...>)
|
||||
(global $"C314 WasmArray<_NamedParameter>[0]" (ref $Array<_NamedParameter>) <...>)
|
||||
(global $"C340 _TopType" (ref $_TopType) <...>)
|
||||
(global $"C344 foo tear-off" (ref $#Closure-0-2)
|
||||
(i32.const 32)
|
||||
(global $"C316 WasmArray<_NamedParameter>[0]" (ref $Array<_NamedParameter>) <...>)
|
||||
(global $"C342 _TopType" (ref $_TopType) <...>)
|
||||
(global $"C346 foo tear-off" (ref $#Closure-0-2)
|
||||
(i32.const 56)
|
||||
(i32.const 0)
|
||||
(global.get $global0)
|
||||
(ref.func $"foo tear-off trampoline")
|
||||
@@ -34,21 +34,21 @@
|
||||
(i32.const 0)
|
||||
(global.get $"C1 WasmArray<_Type>[0]")
|
||||
(global.get $"C1 WasmArray<_Type>[0]")
|
||||
(global.get $"C340 _TopType")
|
||||
(global.get $"C342 _TopType")
|
||||
(global.get $"C28 _InterfaceType")
|
||||
(i32.const 10)
|
||||
(i32.const 0)
|
||||
(i32.const 1)
|
||||
(i32.const 152)
|
||||
(i32.const 154)
|
||||
(global.get $"C1 WasmArray<_Type>[0]")
|
||||
(struct.new $_InterfaceType)
|
||||
(array.new_fixed $Array<_Type> 2)
|
||||
(i64.const 1)
|
||||
(global.get $"C314 WasmArray<_NamedParameter>[0]")
|
||||
(global.get $"C316 WasmArray<_NamedParameter>[0]")
|
||||
(struct.new $_FunctionType)
|
||||
(struct.new $#Closure-0-2))
|
||||
(global $"C348 bar tear-off" (ref $#Closure-0-2)
|
||||
(i32.const 32)
|
||||
(global $"C350 bar tear-off" (ref $#Closure-0-2)
|
||||
(i32.const 56)
|
||||
(i32.const 0)
|
||||
(global.get $global0)
|
||||
(ref.func $"bar tear-off trampoline")
|
||||
@@ -59,17 +59,17 @@
|
||||
(i32.const 0)
|
||||
(global.get $"C1 WasmArray<_Type>[0]")
|
||||
(global.get $"C1 WasmArray<_Type>[0]")
|
||||
(global.get $"C340 _TopType")
|
||||
(global.get $"C342 _TopType")
|
||||
(global.get $"C28 _InterfaceType")
|
||||
(i32.const 10)
|
||||
(i32.const 0)
|
||||
(i32.const 1)
|
||||
(i32.const 135)
|
||||
(i32.const 137)
|
||||
(global.get $"C1 WasmArray<_Type>[0]")
|
||||
(struct.new $_InterfaceType)
|
||||
(array.new_fixed $Array<_Type> 2)
|
||||
(i64.const 1)
|
||||
(global.get $"C314 WasmArray<_NamedParameter>[0]")
|
||||
(global.get $"C316 WasmArray<_NamedParameter>[0]")
|
||||
(struct.new $_FunctionType)
|
||||
(struct.new $#Closure-0-2))
|
||||
(global $global0 (ref $#DummyStruct) <...>)
|
||||
|
||||
@@ -20,10 +20,10 @@
|
||||
(type $type2 <...>)
|
||||
(global $"C1 WasmArray<_Type>[0]" (ref $Array<_Type>) <...>)
|
||||
(global $"C28 _InterfaceType" (ref $_InterfaceType) <...>)
|
||||
(global $"C314 WasmArray<_NamedParameter>[0]" (ref $Array<_NamedParameter>) <...>)
|
||||
(global $"C340 _TopType" (ref $_TopType) <...>)
|
||||
(global $"C344 foo tear-off" (ref $#Closure-0-2)
|
||||
(i32.const 32)
|
||||
(global $"C316 WasmArray<_NamedParameter>[0]" (ref $Array<_NamedParameter>) <...>)
|
||||
(global $"C342 _TopType" (ref $_TopType) <...>)
|
||||
(global $"C346 foo tear-off" (ref $#Closure-0-2)
|
||||
(i32.const 56)
|
||||
(i32.const 0)
|
||||
(global.get $global0)
|
||||
(ref.func $"foo tear-off trampoline")
|
||||
@@ -34,21 +34,21 @@
|
||||
(i32.const 0)
|
||||
(global.get $"C1 WasmArray<_Type>[0]")
|
||||
(global.get $"C1 WasmArray<_Type>[0]")
|
||||
(global.get $"C340 _TopType")
|
||||
(global.get $"C342 _TopType")
|
||||
(global.get $"C28 _InterfaceType")
|
||||
(i32.const 10)
|
||||
(i32.const 0)
|
||||
(i32.const 1)
|
||||
(i32.const 152)
|
||||
(i32.const 154)
|
||||
(global.get $"C1 WasmArray<_Type>[0]")
|
||||
(struct.new $_InterfaceType)
|
||||
(array.new_fixed $Array<_Type> 2)
|
||||
(i64.const 1)
|
||||
(global.get $"C314 WasmArray<_NamedParameter>[0]")
|
||||
(global.get $"C316 WasmArray<_NamedParameter>[0]")
|
||||
(struct.new $_FunctionType)
|
||||
(struct.new $#Closure-0-2))
|
||||
(global $"C349 bar tear-off" (ref $#Closure-0-2)
|
||||
(i32.const 32)
|
||||
(global $"C351 bar tear-off" (ref $#Closure-0-2)
|
||||
(i32.const 56)
|
||||
(i32.const 0)
|
||||
(global.get $global0)
|
||||
(ref.func $"bar tear-off trampoline")
|
||||
@@ -59,17 +59,17 @@
|
||||
(i32.const 0)
|
||||
(global.get $"C1 WasmArray<_Type>[0]")
|
||||
(global.get $"C1 WasmArray<_Type>[0]")
|
||||
(global.get $"C340 _TopType")
|
||||
(global.get $"C342 _TopType")
|
||||
(global.get $"C28 _InterfaceType")
|
||||
(i32.const 10)
|
||||
(i32.const 0)
|
||||
(i32.const 1)
|
||||
(i32.const 135)
|
||||
(i32.const 137)
|
||||
(global.get $"C1 WasmArray<_Type>[0]")
|
||||
(struct.new $_InterfaceType)
|
||||
(array.new_fixed $Array<_Type> 2)
|
||||
(i64.const 1)
|
||||
(global.get $"C314 WasmArray<_NamedParameter>[0]")
|
||||
(global.get $"C316 WasmArray<_NamedParameter>[0]")
|
||||
(struct.new $_FunctionType)
|
||||
(struct.new $#Closure-0-2))
|
||||
(global $global0 (ref $#DummyStruct) <...>)
|
||||
|
||||
@@ -24,10 +24,10 @@
|
||||
(type $type2 <...>)
|
||||
(global $"C1 WasmArray<_Type>[0]" (ref $Array<_Type>) <...>)
|
||||
(global $"C28 _InterfaceType" (ref $_InterfaceType) <...>)
|
||||
(global $"C314 WasmArray<_NamedParameter>[0]" (ref $Array<_NamedParameter>) <...>)
|
||||
(global $"C340 _TopType" (ref $_TopType) <...>)
|
||||
(global $"C344 foo tear-off" (ref $#Closure-0-2)
|
||||
(i32.const 32)
|
||||
(global $"C316 WasmArray<_NamedParameter>[0]" (ref $Array<_NamedParameter>) <...>)
|
||||
(global $"C342 _TopType" (ref $_TopType) <...>)
|
||||
(global $"C346 foo tear-off" (ref $#Closure-0-2)
|
||||
(i32.const 56)
|
||||
(i32.const 0)
|
||||
(global.get $global0)
|
||||
(ref.func $"foo tear-off dynamic call entry")
|
||||
@@ -38,22 +38,22 @@
|
||||
(i32.const 0)
|
||||
(global.get $"C1 WasmArray<_Type>[0]")
|
||||
(global.get $"C1 WasmArray<_Type>[0]")
|
||||
(global.get $"C340 _TopType")
|
||||
(global.get $"C342 _TopType")
|
||||
(global.get $"C28 _InterfaceType")
|
||||
(i32.const 10)
|
||||
(i32.const 0)
|
||||
(i32.const 1)
|
||||
(i32.const 152)
|
||||
(i32.const 154)
|
||||
(global.get $"C1 WasmArray<_Type>[0]")
|
||||
(struct.new $_InterfaceType)
|
||||
(array.new_fixed $Array<_Type> 2)
|
||||
(i64.const 1)
|
||||
(global.get $"C314 WasmArray<_NamedParameter>[0]")
|
||||
(global.get $"C316 WasmArray<_NamedParameter>[0]")
|
||||
(struct.new $_FunctionType)
|
||||
(struct.new $#Closure-0-2))
|
||||
(global $"C346 _InterfaceType" (ref $_InterfaceType) <...>)
|
||||
(global $"C349 bar tear-off" (ref $#Closure-0-2)
|
||||
(i32.const 32)
|
||||
(global $"C348 _InterfaceType" (ref $_InterfaceType) <...>)
|
||||
(global $"C351 bar tear-off" (ref $#Closure-0-2)
|
||||
(i32.const 56)
|
||||
(i32.const 0)
|
||||
(global.get $global0)
|
||||
(ref.func $"bar tear-off dynamic call entry")
|
||||
@@ -64,12 +64,12 @@
|
||||
(i32.const 0)
|
||||
(global.get $"C1 WasmArray<_Type>[0]")
|
||||
(global.get $"C1 WasmArray<_Type>[0]")
|
||||
(global.get $"C340 _TopType")
|
||||
(global.get $"C342 _TopType")
|
||||
(global.get $"C28 _InterfaceType")
|
||||
(global.get $"C346 _InterfaceType")
|
||||
(global.get $"C348 _InterfaceType")
|
||||
(array.new_fixed $Array<_Type> 2)
|
||||
(i64.const 1)
|
||||
(global.get $"C314 WasmArray<_NamedParameter>[0]")
|
||||
(global.get $"C316 WasmArray<_NamedParameter>[0]")
|
||||
(struct.new $_FunctionType)
|
||||
(struct.new $#Closure-0-2))
|
||||
(global $global0 (ref $#DummyStruct) <...>)
|
||||
|
||||
@@ -5,12 +5,12 @@
|
||||
(field $field0 i32)
|
||||
(field $_ref externref))))
|
||||
(global $".hello world" (import "" "hello world") (ref extern))
|
||||
(global $"C338 \"hello world\"" (ref $JSStringImpl)
|
||||
(global $"C340 \"hello world\"" (ref $JSStringImpl)
|
||||
(i32.const 4)
|
||||
(global.get $".hello world")
|
||||
(struct.new $JSStringImpl))
|
||||
(func $"main <noInline>"
|
||||
global.get $"C338 \"hello world\""
|
||||
global.get $"C340 \"hello world\""
|
||||
call $print
|
||||
)
|
||||
(func $print (param $var0 (ref $#Top)) <...>)
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
(field $field1 (mut i32)))))
|
||||
(func $print (import "module0" "func0") (param (ref null $#Top)) (result (ref null $#Top)))
|
||||
(global $".hello world" (import "" "hello world") (ref extern))
|
||||
(global $"C463 \"hello world\"" (ref $JSStringImpl)
|
||||
(global $"C465 \"hello world\"" (ref $JSStringImpl)
|
||||
(i32.const 4)
|
||||
(i32.const 0)
|
||||
(global.get $".hello world")
|
||||
@@ -20,7 +20,7 @@
|
||||
ref.null none
|
||||
)
|
||||
(func $"mainFoo <noInline>"
|
||||
global.get $"C463 \"hello world\""
|
||||
global.get $"C465 \"hello world\""
|
||||
call $print
|
||||
drop
|
||||
)
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
if (result (ref null $BoxedInt))
|
||||
ref.null none
|
||||
else
|
||||
i32.const 68
|
||||
i32.const 69
|
||||
local.get $var0
|
||||
call $dartifyInt
|
||||
struct.new $BoxedInt
|
||||
@@ -69,7 +69,7 @@
|
||||
if
|
||||
call $"ktrue implicit getter"
|
||||
if (result (ref null $BoxedInt))
|
||||
i32.const 68
|
||||
i32.const 69
|
||||
call $"intValue implicit getter"
|
||||
struct.new $BoxedInt
|
||||
else
|
||||
@@ -94,7 +94,7 @@
|
||||
if (result (ref null $BoxedInt))
|
||||
ref.null none
|
||||
else
|
||||
i32.const 68
|
||||
i32.const 69
|
||||
local.get $var1
|
||||
call $dartifyInt
|
||||
struct.new $BoxedInt
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
// Copyright (c) 2026, 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.
|
||||
|
||||
// functionFilter=main
|
||||
// compilerOption=--no-minify
|
||||
// compilerOption=--enable-experimental-wasm-interop
|
||||
|
||||
import 'dart:_wasm';
|
||||
|
||||
@pragma('wasm:import', 'foo.mem')
|
||||
@pragma('wasm:memory-type', MemoryType(limits: Limits(1)))
|
||||
external Memory get memory;
|
||||
|
||||
@pragma('wasm:never-inline')
|
||||
void main() {
|
||||
memory.size;
|
||||
memory.grow(1);
|
||||
print(memory.loadFloat32(0, align: 2).toDouble());
|
||||
print(memory.loadFloat32(0, align: 2).toDouble());
|
||||
print(memory.loadFloat64(0, align: 3).toDouble());
|
||||
|
||||
print(memory.loadFloat32(0, offset: 1, align: 2).toDouble());
|
||||
print(memory
|
||||
.loadFloat32(
|
||||
0,
|
||||
align: 2,
|
||||
offset: 1,
|
||||
)
|
||||
.toDouble());
|
||||
|
||||
memory.storeInt32(memory.size, WasmI32.fromInt(32), offset: 10);
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
(module $module0
|
||||
(type $#Top (struct
|
||||
(field $field0 i32)))
|
||||
(type $BoxedDouble (sub final $#Top (struct
|
||||
(field $field0 i32)
|
||||
(field $value f64))))
|
||||
(memory $foo.mem (import "foo" "mem") 1)
|
||||
(func $"main <noInline>"
|
||||
memory.size $foo.mem
|
||||
drop
|
||||
i32.const 1
|
||||
memory.grow $foo.mem
|
||||
drop
|
||||
i32.const 88
|
||||
i32.const 0
|
||||
f32.load align=4
|
||||
f64.promote_f32
|
||||
struct.new $BoxedDouble
|
||||
call $print
|
||||
i32.const 88
|
||||
i32.const 0
|
||||
f32.load align=4
|
||||
f64.promote_f32
|
||||
struct.new $BoxedDouble
|
||||
call $print
|
||||
i32.const 88
|
||||
i32.const 0
|
||||
f64.load align=8
|
||||
struct.new $BoxedDouble
|
||||
call $print
|
||||
i32.const 88
|
||||
i32.const 1
|
||||
f32.load align=4
|
||||
f64.promote_f32
|
||||
struct.new $BoxedDouble
|
||||
call $print
|
||||
i32.const 88
|
||||
i32.const 1
|
||||
f32.load align=4
|
||||
f64.promote_f32
|
||||
struct.new $BoxedDouble
|
||||
call $print
|
||||
memory.size $foo.mem
|
||||
i32.const 32
|
||||
i32.store offset=10
|
||||
)
|
||||
(func $print (param $var0 (ref $#Top)) <...>)
|
||||
)
|
||||
@@ -16298,6 +16298,29 @@ const MessageCode codeVoidExpression = const MessageCode(
|
||||
problemMessage: """This expression has type 'void' and can't be used.""",
|
||||
);
|
||||
|
||||
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
|
||||
const MessageCode codeWasmExternInvalidLoad = const MessageCode(
|
||||
"WasmExternInvalidLoad",
|
||||
problemMessage:
|
||||
"""WebAssembly elements may only be referenced to directly call a method on them.""",
|
||||
);
|
||||
|
||||
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
|
||||
const MessageCode codeWasmExternInvalidTarget = const MessageCode(
|
||||
"WasmExternInvalidTarget",
|
||||
problemMessage:
|
||||
"""The receiver of this call must be a top-level variable describing the WebAssembly element.""",
|
||||
);
|
||||
|
||||
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
|
||||
const MessageCode codeWasmExternMemoryMissingAnnotation = const MessageCode(
|
||||
"WasmExternMemoryMissingAnnotation",
|
||||
problemMessage:
|
||||
"""This external getter returns a memory instance, but no annotation describing it was found""",
|
||||
correctionMessage:
|
||||
"""Try adding a `@MemoryType()` or `@Import.memory()` annotation.""",
|
||||
);
|
||||
|
||||
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
|
||||
const MessageCode codeWasmImportOrExportInUserCode = const MessageCode(
|
||||
"WasmImportOrExportInUserCode",
|
||||
@@ -16305,6 +16328,12 @@ const MessageCode codeWasmImportOrExportInUserCode = const MessageCode(
|
||||
"""Pragmas `wasm:import` and `wasm:export` are for internal use only and cannot be used by user code.""",
|
||||
);
|
||||
|
||||
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
|
||||
const MessageCode codeWasmIntrinsicTearOff = const MessageCode(
|
||||
"WasmIntrinsicTearOff",
|
||||
problemMessage: """This intrinsic extension member may not be torn off.""",
|
||||
);
|
||||
|
||||
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
|
||||
const MessageCode codeWeakReferenceMismatchReturnAndArgumentTypes =
|
||||
const MessageCode(
|
||||
|
||||
@@ -162,6 +162,10 @@ front_end/UnsoundSwitchStatementError/example: missingExample # Used in throw in
|
||||
front_end/Unspecified/example: missingExample # This should generally not be used.
|
||||
front_end/UnterminatedToken/example: missingExample # This is a fall-back message that shouldn't happen.
|
||||
front_end/WasmImportOrExportInUserCode/example: missingExample # only issued by wasm build
|
||||
front_end/WasmExternInvalidLoad/example: missingExample # only issued by wasm build
|
||||
front_end/WasmExternInvalidTarget/example: missingExample # only issued by wasm build
|
||||
front_end/WasmIntrinsicTearOff/example: missingExample # only issued by wasm build
|
||||
front_end/WasmExternMemoryMissingAnnotation/example: missingExample # only issued by wasm build
|
||||
front_end/WebLiteralCannotBeRepresentedExactly/example: missingExample # only issued on web build
|
||||
|
||||
# Can we do better?
|
||||
|
||||
@@ -7182,6 +7182,23 @@ wasmImportOrExportInUserCode:
|
||||
parameters: none
|
||||
problemMessage: "Pragmas `wasm:import` and `wasm:export` are for internal use only and cannot be used by user code."
|
||||
|
||||
wasmExternMemoryMissingAnnotation:
|
||||
parameters: none
|
||||
problemMessage: "This external getter returns a memory instance, but no annotation describing it was found"
|
||||
correctionMessage: "Try adding a `@MemoryType()` or `@Import.memory()` annotation."
|
||||
|
||||
wasmExternInvalidLoad:
|
||||
parameters: none
|
||||
problemMessage: "WebAssembly elements may only be referenced to directly call a method on them."
|
||||
|
||||
wasmExternInvalidTarget:
|
||||
parameters: none
|
||||
problemMessage: "The receiver of this call must be a top-level variable describing the WebAssembly element."
|
||||
|
||||
wasmIntrinsicTearOff:
|
||||
parameters: none
|
||||
problemMessage: This intrinsic extension member may not be torn off.
|
||||
|
||||
weakReferenceNotStatic:
|
||||
parameters: none
|
||||
problemMessage: "Weak reference pragma can be used on a static method only."
|
||||
|
||||
@@ -73,6 +73,7 @@ guarded
|
||||
guides
|
||||
h
|
||||
https
|
||||
import.memory
|
||||
int32x
|
||||
interact
|
||||
interop
|
||||
@@ -89,6 +90,7 @@ list.filled
|
||||
loadlibrary
|
||||
macro
|
||||
member(s)
|
||||
memorytype
|
||||
migrate
|
||||
mocking
|
||||
modifier
|
||||
@@ -158,6 +160,7 @@ unavailable
|
||||
unsound
|
||||
unsupportederror
|
||||
v
|
||||
webassembly
|
||||
wasm:export
|
||||
wasm:import
|
||||
x
|
||||
|
||||
@@ -1159,6 +1159,14 @@ class InstructionsBuilder with Builder<ir.Instructions> {
|
||||
_add(ir.MemoryGrow(memory));
|
||||
}
|
||||
|
||||
/// Emit a `memory.fill` instruction.
|
||||
void memory_fill(ir.Memory memory) {
|
||||
assert(_verifyTypes(
|
||||
const [ir.NumType.i32, ir.NumType.i32, ir.NumType.i32], const []));
|
||||
assert(memory.enclosingModule == module);
|
||||
_add(ir.MemoryFill(memory));
|
||||
}
|
||||
|
||||
// Reference instructions
|
||||
|
||||
/// Emit a `ref.null` instruction.
|
||||
|
||||
@@ -552,6 +552,8 @@ abstract class Instruction implements Serializable {
|
||||
return I64TruncSatF64S.deserialize(d);
|
||||
case 0x07:
|
||||
return I64TruncSatF64U.deserialize(d);
|
||||
case 0x0B:
|
||||
return MemoryFill.deserialize(d, memories);
|
||||
case 0x10:
|
||||
return TableSize.deserialize(d, tables);
|
||||
case 0x11:
|
||||
@@ -2004,6 +2006,7 @@ class MemorySize extends Instruction {
|
||||
@override
|
||||
void printTo(IrPrinter p) {
|
||||
p.write(name);
|
||||
p.write(' ');
|
||||
p.writeMemoryReference(memory);
|
||||
}
|
||||
}
|
||||
@@ -2026,6 +2029,33 @@ class MemoryGrow extends Instruction {
|
||||
@override
|
||||
String get name => 'memory.grow';
|
||||
|
||||
@override
|
||||
void printTo(IrPrinter p) {
|
||||
p.write(name);
|
||||
p.write(' ');
|
||||
p.writeMemoryReference(memory);
|
||||
}
|
||||
}
|
||||
|
||||
class MemoryFill extends Instruction {
|
||||
final Memory memory;
|
||||
|
||||
MemoryFill(this.memory);
|
||||
|
||||
static MemoryFill deserialize(Deserializer d, Memories memories) {
|
||||
return MemoryFill(memories[d.readUnsigned()]);
|
||||
}
|
||||
|
||||
@override
|
||||
void serialize(Serializer s) {
|
||||
s.writeByte(0xFC);
|
||||
s.writeUnsigned(0x0B);
|
||||
s.writeUnsigned(memory.index);
|
||||
}
|
||||
|
||||
@override
|
||||
String get name => 'memory.fill';
|
||||
|
||||
@override
|
||||
void printTo(IrPrinter p) {
|
||||
p.write(name);
|
||||
|
||||
@@ -2,11 +2,12 @@
|
||||
// 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 '../serialize/printer.dart';
|
||||
import '../serialize/serialize.dart';
|
||||
import 'ir.dart';
|
||||
|
||||
/// An (imported or defined) memory.
|
||||
class Memory with Indexable, Exportable {
|
||||
abstract class Memory with Indexable, Exportable {
|
||||
@override
|
||||
final FinalizableIndex finalizableIndex;
|
||||
final bool shared;
|
||||
@@ -41,6 +42,18 @@ class Memory with Indexable, Exportable {
|
||||
/// Export a memory from the module.
|
||||
@override
|
||||
Export buildExport(String name) => MemoryExport(name, this);
|
||||
|
||||
void printTo(IrPrinter p);
|
||||
|
||||
void _printType(IrPrinter p) {
|
||||
// We don't encode the optional address type in our representation because
|
||||
// it defaults to i32 and we don't support 64-bit addressing yet.
|
||||
|
||||
p.write('$minSize');
|
||||
if (maxSize case final max?) {
|
||||
p.write(' $max');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A memory defined in a module.
|
||||
@@ -50,6 +63,27 @@ class DefinedMemory extends Memory implements Serializable {
|
||||
|
||||
@override
|
||||
void serialize(Serializer s) => _serializeLimits(s);
|
||||
|
||||
@override
|
||||
void printTo(IrPrinter p) {
|
||||
p.write('(memory ');
|
||||
p.writeMemoryReference(this);
|
||||
String? exportName;
|
||||
for (final e in enclosingModule.exports.exported) {
|
||||
if (e is MemoryExport && e.memory == this) {
|
||||
exportName = e.name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (exportName != null) {
|
||||
p.write(' ');
|
||||
p.writeExport(exportName);
|
||||
}
|
||||
|
||||
p.write(' ');
|
||||
_printType(p);
|
||||
p.write(')');
|
||||
}
|
||||
}
|
||||
|
||||
/// An imported memory.
|
||||
@@ -69,6 +103,17 @@ class ImportedMemory extends Memory implements Import {
|
||||
s.writeByte(0x02);
|
||||
_serializeLimits(s);
|
||||
}
|
||||
|
||||
@override
|
||||
void printTo(IrPrinter p) {
|
||||
p.write('(memory ');
|
||||
p.writeMemoryReference(this);
|
||||
p.write(' ');
|
||||
p.writeImport(module, name);
|
||||
p.write(' ');
|
||||
_printType(p);
|
||||
p.write(')');
|
||||
}
|
||||
}
|
||||
|
||||
class MemoryExport extends Export {
|
||||
|
||||
@@ -289,6 +289,10 @@ class Module implements Serializable {
|
||||
}
|
||||
}
|
||||
|
||||
for (final memory in [...memories.imported, ...memories.defined]) {
|
||||
mp.enqueueMemory(memory);
|
||||
}
|
||||
|
||||
for (final table in [...tables.imported, ...tables.defined]) {
|
||||
mp.enqueueTable(table);
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@ class ModulePrinter {
|
||||
TableNamer(settings.scrubAbsoluteUris, _module, enqueueTable);
|
||||
late final dataNamer =
|
||||
DataNamer(settings.scrubAbsoluteUris, _module, enqueueDataSegment);
|
||||
late final memoryNamer =
|
||||
MemoryNamer(settings.scrubAbsoluteUris, _module, enqueueMemory);
|
||||
|
||||
final _types = <ir.DefType, String>{};
|
||||
final _tags = <ir.Tag, String>{};
|
||||
@@ -32,6 +34,7 @@ class ModulePrinter {
|
||||
final _globals = <ir.Global, String>{};
|
||||
final _functions = <ir.BaseFunction, String>{};
|
||||
final _dataSegments = <ir.BaseDataSegment, String>{};
|
||||
final _memories = <ir.Memory, String>{};
|
||||
|
||||
final _typeQueue = Queue<ir.DefType>();
|
||||
final _functionsQueue = Queue<ir.DefinedFunction>();
|
||||
@@ -42,8 +45,16 @@ class ModulePrinter {
|
||||
|
||||
ModulePrinter(this._module, {this.settings = const ModulePrintSettings()});
|
||||
|
||||
IrPrinter newIrPrinter() => IrPrinter._(settings.preferMultiline, _module,
|
||||
typeNamer, globalNamer, functionNamer, tagNamer, tableNamer, dataNamer);
|
||||
IrPrinter newIrPrinter() => IrPrinter._(
|
||||
settings.preferMultiline,
|
||||
_module,
|
||||
typeNamer,
|
||||
globalNamer,
|
||||
functionNamer,
|
||||
tagNamer,
|
||||
tableNamer,
|
||||
dataNamer,
|
||||
memoryNamer);
|
||||
|
||||
void enqueueType(ir.DefType type) {
|
||||
if (!_types.containsKey(type)) {
|
||||
@@ -81,6 +92,13 @@ class ModulePrinter {
|
||||
}
|
||||
}
|
||||
|
||||
void enqueueMemory(ir.Memory memory) {
|
||||
if (!_memories.containsKey(memory)) {
|
||||
_memories[memory] = '';
|
||||
_generateMemory(memory);
|
||||
}
|
||||
}
|
||||
|
||||
void enqueueTable(ir.Table table) {
|
||||
if (!_tables.containsKey(table)) {
|
||||
_tables[table] = '';
|
||||
@@ -242,8 +260,10 @@ class ModulePrinter {
|
||||
}
|
||||
}
|
||||
|
||||
printOrdered(_module.memories.imported, memoryNamer, _memories);
|
||||
printOrdered(_module.functions.imported, functionNamer, _functions);
|
||||
printOrdered(_module.globals.imported, globalNamer, _globals);
|
||||
printOrdered(_module.memories.defined, memoryNamer, _memories);
|
||||
printOrdered(_module.tables.imported, tableNamer, _tables);
|
||||
printOrdered(_module.tables.defined, tableNamer, _tables);
|
||||
printOrdered(_module.tags.defined, tagNamer, _tags);
|
||||
@@ -286,6 +306,12 @@ class ModulePrinter {
|
||||
_tags[tag] = p.getText();
|
||||
}
|
||||
|
||||
void _generateMemory(ir.Memory memory) {
|
||||
final p = newIrPrinter();
|
||||
memory.printTo(p);
|
||||
_memories[memory] = p.getText();
|
||||
}
|
||||
|
||||
void _generateTable(ir.Table table) {
|
||||
final p = newIrPrinter();
|
||||
if (table is ir.DefinedTable) {
|
||||
@@ -456,6 +482,7 @@ class IrPrinter extends IndentPrinter {
|
||||
final TagNamer _tagNamer;
|
||||
final TableNamer _tableNamer;
|
||||
final DataNamer _dataNamer;
|
||||
final MemoryNamer _memoryNamer;
|
||||
|
||||
_LocalNamer? _localNamer;
|
||||
final _labelNamer = _LabelNamer();
|
||||
@@ -468,12 +495,21 @@ class IrPrinter extends IndentPrinter {
|
||||
this._functionNamer,
|
||||
this._tagNamer,
|
||||
this._tableNamer,
|
||||
this._dataNamer);
|
||||
this._dataNamer,
|
||||
this._memoryNamer);
|
||||
|
||||
/// Returns a new [IrPrinter] with same settings, but empty indentation,
|
||||
/// empty text content and no local namer.
|
||||
IrPrinter dup() => IrPrinter._(preferMultiline, module, _typeNamer,
|
||||
_globalNamer, _functionNamer, _tagNamer, _tableNamer, _dataNamer);
|
||||
IrPrinter dup() => IrPrinter._(
|
||||
preferMultiline,
|
||||
module,
|
||||
_typeNamer,
|
||||
_globalNamer,
|
||||
_functionNamer,
|
||||
_tagNamer,
|
||||
_tableNamer,
|
||||
_dataNamer,
|
||||
_memoryNamer);
|
||||
|
||||
void beginLabeledBlock(ir.Instruction? instruction) {
|
||||
_labelNamer.stack.add(LabelInfo(instruction));
|
||||
@@ -604,7 +640,7 @@ class IrPrinter extends IndentPrinter {
|
||||
}
|
||||
|
||||
void writeMemoryReference(ir.Memory memory) {
|
||||
throw UnimplementedError();
|
||||
write(_memoryNamer.name(memory));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -759,6 +795,19 @@ class DataNamer extends Namer<ir.BaseDataSegment> {
|
||||
}
|
||||
}
|
||||
|
||||
class MemoryNamer extends Namer<ir.Memory> {
|
||||
MemoryNamer(super.scubUris, super.module, super.onReference);
|
||||
|
||||
@override
|
||||
String name(ir.Memory memory, {bool activateOnReferenceCallback = true}) {
|
||||
return super._name(
|
||||
memory,
|
||||
memory is ir.ImportedMemory ? '${memory.module}.${memory.name}' : null,
|
||||
'memory',
|
||||
activateOnReferenceCallback);
|
||||
}
|
||||
}
|
||||
|
||||
class _LabelNamer {
|
||||
int _nextId = 0;
|
||||
final stack = <LabelInfo>[];
|
||||
|
||||
@@ -0,0 +1,196 @@
|
||||
// Copyright (c) 2026, 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.
|
||||
|
||||
part of 'dart:_wasm';
|
||||
|
||||
/// A [memory type] in WebAssembly, describing the address range and [Limits]
|
||||
/// for memory instances.
|
||||
///
|
||||
/// Dart currently only supports 32-bit memory instances.
|
||||
///
|
||||
/// [memory type]: https://webassembly.github.io/spec/core/syntax/types.html#memory-types
|
||||
@pragma("wasm:entry-point")
|
||||
final class MemoryType {
|
||||
/// Minimum and optional maximum size for the memory.
|
||||
final Limits limits;
|
||||
|
||||
const MemoryType({required this.limits});
|
||||
}
|
||||
|
||||
/// Limits for the size of memories or tables in WebAssembly.
|
||||
final class Limits {
|
||||
/// The minimum size for the memory instance (in units of WebAssembly pages).
|
||||
final int minimum;
|
||||
|
||||
/// An optional maximum size for the instance (in units of WebAsembly pages).
|
||||
final int? maximum;
|
||||
|
||||
const Limits(this.minimum, [this.maximum]);
|
||||
}
|
||||
|
||||
/// An instance of linear memory available to this WebAssembly module.
|
||||
///
|
||||
/// ## Using memories
|
||||
///
|
||||
/// By default, compiling Dart to WebAssembly does not create a memory instance
|
||||
/// (since Dart uses garbage collected types for everything instead). Especially
|
||||
/// when interacting with other modules written in languages based on linear
|
||||
/// memory though, it is necessary to access a linear memory instance from Dart.
|
||||
///
|
||||
/// [MemoryAccessExtension] provides methods to load and store values at certain
|
||||
/// positions in a linear memory instance (e.g. [MemoryAccessExtension.loadInt8]
|
||||
/// or [MemoryAccessExtension.storeInt8]), to inspect its
|
||||
/// [MemoryAccessExtension.size] of memory or to [MemoryAccessExtension.grow]
|
||||
/// it (if supported by the memory instance).
|
||||
///
|
||||
/// In WebAssembly, instructions can't be polymorphic with regards to the memory
|
||||
/// instance they operate on: Each `i8.load` instruction has the target memory
|
||||
/// instance encoded into it.
|
||||
/// This also restricts how memory instances can be used in Dart: Every call
|
||||
/// must use a top-level getter defining the memory as a receiver. It is not
|
||||
/// allowed to call methods on other instances of [Memory]:
|
||||
///
|
||||
/// ```
|
||||
/// @pragma('wasm:memory-type', MemoryType(limits: Limits(1, 10)))
|
||||
/// external Memory get additionalMemory;
|
||||
///
|
||||
/// void main() {
|
||||
/// // Allowed: Direct access to memory instance
|
||||
/// print(additionalMemory.size);
|
||||
///
|
||||
/// // Not allowed: Loading a reference to the memory instance.
|
||||
/// useMemory(additionalMemory);
|
||||
/// }
|
||||
///
|
||||
/// void useMemory(Memory memory) {
|
||||
/// // Not allowed: Dynamic memory instance.
|
||||
/// memory.loadInt32(0, 1337);
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Further, tearing-off methods from [MemoryAccessExtension] is a compile-time
|
||||
/// error.
|
||||
///
|
||||
/// ## Obtaining memory instances
|
||||
///
|
||||
/// To access linear memory, a [Memory] instance needs to be defined or
|
||||
/// imported.
|
||||
///
|
||||
/// To define a memory instance, use a top-level getter defined as `external`
|
||||
/// and with a [MemoryType] annotation:
|
||||
///
|
||||
/// ```
|
||||
/// @pragma('wasm:memory-type', MemoryType(limits: Limits(1, 10)))
|
||||
/// external Memory get additionalMemory;
|
||||
/// ```
|
||||
///
|
||||
/// Memory instances can also be imported from the host environment by
|
||||
/// annotating such getter with the `wasm:import` pragma:
|
||||
///
|
||||
/// ```
|
||||
/// @pragma('wasm:memory-type', MemoryType(limits: Limits(1, 10)))
|
||||
/// @pragma('wasm:import', 'module.name')
|
||||
/// external Memory get mySecondMemory;
|
||||
/// ```
|
||||
///
|
||||
/// ## Restrictions
|
||||
///
|
||||
/// Note that only 32-bit [Memory] instances are supported by Dart at the
|
||||
/// moment.
|
||||
@pragma("wasm:entry-point")
|
||||
final class Memory {
|
||||
Memory._();
|
||||
|
||||
/// The size of a page in WebAssembly memory.
|
||||
static const pageSize = 65536;
|
||||
}
|
||||
|
||||
/// Operators accessing [Memory] instances in WebAssembly.
|
||||
extension MemoryAccessExtension on Memory {
|
||||
/// Returns the size of this memory instance in units of [Memory.pageSize]
|
||||
/// (65536 bytes).
|
||||
@pragma("wasm:intrinsic")
|
||||
external int get size;
|
||||
|
||||
/// Grows the size of this memory instance by the amount of [pages].
|
||||
///
|
||||
/// This returns the old size (also in units of [Memory.pageSize]) if growing
|
||||
/// this memory instance was successful, or `-1` otherwise (e.g. due to an
|
||||
/// out-of-memory error).
|
||||
@pragma("wasm:intrinsic")
|
||||
external int grow(int pages);
|
||||
|
||||
/// Copies the byte [value] to the memory region from [startOffset] to
|
||||
/// [startOffset] plus [length] (exclusive).
|
||||
///
|
||||
/// This causes a WebAssembly trap if the target region is out-of-bounds for
|
||||
/// this memory.
|
||||
@pragma("wasm:intrinsic")
|
||||
external void fill(WasmI32 value, int startOffset, int length);
|
||||
|
||||
@pragma("wasm:intrinsic")
|
||||
external WasmF32 loadFloat32(int address, {int align = 0, int offset = 0});
|
||||
@pragma("wasm:intrinsic")
|
||||
external WasmF64 loadFloat64(int address, {int align = 0, int offset = 0});
|
||||
|
||||
@pragma("wasm:intrinsic")
|
||||
external WasmI32 loadInt8(int address, {int align = 0, int offset = 0});
|
||||
@pragma("wasm:intrinsic")
|
||||
external WasmI32 loadInt16(int address, {int align = 0, int offset = 0});
|
||||
@pragma("wasm:intrinsic")
|
||||
external WasmI32 loadInt32(int address, {int align = 0, int offset = 0});
|
||||
@pragma("wasm:intrinsic")
|
||||
external WasmI64 loadInt64(int address, {int align = 0, int offset = 0});
|
||||
|
||||
@pragma("wasm:intrinsic")
|
||||
external WasmI32 loadUint8(int address, {int align = 0, int offset = 0});
|
||||
@pragma("wasm:intrinsic")
|
||||
external WasmI32 loadUint16(int address, {int align = 0, int offset = 0});
|
||||
@pragma("wasm:intrinsic")
|
||||
external WasmI32 loadUint32(int address, {int align = 0, int offset = 0});
|
||||
|
||||
@pragma("wasm:intrinsic")
|
||||
external void storeFloat32(
|
||||
int address,
|
||||
WasmF32 value, {
|
||||
int align = 0,
|
||||
int offset = 0,
|
||||
});
|
||||
@pragma("wasm:intrinsic")
|
||||
external void storeFloat64(
|
||||
int address,
|
||||
WasmF64 value, {
|
||||
int align = 0,
|
||||
int offset = 0,
|
||||
});
|
||||
|
||||
@pragma("wasm:intrinsic")
|
||||
external void storeInt8(
|
||||
int address,
|
||||
WasmI32 value, {
|
||||
int align = 0,
|
||||
int offset = 0,
|
||||
});
|
||||
@pragma("wasm:intrinsic")
|
||||
external void storeInt16(
|
||||
int address,
|
||||
WasmI32 value, {
|
||||
int align = 0,
|
||||
int offset = 0,
|
||||
});
|
||||
@pragma("wasm:intrinsic")
|
||||
external void storeInt32(
|
||||
int address,
|
||||
WasmI32 value, {
|
||||
int align = 0,
|
||||
int offset = 0,
|
||||
});
|
||||
@pragma("wasm:intrinsic")
|
||||
external void storeInt64(
|
||||
int address,
|
||||
WasmI64 value, {
|
||||
int align = 0,
|
||||
int offset = 0,
|
||||
});
|
||||
}
|
||||
@@ -2,4 +2,7 @@
|
||||
# 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.
|
||||
|
||||
wasm_sdk_sources = [ "wasm_types.dart" ]
|
||||
wasm_sdk_sources = [
|
||||
"memory.dart",
|
||||
"wasm_types.dart",
|
||||
]
|
||||
|
||||
@@ -6,6 +6,8 @@ library dart._wasm;
|
||||
|
||||
import 'dart:js_interop';
|
||||
|
||||
part 'memory.dart';
|
||||
|
||||
// A collection a special Dart types that are mapped directly to Wasm types
|
||||
// by the dart2wasm compiler. These types have a number of constraints:
|
||||
//
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
// Copyright (c) 2026, 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.
|
||||
//
|
||||
// dart2wasmOptions=--enable-deferred-loading --extra-compiler-option=--enable-experimental-wasm-interop
|
||||
|
||||
import 'dart:_wasm';
|
||||
|
||||
import '' deferred as M;
|
||||
|
||||
import 'package:expect/expect.dart';
|
||||
|
||||
void main() async {
|
||||
await M.loadLibrary();
|
||||
|
||||
write();
|
||||
Expect.equals(42, M.read());
|
||||
}
|
||||
|
||||
void write() {
|
||||
M.memory.storeInt64(0, WasmI64.fromInt(42));
|
||||
}
|
||||
|
||||
@pragma('wasm:memory-type', MemoryType(limits: Limits(1)))
|
||||
external Memory get memory;
|
||||
|
||||
@pragma('wasm:never-inline')
|
||||
int read() {
|
||||
return memory.loadInt64(0).toInt();
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
// Copyright (c) 2026, 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.
|
||||
|
||||
// dart2wasmOptions=--extra-compiler-option=--enable-experimental-wasm-interop
|
||||
|
||||
import 'dart:_wasm';
|
||||
|
||||
external Memory get missingAnnotation;
|
||||
// ^
|
||||
// [web] This external getter returns a memory instance, but no annotation describing it was found
|
||||
|
||||
@pragma('wasm:memory-type', MemoryType(limits: Limits(1, 10)))
|
||||
external Memory get validDefinition;
|
||||
|
||||
void main() {
|
||||
validDefinition.loadUint8(10);
|
||||
validDefinition.size;
|
||||
|
||||
print(validDefinition);
|
||||
// ^
|
||||
// [web] WebAssembly elements may only be referenced to directly call a method on them.
|
||||
|
||||
print(validDefinition.fill);
|
||||
// ^
|
||||
// [web] This intrinsic extension member may not be torn off.
|
||||
}
|
||||
|
||||
void invalidDynamicMemory(Memory memory) {
|
||||
memory.loadUint8(10);
|
||||
// ^
|
||||
// [web] The receiver of this call must be a top-level variable describing the WebAssembly element.
|
||||
}
|
||||
|
||||
int get notAConstant => 3;
|
||||
|
||||
void invalidNonConstantArguments() {
|
||||
validDefinition.loadUint8(0, offset: 12, align: 1);
|
||||
validDefinition.loadUint8(0, offset: notAConstant);
|
||||
// ^
|
||||
// [web] The variable 'offset' is not a constant, only constant expressions are allowed.
|
||||
validDefinition.loadUint8(0, align: notAConstant);
|
||||
// ^
|
||||
// [web] The variable 'align' is not a constant, only constant expressions are allowed.
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
// Copyright (c) 2026, 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.
|
||||
|
||||
// dart2wasmOptions=--extra-compiler-option=--enable-experimental-wasm-interop
|
||||
|
||||
import 'dart:_wasm';
|
||||
|
||||
import 'package:expect/expect.dart';
|
||||
|
||||
@pragma('wasm:memory-type', MemoryType(limits: Limits(1, 10)))
|
||||
external Memory get _memory;
|
||||
|
||||
void main() {
|
||||
_testSizeAndGrow();
|
||||
_testFill();
|
||||
|
||||
_testFloat();
|
||||
_testInt();
|
||||
_testOffset();
|
||||
}
|
||||
|
||||
void _testSizeAndGrow() {
|
||||
Expect.equals(1, _memory.size);
|
||||
Expect.equals(1, _memory.grow(1));
|
||||
Expect.equals(2, _memory.size);
|
||||
|
||||
Expect.equals(0, _memory.loadInt32(Memory.pageSize + 1).toIntSigned());
|
||||
|
||||
Expect.equals(-1, _memory.grow(20));
|
||||
}
|
||||
|
||||
void _testFill() {
|
||||
_memory.fill(WasmI32.fromInt(42), 0, 1024);
|
||||
for (var i = 0; i < 1024; i++) {
|
||||
Expect.equals(42, _memory.loadUint8(i).toIntSigned());
|
||||
}
|
||||
}
|
||||
|
||||
void _testFloat() {
|
||||
_memory.storeFloat64(0, WasmF64.fromDouble(1.5));
|
||||
Expect.equals(1.5, _memory.loadFloat64(0).toDouble());
|
||||
|
||||
_memory.storeFloat32(0, WasmF32.fromDouble(1.5));
|
||||
Expect.equals(1.5, _memory.loadFloat32(0).toDouble());
|
||||
}
|
||||
|
||||
void _testInt() {
|
||||
_memory.storeInt8(0, WasmI32.fromInt(-1));
|
||||
Expect.equals(-1, _memory.loadInt8(0).toIntSigned());
|
||||
Expect.equals(255, _memory.loadUint8(0).toIntUnsigned());
|
||||
}
|
||||
|
||||
void _testOffset() {
|
||||
_memory.storeInt32(0, WasmI32.fromInt(0x01020304));
|
||||
Expect.equals(0x01, _memory.loadInt8(0, offset: 3).toIntSigned());
|
||||
Expect.equals(0x02, _memory.loadInt8(0, offset: 2).toIntSigned());
|
||||
Expect.equals(0x03, _memory.loadInt8(0, offset: 1).toIntSigned());
|
||||
Expect.equals(0x04, _memory.loadInt8(0, offset: 0).toIntSigned());
|
||||
}
|
||||
Reference in New Issue
Block a user