[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:
Simon Binder
2026-01-15 03:45:21 -08:00
committed by Commit Queue
parent 47ba2f23a1
commit 7d8cd032dc
50 changed files with 1365 additions and 280 deletions
+3
View File
@@ -7,6 +7,9 @@ analyzer:
implementation_imports: ignore
library_prefixes: ignore
exclude:
- test/ir_tests/*.dart
linter:
rules:
- directives_ordering
-1
View File
@@ -110,7 +110,6 @@ class DynamicSubmoduleOutputData extends ModuleOutputData {
}
class DynamicMainModuleStrategy extends ModuleStrategy with KernelNodes {
@override
final Component component;
@override
final CoreTypes coreTypes;
+24 -43
View File
@@ -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);
}
+178 -5
View File
@@ -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;
}
+8 -2
View File
@@ -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");
+2 -5
View File
@@ -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 {
+13 -27
View File
@@ -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,
);
}
}
}
+60 -1
View File
@@ -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);
+49
View File
@@ -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);
+54
View File
@@ -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);
}
}
+172
View File
@@ -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
+12 -12
View File
@@ -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) <...>)
+2 -2
View File
@@ -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
)
+3 -3
View File
@@ -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(
+4
View File
@@ -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?
+17
View File
@@ -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);
+46 -1
View File
@@ -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 {
+4
View File
@@ -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>[];
+196
View File
@@ -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,
});
}
+4 -1
View File
@@ -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",
]
+2
View File
@@ -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:
//
+30
View File
@@ -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.
}
+60
View File
@@ -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());
}