[wasm_builder] Refactor to a builder / built pattern.

This CL mostly just moves code around. There are three broad changes in this CL:
1) Reify the builder / built pattern that exists implicitly in the existing code. Builders now live in `src/builder`, while the built ir lives in `src/ir`.
2) Reify the module subsections.
3) `pkg/dart2wasm` has been updated to use the new API.

There is only one minor logic change in the entire CL, we now defer serialization of a module until the bytes are actually required, as opposed to serializing eagerly.

This change is designed to make the wasm_builder more robust. By clearly delineating which parts of the AST are mutable and which parts are immutable, then it should make it easier for users of the wasm_builder to avoid undefined behavior, i.e. holding on to something that can change.

Change-Id: I676107b867aa74fabf413108673e170126bdb5c1
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/316280
Reviewed-by: Ömer Ağacan <omersa@google.com>
Commit-Queue: Joshua Litt <joshualitt@google.com>
This commit is contained in:
Joshua Litt
2023-07-31 15:27:58 +00:00
committed by Commit Queue
parent c1d081fbcd
commit 96d6c2e0d3
54 changed files with 4079 additions and 3415 deletions
+7 -7
View File
@@ -257,7 +257,7 @@ class _ExceptionHandlerStack {
/// CFG block.
///
/// Call this when generating a new CFG block.
void generateTryBlocks(w.Instructions b) {
void generateTryBlocks(w.InstructionsBuilder b) {
final handlersToCover = _handlers.length - coveredHandlers;
if (handlersToCover == 0) {
@@ -543,7 +543,7 @@ class AsyncCodeGenerator extends CodeGenerator {
}
@override
w.DefinedFunction generateLambda(Lambda lambda, Closures closures) {
w.BaseFunction generateLambda(Lambda lambda, Closures closures) {
this.closures = closures;
setupLambdaParametersAndContexts(lambda);
_generateBodies(lambda.functionNode);
@@ -568,8 +568,8 @@ class AsyncCodeGenerator extends CodeGenerator {
// Wasm function containing the body of the `async` function
// (`_AyncResumeFun`).
final w.DefinedFunction resumeFun = m.addFunction(
m.addFunctionType([
final resumeFun = m.functions.define(
m.types.defineFunction([
asyncSuspendStateInfo.nonNullableType, // _AsyncSuspendState
translator.topInfo.nullableType, // Object?, await value
translator.topInfo.nullableType, // Object?, error value
@@ -596,8 +596,8 @@ class AsyncCodeGenerator extends CodeGenerator {
_generateInner(functionNode, context, resumeFun);
}
void _generateOuter(FunctionNode functionNode, Context? context,
w.DefinedFunction resumeFun) {
void _generateOuter(
FunctionNode functionNode, Context? context, w.BaseFunction resumeFun) {
// Outer (wrapper) function creates async state, calls the inner function
// (which runs until first suspension point, i.e. `await`), and returns the
// completer's future.
@@ -712,7 +712,7 @@ class AsyncCodeGenerator extends CodeGenerator {
}
void _generateInner(FunctionNode functionNode, Context? context,
w.DefinedFunction resumeFun) {
w.FunctionBuilder resumeFun) {
// void Function(_AsyncSuspendState, Object?)
// Set the current Wasm function for the code generator to the inner
+5 -5
View File
@@ -295,12 +295,12 @@ class ClassInfoCollector {
ClassInfoCollector(this.translator);
w.Module get m => translator.m;
w.ModuleBuilder get m => translator.m;
TranslatorOptions get options => translator.options;
void _initializeTop() {
final w.StructType struct = m.addStructType("#Top");
final w.StructType struct = m.types.defineStruct("#Top");
topInfo = ClassInfo(null, _nextClassId++, 0, struct, null);
translator.classes.add(topInfo);
translator.classForHeapType[struct] = topInfo;
@@ -314,7 +314,7 @@ class ClassInfoCollector {
if (superclass == null) {
ClassInfo superInfo = topInfo;
final w.StructType struct =
m.addStructType(cls.name, superType: superInfo.struct);
m.types.defineStruct(cls.name, superType: superInfo.struct);
info = ClassInfo(
cls, _nextClassId++, superInfo.depth + 1, struct, superInfo);
// Mark Top type as implementing Object to force the representation
@@ -368,7 +368,7 @@ class ClassInfoCollector {
cls.fields.where((f) => f.isInstanceMember).isEmpty;
w.StructType struct = canReuseSuperStruct
? superInfo.struct
: m.addStructType(cls.name, superType: superInfo.struct);
: m.types.defineStruct(cls.name, superType: superInfo.struct);
info = ClassInfo(
cls, _nextClassId++, superInfo.depth + 1, struct, superInfo,
typeParameterMatch: typeParameterMatch);
@@ -416,7 +416,7 @@ class ClassInfoCollector {
final struct = _recordStructs.putIfAbsent(
numFields,
() => m.addStructType(
() => m.types.defineStruct(
'Record$numFields',
superType: translator.recordInfo.struct,
));
+55 -53
View File
@@ -26,10 +26,10 @@ class ClosureImplementation {
///
/// This list does not include the dynamic call entry and the instantiation
/// function.
final List<w.DefinedFunction> functions;
final List<w.BaseFunction> functions;
/// The vtable entry used for dynamic calls.
final w.DefinedFunction dynamicCallEntry;
final w.BaseFunction dynamicCallEntry;
/// The constant global variable pointing to the vtable.
final w.Global vtable;
@@ -62,22 +62,22 @@ class ClosureRepresentation {
final w.StructType? instantiationContextStruct;
/// Entry point functions for instantiations of this generic closure.
late final List<w.DefinedFunction> instantiationTrampolines =
late final List<w.BaseFunction> instantiationTrampolines =
_instantiationTrampolinesThunk!();
List<w.DefinedFunction> Function()? _instantiationTrampolinesThunk;
List<w.BaseFunction> Function()? _instantiationTrampolinesThunk;
/// The function that instantiates this generic closure.
late final w.DefinedFunction instantiationFunction =
late final w.BaseFunction instantiationFunction =
_instantiationFunctionThunk!();
w.DefinedFunction Function()? _instantiationFunctionThunk;
w.BaseFunction Function()? _instantiationFunctionThunk;
/// The function that takes instantiation context of this generic closure and
/// another instantiation context (both as `ref
/// #InstantiationClosureContextBase`) and compares types in the contexts.
/// This function is used to implement function equality of instantiations.
late final w.DefinedFunction instantiationTypeComparisonFunction =
late final w.BaseFunction instantiationTypeComparisonFunction =
_instantiationTypeComparisonFunctionThunk!();
w.DefinedFunction Function()? _instantiationTypeComparisonFunctionThunk;
w.BaseFunction Function()? _instantiationTypeComparisonFunctionThunk;
/// The signature of the function that instantiates this generic closure.
w.FunctionType get instantiationFunctionType {
@@ -179,13 +179,13 @@ class ClosureLayouter extends RecursiveVisitor {
// by [closureBaseStruct] instead of the fully initialized version
// ([vtableBaseStruct]) to break the type cycle.
late final w.StructType _vtableBaseStructBare =
m.addStructType("#VtableBase");
m.types.defineStruct("#VtableBase");
/// Base struct for instantiation closure contexts. Type tests against this
/// type is used in `_Closure._equals` to check if a closure is an
/// instantiation.
late final w.StructType instantiationContextBaseStruct =
m.addStructType("#InstantiationClosureContextBase", fields: [
m.types.defineStruct("#InstantiationClosureContextBase", fields: [
w.FieldType(w.RefType.def(closureBaseStruct, nullable: false),
mutable: false),
]);
@@ -198,7 +198,7 @@ class ClosureLayouter extends RecursiveVisitor {
mutable: false));
/// Base struct for generic closure vtables.
late final w.StructType genericVtableBaseStruct = m.addStructType(
late final w.StructType genericVtableBaseStruct = m.types.defineStruct(
"#GenericVtableBase",
fields: vtableBaseStruct.fields.toList()
..add(w.FieldType(
@@ -209,7 +209,7 @@ class ClosureLayouter extends RecursiveVisitor {
/// Type of [ClosureRepresentation.instantiationTypeComparisonFunction].
late final w.FunctionType instantiationClosureTypeComparisonFunctionType =
m.addFunctionType(
m.types.defineFunction(
[
w.RefType.def(instantiationContextBaseStruct, nullable: false),
w.RefType.def(instantiationContextBaseStruct, nullable: false)
@@ -231,7 +231,8 @@ class ClosureLayouter extends RecursiveVisitor {
w.StructType _getInstantiationContextBaseStruct(int numTypes) =>
_instantiationContextBaseStructs.putIfAbsent(
numTypes,
() => m.addStructType("#InstantiationClosureContextBase-$numTypes",
() => m.types.defineStruct(
"#InstantiationClosureContextBase-$numTypes",
fields: [
w.FieldType(w.RefType.def(closureBaseStruct, nullable: false),
mutable: false),
@@ -239,9 +240,9 @@ class ClosureLayouter extends RecursiveVisitor {
],
superType: instantiationContextBaseStruct));
final Map<int, w.DefinedFunction> _instantiationTypeComparisonFunctions = {};
final Map<int, w.BaseFunction> _instantiationTypeComparisonFunctions = {};
w.DefinedFunction _getInstantiationTypeComparisonFunction(int numTypes) =>
w.BaseFunction _getInstantiationTypeComparisonFunction(int numTypes) =>
_instantiationTypeComparisonFunctions.putIfAbsent(
numTypes, () => _createInstantiationTypeComparisonFunction(numTypes));
@@ -253,7 +254,7 @@ class ClosureLayouter extends RecursiveVisitor {
// - A context reference (used for `this` in tear-offs)
// - A vtable reference
// - A `_FunctionType`
return m.addStructType(name,
return m.types.defineStruct(name,
fields: [
w.FieldType(w.NumType.i32),
w.FieldType(w.NumType.i32),
@@ -265,7 +266,7 @@ class ClosureLayouter extends RecursiveVisitor {
superType: superType);
}
w.Module get m => translator.m;
w.ModuleBuilder get m => translator.m;
w.ValueType get topType => translator.topInfo.nullableType;
ClosureLayouter(this.translator)
@@ -348,7 +349,7 @@ class ClosureLayouter extends RecursiveVisitor {
String closureName = ["#Closure", ...nameTags].join("-");
w.StructType parentVtableStruct = parent?.vtableStruct ??
(typeCount == 0 ? vtableBaseStruct : genericVtableBaseStruct);
w.StructType vtableStruct = m.addStructType(vtableName,
w.StructType vtableStruct = m.types.defineStruct(vtableName,
fields: parentVtableStruct.fields, superType: parentVtableStruct);
w.StructType closureStruct = _makeClosureStruct(
closureName, vtableStruct, parent?.closureStruct ?? closureBaseStruct);
@@ -363,7 +364,7 @@ class ClosureLayouter extends RecursiveVisitor {
w.RefType outputType = w.RefType.def(
instantiatedRepresentation.closureStruct,
nullable: false);
w.FunctionType instantiationFunctionType = m.addFunctionType(
w.FunctionType instantiationFunctionType = m.types.defineFunction(
[inputType, ...List.filled(typeCount, typeType)], [outputType],
superType: parent?.instantiationFunctionType);
w.FieldType functionFieldType = w.FieldType(
@@ -382,18 +383,19 @@ class ClosureLayouter extends RecursiveVisitor {
// original closure plus the type arguments.
String instantiationContextName =
["#InstantiationContext", ...nameTags].join("-");
instantiationContextStruct = m.addStructType(instantiationContextName,
fields: [
w.FieldType(w.RefType.def(closureStruct, nullable: false),
mutable: false),
...List.filled(typeCount, w.FieldType(typeType, mutable: false))
],
superType: _getInstantiationContextBaseStruct(typeCount));
instantiationContextStruct =
m.types.defineStruct(instantiationContextName,
fields: [
w.FieldType(w.RefType.def(closureStruct, nullable: false),
mutable: false),
...List.filled(typeCount, w.FieldType(typeType, mutable: false))
],
superType: _getInstantiationContextBaseStruct(typeCount));
}
// Add vtable fields for additional entry points relative to the parent.
for (int paramCount in paramCounts) {
w.FunctionType entry = m.addFunctionType([
w.FunctionType entry = m.types.defineFunction([
w.RefType.struct(nullable: false),
...List.filled(typeCount, typeType),
...List.filled(paramCount, topType)
@@ -420,12 +422,12 @@ class ClosureLayouter extends RecursiveVisitor {
// generation, after the imports have been added.
representation._instantiationTrampolinesThunk = () {
List<w.DefinedFunction> instantiationTrampolines = [
List<w.BaseFunction> instantiationTrampolines = [
...?parent?.instantiationTrampolines
];
if (names.isEmpty) {
// Add trampoline to the corresponding entry in the generic closure.
w.DefinedFunction trampoline = _createInstantiationTrampoline(
w.BaseFunction trampoline = _createInstantiationTrampoline(
typeCount,
closureStruct,
instantiationContextStruct!,
@@ -442,7 +444,7 @@ class ClosureLayouter extends RecursiveVisitor {
for (NameCombination combination
in instantiatedRepresentation!._indexOfCombination!.keys) {
int? genericIndex = indexOfCombination![combination];
w.DefinedFunction trampoline = genericIndex != null
w.BaseFunction trampoline = genericIndex != null
? _createInstantiationTrampoline(
typeCount,
closureStruct,
@@ -486,7 +488,7 @@ class ClosureLayouter extends RecursiveVisitor {
return representation;
}
w.DefinedFunction _createInstantiationTrampoline(
w.BaseFunction _createInstantiationTrampoline(
int typeCount,
w.StructType genericClosureStruct,
w.StructType contextStruct,
@@ -504,8 +506,8 @@ class ClosureLayouter extends RecursiveVisitor {
assert(genericFunctionType.inputs.length ==
instantiatedFunctionType.inputs.length + typeCount);
w.DefinedFunction trampoline = m.addFunction(instantiatedFunctionType);
w.Instructions b = trampoline.body;
final trampoline = m.functions.define(instantiatedFunctionType);
final b = trampoline.body;
// Cast context reference to actual context type.
w.RefType contextType = w.RefType.def(contextStruct, nullable: false);
@@ -541,12 +543,12 @@ class ClosureLayouter extends RecursiveVisitor {
return trampoline;
}
w.DefinedFunction _createInstantiationDynamicCallEntry(
w.BaseFunction _createInstantiationDynamicCallEntry(
int typeCount, w.StructType instantiationContextStruct) {
w.DefinedFunction function = m.addFunction(
final function = m.functions.define(
translator.dynamicCallVtableEntryFunctionType,
"instantiation dynamic call entry");
w.Instructions b = function.body;
final b = function.body;
final instantiatedClosureLocal = function.locals[0];
// First argument is the type list, which will always be empty. We'll pass
@@ -603,10 +605,10 @@ class ClosureLayouter extends RecursiveVisitor {
return function;
}
w.DefinedFunction _createInstantiationFunction(
w.BaseFunction _createInstantiationFunction(
int typeCount,
ClosureRepresentation instantiatedRepresentation,
List<w.DefinedFunction> instantiationTrampolines,
List<w.BaseFunction> instantiationTrampolines,
w.FunctionType functionType,
w.StructType contextStruct,
w.StructType genericClosureStruct,
@@ -620,20 +622,20 @@ class ClosureLayouter extends RecursiveVisitor {
assert(functionType.outputs.single == instantiatedClosureType);
// Create vtable for the instantiated closure, containing the trampolines.
w.DefinedGlobal vtable = m.addGlobal(w.GlobalType(
final vtable = m.globals.define(w.GlobalType(
w.RefType.def(instantiatedRepresentation.vtableStruct, nullable: false),
mutable: false));
w.Instructions ib = vtable.initializer;
final ib = vtable.initializer;
ib.ref_func(_createInstantiationDynamicCallEntry(typeCount, contextStruct));
for (w.DefinedFunction trampoline in instantiationTrampolines) {
for (w.BaseFunction trampoline in instantiationTrampolines) {
ib.ref_func(trampoline);
}
ib.struct_new(instantiatedRepresentation.vtableStruct);
ib.end();
w.DefinedFunction instantiationFunction = m.addFunction(functionType, name);
final instantiationFunction = m.functions.define(functionType, name);
w.Local preciseClosure = instantiationFunction.addLocal(genericClosureType);
w.Instructions b = instantiationFunction.body;
final b = instantiationFunction.body;
// Parameters to the instantiation function
final w.Local closureParam = instantiationFunction.locals[0];
@@ -691,12 +693,12 @@ class ClosureLayouter extends RecursiveVisitor {
return instantiationFunction;
}
w.DefinedFunction _createInstantiationTypeComparisonFunction(int numTypes) {
final function = m.addFunction(
w.BaseFunction _createInstantiationTypeComparisonFunction(int numTypes) {
final function = m.functions.define(
instantiationClosureTypeComparisonFunctionType,
"#InstantiationTypeComparison-$numTypes");
final w.Instructions b = function.body;
final b = function.body;
final contextStructType = _getInstantiationContextBaseStruct(numTypes);
final contextRefType = w.RefType.def(contextStructType, nullable: false);
@@ -921,7 +923,7 @@ class ClosureRepresentationCluster {
/// A local function or function expression.
class Lambda {
final FunctionNode functionNode;
final w.DefinedFunction function;
final w.FunctionBuilder function;
Lambda(this.functionNode, this.function);
}
@@ -1017,7 +1019,7 @@ class Closures {
Translator get translator => codeGen.translator;
w.Module get m => translator.m;
w.ModuleBuilder get m => translator.m;
late final w.ValueType typeType =
translator.classInfo[translator.typeClass]!.nonNullableType;
@@ -1046,7 +1048,7 @@ class Closures {
// Make struct definitions
for (Context context in contexts.values) {
if (!context.isEmpty) {
context.struct = m.addStructType("<context>");
context.struct = m.types.defineStruct("<context>");
}
}
@@ -1092,7 +1094,7 @@ class CaptureFinder extends RecursiveVisitor {
Translator get translator => closures.translator;
w.Module get m => translator.m;
w.ModuleBuilder get m => translator.m;
@override
void visitFunctionNode(FunctionNode node) {
@@ -1209,9 +1211,9 @@ class CaptureFinder extends RecursiveVisitor {
translator.translateType(param.type)
];
List<w.ValueType> outputs = [translator.translateType(node.returnType)];
w.FunctionType type = m.addFunctionType(inputs, outputs);
w.DefinedFunction function =
m.addFunction(type, "$member closure at ${node.location}");
w.FunctionType type = m.types.defineFunction(inputs, outputs);
final function =
m.functions.define(type, "$member closure at ${node.location}");
closures.lambdas[node] = Lambda(node, function);
functionIsSyncStarOrAsync.add(node.asyncMarker == AsyncMarker.SyncStar ||
+5 -5
View File
@@ -40,7 +40,7 @@ import 'package:wasm_builder/wasm_builder.dart' as w;
class CodeGenerator extends ExpressionVisitor1<w.ValueType, w.ValueType>
implements InitializerVisitor<void>, StatementVisitor<void> {
final Translator translator;
w.DefinedFunction function;
w.FunctionBuilder function;
final Reference reference;
late final List<w.Local> paramLocals;
final w.Label? returnLabel;
@@ -95,7 +95,7 @@ class CodeGenerator extends ExpressionVisitor1<w.ValueType, w.ValueType>
factory CodeGenerator.forFunction(
Translator translator,
FunctionNode? functionNode,
w.DefinedFunction function,
w.FunctionBuilder function,
Reference reference) {
bool isSyncStar = functionNode?.asyncMarker == AsyncMarker.SyncStar &&
!reference.isTearOffReference;
@@ -112,8 +112,8 @@ class CodeGenerator extends ExpressionVisitor1<w.ValueType, w.ValueType>
}
}
w.Module get m => translator.m;
w.Instructions get b => function.body;
w.ModuleBuilder get m => translator.m;
w.InstructionsBuilder get b => function.body;
Member get member => reference.asMember;
@@ -475,7 +475,7 @@ class CodeGenerator extends ExpressionVisitor1<w.ValueType, w.ValueType>
}
/// Generate code for the body of a lambda.
w.DefinedFunction generateLambda(Lambda lambda, Closures closures) {
w.BaseFunction generateLambda(Lambda lambda, Closures closures) {
// Initialize closure information from enclosing member.
this.closures = closures;
+12 -3
View File
@@ -35,12 +35,21 @@ import 'package:dart2wasm/records.dart';
import 'package:dart2wasm/target.dart' hide Mode;
import 'package:dart2wasm/target.dart' as wasm show Mode;
import 'package:dart2wasm/translator.dart';
import 'package:wasm_builder/wasm_builder.dart' show Module, Serializer;
class CompilerOutput {
final Uint8List wasmModule;
final Module _wasmModule;
final String jsRuntime;
CompilerOutput(this.wasmModule, this.jsRuntime);
late final Uint8List wasmModule = _serializeWasmModule();
Uint8List _serializeWasmModule() {
final s = Serializer();
_wasmModule.serialize(s);
return s.data;
}
CompilerOutput(this._wasmModule, this.jsRuntime);
}
/// Compile a Dart file into a Wasm module.
@@ -126,7 +135,7 @@ Future<CompilerOutput?> compileToModule(compiler.CompilerOptions options,
options.outputFile, depFile);
}
Uint8List wasmModule = translator.translate();
final wasmModule = translator.translate();
String jsRuntime =
jsRuntimeFinalizer.generate(translator.functions.translatedProcedures);
return CompilerOutput(wasmModule, jsRuntime);
+36 -35
View File
@@ -20,8 +20,8 @@ const int maxArrayNewFixedLength = 10000;
class ConstantInfo {
final Constant constant;
final w.DefinedGlobal global;
final w.DefinedFunction? function;
final w.Global global;
final w.BaseFunction? function;
ConstantInfo(this.constant, this.global, this.function);
@@ -29,7 +29,7 @@ class ConstantInfo {
}
typedef ConstantCodeGenerator = void Function(
w.DefinedFunction?, w.Instructions);
w.FunctionBuilder?, w.InstructionsBuilder);
/// Handles the creation of Dart constants.
///
@@ -47,9 +47,9 @@ typedef ConstantCodeGenerator = void Function(
class Constants {
final Translator translator;
final Map<Constant, ConstantInfo> constantInfo = {};
w.DataSegment? oneByteStringSegment;
w.DataSegment? twoByteStringSegment;
late final w.DefinedGlobal emptyTypeList;
w.DataSegmentBuilder? oneByteStringSegment;
w.DataSegmentBuilder? twoByteStringSegment;
late final w.Global emptyTypeList;
late final ClassInfo typeInfo = translator.classInfo[translator.typeClass]!;
bool currentlyCreating = false;
@@ -58,7 +58,7 @@ class Constants {
_initEmptyTypeList();
}
w.Module get m => translator.m;
w.ModuleBuilder get m => translator.m;
void _initEmptyTypeList() {
ClassInfo info = translator.classInfo[translator.immutableListClass]!;
@@ -66,8 +66,9 @@ class Constants {
// Create the empty type list with its type parameter uninitialized for now.
w.RefType emptyListType = info.nonNullableType;
emptyTypeList = m.addGlobal(w.GlobalType(emptyListType, mutable: false));
w.Instructions ib = emptyTypeList.initializer;
final emptyTypeListBuilder =
m.globals.define(w.GlobalType(emptyListType, mutable: false));
w.InstructionsBuilder ib = emptyTypeListBuilder.initializer;
ib.i32_const(info.classId);
ib.i32_const(initialIdentityHash);
ib.ref_null(w.HeapType.none); // Initialized later
@@ -75,6 +76,7 @@ class Constants {
ib.array_new_fixed(translator.listArrayType, 0);
ib.struct_new(info.struct);
ib.end(); // end of global initializer expression
emptyTypeList = emptyTypeListBuilder;
Constant emptyTypeListConstant = ListConstant(
InterfaceType(translator.typeClass, Nullability.nonNullable), const []);
@@ -83,7 +85,7 @@ class Constants {
// Initialize the type parameter of the empty type list to the type object
// for _Type, which itself refers to the empty type list.
w.Instructions b = translator.initFunction.body;
final b = translator.initFunction.body;
b.global_get(emptyTypeList);
instantiateConstant(
translator.initFunction,
@@ -132,7 +134,7 @@ class Constants {
}
/// Emit code to push a constant onto the stack.
void instantiateConstant(w.DefinedFunction? function, w.Instructions b,
void instantiateConstant(w.BaseFunction? function, w.InstructionsBuilder b,
Constant constant, w.ValueType expectedType) {
if (expectedType == translator.voidMarker) return;
ConstantInstantiator(this, function, b, expectedType).instantiate(constant);
@@ -141,15 +143,15 @@ class Constants {
class ConstantInstantiator extends ConstantVisitor<w.ValueType> {
final Constants constants;
final w.DefinedFunction? function;
final w.Instructions b;
final w.BaseFunction? function;
final w.InstructionsBuilder b;
final w.ValueType expectedType;
ConstantInstantiator(
this.constants, this.function, this.b, this.expectedType);
Translator get translator => constants.translator;
w.Module get m => translator.m;
w.ModuleBuilder get m => translator.m;
void instantiate(Constant constant) {
w.ValueType resultType = constant.accept(this);
@@ -260,7 +262,7 @@ class ConstantCreator extends ConstantVisitor<ConstantInfo?> {
Translator get translator => constants.translator;
Types get types => translator.types;
w.Module get m => constants.m;
w.ModuleBuilder get m => constants.m;
ConstantInfo? ensureConstant(Constant constant) {
ConstantInfo? info = constants.constantInfo[constant];
@@ -279,15 +281,14 @@ class ConstantCreator extends ConstantVisitor<ConstantInfo?> {
assert(!type.nullable);
if (lazy) {
// Create uninitialized global and function to initialize it.
w.DefinedGlobal global =
m.addGlobal(w.GlobalType(type.withNullability(true)));
final global = m.globals.define(w.GlobalType(type.withNullability(true)));
global.initializer.ref_null(w.HeapType.none);
global.initializer.end();
w.FunctionType ftype = m.addFunctionType(const [], [type]);
w.DefinedFunction function = m.addFunction(ftype, "$constant");
w.FunctionType ftype = m.types.defineFunction(const [], [type]);
final function = m.functions.define(ftype, "$constant");
generator(function, function.body);
w.Local temp = function.addLocal(type);
w.Instructions b2 = function.body;
final b2 = function.body;
b2.local_tee(temp);
b2.global_set(global);
b2.local_get(temp);
@@ -298,7 +299,7 @@ class ConstantCreator extends ConstantVisitor<ConstantInfo?> {
// Create global with the constant in its initializer.
assert(!constants.currentlyCreating);
constants.currentlyCreating = true;
w.DefinedGlobal global = m.addGlobal(w.GlobalType(type, mutable: false));
final global = m.globals.define(w.GlobalType(type, mutable: false));
generator(null, global.initializer);
global.initializer.end();
constants.currentlyCreating = false;
@@ -358,14 +359,14 @@ class ConstantCreator extends ConstantVisitor<ConstantInfo?> {
b.i32_const(initialIdentityHash);
if (lazy) {
// Initialize string contents from passive data segment.
w.DataSegment segment;
w.DataSegmentBuilder segment;
Uint8List bytes;
if (isOneByte) {
segment = constants.oneByteStringSegment ??= m.addDataSegment();
segment = constants.oneByteStringSegment ??= m.dataSegments.define();
bytes = Uint8List.fromList(constant.value.codeUnits);
} else {
assert(Endian.host == Endian.little);
segment = constants.twoByteStringSegment ??= m.addDataSegment();
segment = constants.twoByteStringSegment ??= m.dataSegments.define();
bytes = Uint16List.fromList(constant.value.codeUnits)
.buffer
.asUint8List();
@@ -617,11 +618,11 @@ class ConstantCreator extends ConstantVisitor<ConstantInfo?> {
final tearOffConstantInfo = ensureConstant(tearOffConstant)!;
w.DefinedFunction makeDynamicCallEntry() {
final w.DefinedFunction function = m.addFunction(
w.BaseFunction makeDynamicCallEntry() {
final function = m.functions.define(
translator.dynamicCallVtableEntryFunctionType, "dynamic call entry");
final w.Instructions b = function.body;
final b = function.body;
final closureLocal = function.locals[0];
final typeArgsListLocal = function.locals[1]; // empty
@@ -643,19 +644,19 @@ class ConstantCreator extends ConstantVisitor<ConstantInfo?> {
// Dynamic call entry needs to be created first (before `createConstant`)
// as it needs to create a constant for the type list, and we cannot create
// a constant while creating another one.
final w.DefinedFunction dynamicCallEntry = makeDynamicCallEntry();
final w.BaseFunction dynamicCallEntry = makeDynamicCallEntry();
return createConstant(constant, type, (function, b) {
ClassInfo info = translator.closureInfo;
translator.functions.allocateClass(info.classId);
w.DefinedFunction makeTrampoline(
w.FunctionType signature, w.DefinedFunction tearOffFunction) {
w.BaseFunction makeTrampoline(
w.FunctionType signature, w.BaseFunction tearOffFunction) {
assert(tearOffFunction.type.inputs.length ==
signature.inputs.length + types.length);
w.DefinedFunction function =
m.addFunction(signature, "instantiation constant trampoline");
w.Instructions b = function.body;
final function =
m.functions.define(signature, "instantiation constant trampoline");
final b = function.body;
b.local_get(function.locals[0]);
for (ConstantInfo typeInfo in types) {
b.global_get(typeInfo.global);
@@ -676,9 +677,9 @@ class ConstantCreator extends ConstantVisitor<ConstantInfo?> {
w.FunctionType signature =
representation.getVtableFieldType(fieldIndex);
w.DefinedFunction tearOffFunction = tearOffClosure.functions[
w.BaseFunction tearOffFunction = tearOffClosure.functions[
tearOffFieldIndex - tearOffClosure.representation.vtableBaseIndex];
w.DefinedFunction function =
w.BaseFunction function =
translator.globals.isDummyFunction(tearOffFunction)
? translator.globals.getDummyFunction(signature)
: makeTrampoline(signature, tearOffFunction);
+5 -5
View File
@@ -67,7 +67,7 @@ class SelectorInfo {
/// class member for this selector.
int? offset;
w.Module get m => translator.m;
w.ModuleBuilder get m => translator.m;
/// The selector's member's name.
String get name => paramInfo.member!.name.text;
@@ -173,7 +173,7 @@ class SelectorInfo {
}
List<w.ValueType> outputs = List.generate(outputSets.length,
(i) => _upperBound(outputSets[i], ensureBoxed: false));
return m.addFunctionType(
return m.types.defineFunction(
[inputs[0], ...typeParameters, ...inputs.sublist(1)], outputs);
}
@@ -237,9 +237,9 @@ class DispatchTable {
late final List<Reference?> _table;
/// The Wasm table for the dispatch table.
late final w.DefinedTable wasmTable;
late final w.TableBuilder wasmTable;
w.Module get m => translator.m;
w.ModuleBuilder get m => translator.m;
DispatchTable(this.translator)
: _selectorMetadata =
@@ -461,7 +461,7 @@ class DispatchTable {
}
}
wasmTable = m.addTable(w.RefType.func(nullable: true), _table.length);
wasmTable = m.tables.define(w.RefType.func(nullable: true), _table.length);
}
void output() {
+12 -12
View File
@@ -69,10 +69,10 @@ class Forwarder {
final String memberName;
final w.DefinedFunction function;
final w.FunctionBuilder function;
Forwarder(Translator translator, this.kind, this.memberName)
: function = translator.m.addFunction(
: function = translator.m.functions.define(
kind.functionType(translator), "$kind forwarder for '$memberName'");
void _generateCode(Translator translator) {
@@ -92,7 +92,7 @@ class Forwarder {
}
void _generateGetterCode(Translator translator) {
final w.Instructions b = function.body;
final b = function.body;
final receiverLocal = function.locals[0];
@@ -151,7 +151,7 @@ class Forwarder {
}
void _generateSetterCode(Translator translator) {
final w.Instructions b = function.body;
final b = function.body;
final receiverLocal = function.locals[0];
final positionalArgLocal = function.locals[1];
@@ -197,7 +197,7 @@ class Forwarder {
}
void _generateMethodCode(Translator translator) {
final w.Instructions b = function.body;
final b = function.body;
final receiverLocal = function.locals[0]; // ref #Top
final typeArgsLocal = function.locals[1]; // ref _ListBase
@@ -622,7 +622,7 @@ enum _ForwarderKind {
/// [noSuchMethodBlock] is used as the `br` target when the shape check fails.
void generateDynamicFunctionCall(
Translator translator,
w.DefinedFunction function,
w.FunctionBuilder function,
w.Local closureLocal,
w.Local typeArgsLocal,
w.Local posArgsLocal,
@@ -685,7 +685,7 @@ void generateDynamicFunctionCall(
void createInvocationObject(
Translator translator,
w.DefinedFunction function,
w.FunctionBuilder function,
String memberName,
w.Local typeArgsLocal,
w.Local positionalArgsLocal,
@@ -709,7 +709,7 @@ void createInvocationObject(
void createGetterInvocationObject(
Translator translator,
w.DefinedFunction function,
w.FunctionBuilder function,
String memberName,
) {
final b = function.body;
@@ -726,7 +726,7 @@ void createGetterInvocationObject(
void createSetterInvocationObject(
Translator translator,
w.DefinedFunction function,
w.FunctionBuilder function,
String memberName,
w.Local positionalArgLocal,
) {
@@ -747,7 +747,7 @@ void createSetterInvocationObject(
void generateNoSuchMethodCall(
Translator translator,
w.DefinedFunction function,
w.FunctionBuilder function,
void Function() pushReceiver,
void Function() pushInvocationObject,
) {
@@ -810,8 +810,8 @@ void generateNoSuchMethodCall(
}
void _makeEmptyGrowableList(
Translator translator, w.DefinedFunction function, int capacity) {
final w.Instructions b = function.body;
Translator translator, w.FunctionBuilder function, int capacity) {
final b = function.body;
Class cls = translator.growableListClass;
ClassInfo info = translator.classInfo[cls]!;
translator.functions.allocateClass(info.classId);
+11 -11
View File
@@ -30,7 +30,7 @@ class FunctionCollector {
FunctionCollector(this.translator);
w.Module get m => translator.m;
w.ModuleBuilder get m => translator.m;
void collectImportsAndExports() {
for (Library library in translator.libraries) {
@@ -58,13 +58,13 @@ class FunctionCollector {
// Define the function type in a singular recursion group to enable it
// to be unified with function types defined in FFI modules or using
// `WebAssembly.Function`.
m.splitRecursionGroup();
m.types.splitRecursionGroup();
w.FunctionType ftype = _makeFunctionType(
translator, member.reference, member.function.returnType, null,
isImportOrExport: true);
m.splitRecursionGroup();
m.types.splitRecursionGroup();
_functions[member.reference] =
m.importFunction(module, name, ftype, "$importName (import)");
m.functions.import(module, name, ftype, "$importName (import)");
}
}
}
@@ -77,11 +77,11 @@ class FunctionCollector {
// since Binaryen's `--closed-world` optimization mode requires all
// publicly exposed types to be defined in separate recursion groups
// from GC types.
m.splitRecursionGroup();
m.types.splitRecursionGroup();
_makeFunctionType(
translator, member.reference, member.function.returnType, null,
isImportOrExport: true);
m.splitRecursionGroup();
m.types.splitRecursionGroup();
}
addExport(member.reference, exportName);
}
@@ -105,13 +105,13 @@ class FunctionCollector {
w.FunctionType ftype = _makeFunctionType(
translator, target, node.function.returnType, null,
isImportOrExport: true);
w.DefinedFunction function = m.addFunction(ftype, "$node");
w.BaseFunction function = m.functions.define(ftype, "$node");
_functions[target] = function;
m.exportFunction(export.value, function);
m.exports.export(export.value, function);
} else if (node is Field) {
w.Table? table = translator.getTable(node);
if (table != null) {
m.exportTable(export.value, table);
m.exports.export(export.value, table);
}
}
}
@@ -129,7 +129,7 @@ class FunctionCollector {
w.BaseFunction getFunction(Reference target) {
return _functions.putIfAbsent(target, () {
_worklist.add(target);
return _getFunctionTypeAndName(target, m.addFunction);
return _getFunctionTypeAndName(target, m.functions.define);
});
}
@@ -279,5 +279,5 @@ w.FunctionType _makeFunctionType(Translator translator, Reference target,
final List<w.ValueType> outputs =
emptyOutputList ? const [] : [translateType(returnType)];
return translator.m.addFunctionType(inputs, outputs);
return translator.m.types.defineFunction(inputs, outputs);
}
+29 -27
View File
@@ -15,11 +15,11 @@ class Globals {
final Map<Field, w.Global> _globals = {};
final Map<Field, w.BaseFunction> _globalInitializers = {};
final Map<Field, w.Global> _globalInitializedFlag = {};
final Map<w.FunctionType, w.DefinedFunction> _dummyFunctions = {};
final Map<w.HeapType, w.DefinedGlobal> _dummyValues = {};
late final w.DefinedGlobal dummyStructGlobal;
final Map<w.FunctionType, w.BaseFunction> _dummyFunctions = {};
final Map<w.HeapType, w.Global> _dummyValues = {};
late final w.Global dummyStructGlobal;
w.Module get m => translator.m;
w.ModuleBuilder get m => translator.m;
Globals(this.translator) {
_initDummyValues();
@@ -27,23 +27,24 @@ class Globals {
void _initDummyValues() {
// Create dummy struct for anyref/eqref/structref dummy values
w.StructType structType = m.addStructType("#DummyStruct");
dummyStructGlobal = m.addGlobal(
w.StructType structType = m.types.defineStruct("#DummyStruct");
final dummyStructGlobalInit = m.globals.define(
w.GlobalType(w.RefType.struct(nullable: false), mutable: false));
w.Instructions ib = dummyStructGlobal.initializer;
final ib = dummyStructGlobalInit.initializer;
ib.struct_new(structType);
ib.end();
_dummyValues[w.HeapType.any] = dummyStructGlobal;
_dummyValues[w.HeapType.eq] = dummyStructGlobal;
_dummyValues[w.HeapType.struct] = dummyStructGlobal;
_dummyValues[w.HeapType.any] = dummyStructGlobalInit;
_dummyValues[w.HeapType.eq] = dummyStructGlobalInit;
_dummyValues[w.HeapType.struct] = dummyStructGlobalInit;
dummyStructGlobal = dummyStructGlobalInit;
}
/// Provide a dummy function with the given signature. Used for empty entries
/// in vtables and for dummy values of function reference type.
w.DefinedFunction getDummyFunction(w.FunctionType type) {
w.BaseFunction getDummyFunction(w.FunctionType type) {
return _dummyFunctions.putIfAbsent(type, () {
w.DefinedFunction function = m.addFunction(type, "#dummy function $type");
w.Instructions b = function.body;
final function = m.functions.define(type, "#dummy function $type");
final b = function.body;
b.unreachable();
b.end();
return function;
@@ -58,28 +59,29 @@ class Globals {
w.Global? _prepareDummyValue(w.ValueType type) {
if (type is w.RefType && !type.nullable) {
w.HeapType heapType = type.heapType;
w.DefinedGlobal? global = _dummyValues[heapType];
if (global != null) return global;
w.Global? foundGlobal = _dummyValues[heapType];
if (foundGlobal != null) return foundGlobal;
w.GlobalBuilder? global;
if (heapType is w.DefType) {
if (heapType is w.StructType) {
for (w.FieldType field in heapType.fields) {
_prepareDummyValue(field.type.unpacked);
}
global = m.addGlobal(w.GlobalType(type, mutable: false));
w.Instructions ib = global.initializer;
global = m.globals.define(w.GlobalType(type, mutable: false));
final ib = global.initializer;
for (w.FieldType field in heapType.fields) {
instantiateDummyValue(ib, field.type.unpacked);
}
ib.struct_new(heapType);
ib.end();
} else if (heapType is w.ArrayType) {
global = m.addGlobal(w.GlobalType(type, mutable: false));
w.Instructions ib = global.initializer;
global = m.globals.define(w.GlobalType(type, mutable: false));
final ib = global.initializer;
ib.array_new_fixed(heapType, 0);
ib.end();
} else if (heapType is w.FunctionType) {
global = m.addGlobal(w.GlobalType(type, mutable: false));
w.Instructions ib = global.initializer;
global = m.globals.define(w.GlobalType(type, mutable: false));
final ib = global.initializer;
ib.ref_func(getDummyFunction(heapType));
ib.end();
}
@@ -94,7 +96,7 @@ class Globals {
/// Produce a dummy value of any Wasm type. For non-nullable reference types,
/// the value is constructed in a global initializer, and the instantiation
/// of the value merely reads the global.
void instantiateDummyValue(w.Instructions b, w.ValueType type) {
void instantiateDummyValue(w.InstructionsBuilder b, w.ValueType type) {
switch (type) {
case w.NumType.i32:
b.i32_const(0);
@@ -144,8 +146,8 @@ class Globals {
if (init != null &&
!(translator.constants.ensureConstant(init)?.isLazy ?? false)) {
// Initialized to a constant
w.DefinedGlobal global =
m.addGlobal(w.GlobalType(type, mutable: !variable.isFinal));
final global =
m.globals.define(w.GlobalType(type, mutable: !variable.isFinal));
translator.constants
.instantiateConstant(null, global.initializer, init, type);
global.initializer.end();
@@ -156,13 +158,13 @@ class Globals {
type = type.withNullability(true);
} else {
// Explicit initialization flag
w.DefinedGlobal flag = m.addGlobal(w.GlobalType(w.NumType.i32));
final flag = m.globals.define(w.GlobalType(w.NumType.i32));
flag.initializer.i32_const(0);
flag.initializer.end();
_globalInitializedFlag[variable] = flag;
}
w.DefinedGlobal global = m.addGlobal(w.GlobalType(type));
final global = m.globals.define(w.GlobalType(type));
instantiateDummyValue(global.initializer, type);
global.initializer.end();
@@ -182,7 +184,7 @@ class Globals {
}
/// Emit code to read a static field.
w.ValueType readGlobal(w.Instructions b, Field variable) {
w.ValueType readGlobal(w.InstructionsBuilder b, Field variable) {
w.Global global = getGlobal(variable);
w.BaseFunction? initFunction = _globalInitializers[variable];
if (initFunction == null) {
+3 -3
View File
@@ -12,7 +12,7 @@ import 'package:kernel/ast.dart';
import 'package:wasm_builder/wasm_builder.dart' as w;
import 'abi.dart' show kWasmAbiEnumIndex;
typedef CodeGenCallback = void Function(w.Instructions);
typedef CodeGenCallback = void Function(w.InstructionsBuilder);
/// Specialized code generation for external members.
///
@@ -133,7 +133,7 @@ class Intrinsifier {
};
Translator get translator => codeGen.translator;
w.Instructions get b => codeGen.b;
w.InstructionsBuilder get b => codeGen.b;
DartType dartTypeOf(Expression exp) => codeGen.dartTypeOf(exp);
@@ -1101,7 +1101,7 @@ class Intrinsifier {
return null;
}
bool generateMemberIntrinsic(Reference target, w.DefinedFunction function,
bool generateMemberIntrinsic(Reference target, w.FunctionBuilder function,
List<w.Local> paramLocals, w.Label? returnLabel) {
Member member = target.asMember;
if (member is! Procedure) return false;
+6 -6
View File
@@ -214,7 +214,7 @@ class SyncStarCodeGenerator extends CodeGenerator {
}
@override
w.DefinedFunction generateLambda(Lambda lambda, Closures closures) {
w.BaseFunction generateLambda(Lambda lambda, Closures closures) {
this.closures = closures;
setupLambdaParametersAndContexts(lambda);
generateBodies(lambda.functionNode);
@@ -236,8 +236,8 @@ class SyncStarCodeGenerator extends CodeGenerator {
}
// Wasm function containing the body of the `sync*` function.
final w.DefinedFunction resumeFun = m.addFunction(
m.addFunctionType([
final resumeFun = m.functions.define(
m.types.defineFunction([
suspendStateInfo.nonNullableType,
translator.topInfo.nullableType,
translator.stackTraceInfo.nullableType
@@ -259,8 +259,8 @@ class SyncStarCodeGenerator extends CodeGenerator {
generateInner(functionNode, context, resumeFun);
}
void generateOuter(FunctionNode functionNode, Context? context,
w.DefinedFunction resumeFun) {
void generateOuter(
FunctionNode functionNode, Context? context, w.BaseFunction resumeFun) {
// Instantiate a [_SyncStarIterable] containing the context and resume
// function for this `sync*` function.
DartType returnType = functionNode.returnType;
@@ -327,7 +327,7 @@ class SyncStarCodeGenerator extends CodeGenerator {
}
void generateInner(FunctionNode functionNode, Context? context,
w.DefinedFunction resumeFun) {
w.FunctionBuilder resumeFun) {
// Set the current Wasm function for the code generator to the inner
// function of the `sync*`, which is to contain the body.
function = resumeFun;
+63 -59
View File
@@ -2,8 +2,6 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'dart:typed_data';
import 'package:dart2wasm/class_info.dart';
import 'package:dart2wasm/closures.dart';
import 'package:dart2wasm/code_generator.dart';
@@ -90,18 +88,18 @@ class Translator with KernelNodes {
final Map<Field, int> fieldIndex = {};
final Map<TypeParameter, int> typeParameterIndex = {};
final Map<Reference, ParameterInfo> staticParamInfo = {};
final Map<Field, w.DefinedTable> declaredTables = {};
final Map<Field, w.Table> declaredTables = {};
final Set<Member> membersContainingInnerFunctions = {};
final Set<Member> membersBeingGenerated = {};
final List<_FunctionGenerator> _pendingFunctions = [];
late final Procedure mainFunction;
late final w.Module m;
late final w.DefinedFunction initFunction;
late final w.ModuleBuilder m;
late final w.FunctionBuilder initFunction;
late final w.ValueType voidMarker;
// Lazily create exception tag if used.
late final w.Tag exceptionTag = createExceptionTag();
// Lazily import FFI memory if used.
late final w.Memory ffiMemory = m.importMemory("ffi", "memory",
late final w.Memory ffiMemory = m.memories.import("ffi", "memory",
options.importSharedMemory, 0, options.sharedMemoryMaxPages);
/// Maps record shapes to the record class for the shape. Classes generated
@@ -110,7 +108,7 @@ class Translator with KernelNodes {
// Caches for when identical source constructs need a common representation.
final Map<w.StorageType, w.ArrayType> arrayTypeCache = {};
final Map<w.BaseFunction, w.DefinedGlobal> functionRefCache = {};
final Map<w.BaseFunction, w.Global> functionRefCache = {};
final Map<Procedure, ClosureImplementation> tearOffFunctionCache = {};
// Some convenience accessors for commonly used values.
@@ -169,7 +167,7 @@ class Translator with KernelNodes {
/// Type for vtable entries for dynamic calls. These entries are used in
/// dynamic invocations and `Function.apply`.
late final w.FunctionType dynamicCallVtableEntryFunctionType =
m.addFunctionType([
m.types.defineFunction([
// Closure
w.RefType.def(closureLayouter.closureBaseStruct, nullable: false),
@@ -187,7 +185,7 @@ class Translator with KernelNodes {
/// Type of a dynamic invocation forwarder function.
late final w.FunctionType dynamicInvocationForwarderFunctionType =
m.addFunctionType([
m.types.defineFunction([
// Receiver
topInfo.nonNullableType,
@@ -205,7 +203,7 @@ class Translator with KernelNodes {
/// Type of a dynamic get forwarder function.
late final w.FunctionType dynamicGetForwarderFunctionType =
m.addFunctionType([
m.types.defineFunction([
// Receiver
topInfo.nonNullableType,
], [
@@ -214,7 +212,7 @@ class Translator with KernelNodes {
/// Type of a dynamic set forwarder function.
late final w.FunctionType dynamicSetForwarderFunctionType =
m.addFunctionType([
m.types.defineFunction([
// Receiver
topInfo.nonNullableType,
@@ -260,8 +258,8 @@ class Translator with KernelNodes {
'Entry uri ${entryLibrary.fileUri} has no main method.');
}
Uint8List translate() {
m = w.Module(watchPoints: options.watchPoints);
w.Module translate() {
m = w.ModuleBuilder(watchPoints: options.watchPoints);
voidMarker = w.RefType.def(w.StructType("void"), nullable: true);
mainFunction = _findMainMethod(libraries.first);
@@ -273,22 +271,21 @@ class Translator with KernelNodes {
classInfoCollector.collect();
initFunction =
m.addFunction(m.addFunctionType(const [], const []), "#init");
m.startFunction = initFunction;
m.functions.define(m.types.defineFunction(const [], const []), "#init");
m.functions.start = initFunction;
globals = Globals(this);
constants = Constants(this);
dispatchTable.build();
m.exportFunction("\$getMain", generateGetMain(mainFunction));
m.exports.export("\$getMain", generateGetMain(mainFunction));
functions.initialize();
while (!functions.isWorkListEmpty()) {
Reference reference = functions.popWorkList();
Member member = reference.asMember;
var function =
functions.getExistingFunction(reference) as w.DefinedFunction;
var function = functions.getExistingFunction(reference) as w.BaseFunction;
String canonicalName = "$member";
if (reference.isSetter) {
@@ -338,11 +335,11 @@ class Translator with KernelNodes {
}
if (options.exportAll && exportName == null) {
m.exportFunction(canonicalName, function);
m.exports.export(canonicalName, function);
}
final CodeGenerator codeGen =
CodeGenerator.forFunction(this, member.function, function, reference);
final CodeGenerator codeGen = CodeGenerator.forFunction(
this, member.function, function as w.FunctionBuilder, reference);
codeGen.generate();
if (options.printWasm) {
@@ -351,7 +348,7 @@ class Translator with KernelNodes {
}
for (Lambda lambda in codeGen.closures.lambdas.values) {
w.DefinedFunction lambdaFunction = CodeGenerator.forFunction(
w.BaseFunction lambdaFunction = CodeGenerator.forFunction(
this, lambda.functionNode, lambda.function, reference)
.generateLambda(lambda, codeGen.closures);
_printFunction(lambdaFunction, "$canonicalName (closure)");
@@ -369,31 +366,37 @@ class Translator with KernelNodes {
initFunction.body.end();
for (ConstantInfo info in constants.constantInfo.values) {
w.DefinedFunction? function = info.function;
w.BaseFunction? function = info.function;
if (function != null) {
_printFunction(function, info.constant);
} else {
if (options.printWasm) {
print("Global #${info.global.index}: ${info.constant}");
print(info.global.initializer.trace);
final global = info.global;
if (global is w.GlobalBuilder) {
print(global.initializer.trace);
}
}
}
}
_printFunction(initFunction, "init");
return m.encode(emitNameSection: options.nameSection);
return m.build();
}
void _printFunction(w.DefinedFunction function, Object name) {
void _printFunction(w.BaseFunction function, Object name) {
if (options.printWasm) {
print("#${function.index}: $name");
print(function.body.trace);
final f = function;
if (f is w.FunctionBuilder) {
print(f.body.trace);
}
}
}
w.DefinedFunction generateGetMain(Procedure mainFunction) {
w.DefinedFunction getMain = m.addFunction(
m.addFunctionType(const [], const [w.RefType.any(nullable: true)]));
w.BaseFunction generateGetMain(Procedure mainFunction) {
final getMain = m.functions.define(m.types
.defineFunction(const [], const [w.RefType.any(nullable: true)]));
constants.instantiateConstant(getMain, getMain.body,
StaticTearOffConstant(mainFunction), getMain.type.outputs.single);
getMain.body.end();
@@ -452,9 +455,9 @@ class Translator with KernelNodes {
/// [stackTraceInfo.nonNullableType] to hold a stack trace. This single
/// exception tag is used to throw and catch all Dart exceptions.
w.Tag createExceptionTag() {
w.FunctionType tagType = m.addFunctionType(
w.FunctionType tagType = m.types.defineFunction(
[topInfo.nonNullableType, stackTraceInfo.nonNullableType], const []);
w.Tag tag = m.addTag(tagType);
w.Tag tag = m.tags.define(tagType);
return tag;
}
@@ -517,7 +520,7 @@ class Translator with KernelNodes {
List<w.ValueType> outputs = [
if (!voidReturn) translateType(functionType.returnType)
];
w.FunctionType wasmType = m.addFunctionType(inputs, outputs);
w.FunctionType wasmType = m.types.defineFunction(inputs, outputs);
return w.RefType.def(wasmType, nullable: nullable);
}
@@ -594,7 +597,7 @@ class Translator with KernelNodes {
{bool mutable = true}) {
return arrayTypeCache.putIfAbsent(
type,
() => m.addArrayType("Array<$name>",
() => m.types.defineArray("Array<$name>",
elementType: w.FieldType(type, mutable: mutable)));
}
@@ -629,9 +632,9 @@ class Translator with KernelNodes {
return w.RefType.any(nullable: true);
}
w.DefinedGlobal makeFunctionRef(w.BaseFunction f) {
w.Global makeFunctionRef(w.BaseFunction f) {
return functionRefCache.putIfAbsent(f, () {
w.DefinedGlobal global = m.addGlobal(
final global = m.globals.define(
w.GlobalType(w.RefType.def(f.type, nullable: false), mutable: false));
global.initializer.ref_func(f);
global.initializer.end();
@@ -678,7 +681,7 @@ class Translator with KernelNodes {
(1 + positionalCount) +
representation.nameCombinations.length);
List<w.DefinedFunction> functions = [];
List<w.BaseFunction> functions = [];
bool canBeCalledWith(int posArgCount, List<String> argNames) {
if (posArgCount < functionNode.requiredParameterCount) {
@@ -724,9 +727,9 @@ class Translator with KernelNodes {
return true;
}
w.DefinedFunction makeTrampoline(
w.BaseFunction makeTrampoline(
w.FunctionType signature, int posArgCount, List<String> argNames) {
w.DefinedFunction trampoline = m.addFunction(signature, name);
final trampoline = m.functions.define(signature, name);
// Defer generation of the trampoline body to avoid cyclic dependency
// when a tear-off constant is used as default value in the torn-off
@@ -737,8 +740,8 @@ class Translator with KernelNodes {
return trampoline;
}
w.DefinedFunction makeDynamicCallEntry() {
final w.DefinedFunction function = m.addFunction(
w.BaseFunction makeDynamicCallEntry() {
final function = m.functions.define(
dynamicCallVtableEntryFunctionType, "$name dynamic call entry");
// Defer generation of the trampoline body to avoid cyclic dependency
@@ -751,22 +754,22 @@ class Translator with KernelNodes {
}
void fillVtableEntry(
w.Instructions ib, int posArgCount, List<String> argNames) {
w.InstructionsBuilder ib, int posArgCount, List<String> argNames) {
int fieldIndex = representation.vtableBaseIndex + functions.length;
assert(fieldIndex ==
representation.fieldIndexForSignature(posArgCount, argNames));
w.FunctionType signature = representation.getVtableFieldType(fieldIndex);
w.DefinedFunction function = canBeCalledWith(posArgCount, argNames)
w.BaseFunction function = canBeCalledWith(posArgCount, argNames)
? makeTrampoline(signature, posArgCount, argNames)
: globals.getDummyFunction(signature);
functions.add(function);
ib.ref_func(function);
}
w.DefinedGlobal vtable = m.addGlobal(w.GlobalType(
final vtable = m.globals.define(w.GlobalType(
w.RefType.def(representation.vtableStruct, nullable: false),
mutable: false));
w.Instructions ib = vtable.initializer;
final ib = vtable.initializer;
final dynamicCallEntry = makeDynamicCallEntry();
ib.ref_func(dynamicCallEntry);
if (representation.isGeneric) {
@@ -795,8 +798,8 @@ class Translator with KernelNodes {
}
void convertType(
w.DefinedFunction function, w.ValueType from, w.ValueType to) {
w.Instructions b = function.body;
w.FunctionBuilder function, w.ValueType from, w.ValueType to) {
final b = function.body;
if (from == voidMarker || to == voidMarker) {
if (from != voidMarker) {
b.drop();
@@ -869,8 +872,8 @@ class Translator with KernelNodes {
/// This function participates in tree shaking in the sense that if it's
/// never called for a particular table declaration, that table is not added
/// to the output module.
w.DefinedTable? getTable(Field field) {
w.DefinedTable? table = declaredTables[field];
w.Table? getTable(Field field) {
w.Table? table = declaredTables[field];
if (table != null) return table;
DartType fieldType = field.type;
if (fieldType is InterfaceType && fieldType.classNode == wasmTableClass) {
@@ -886,7 +889,7 @@ class Translator with KernelNodes {
int size = sizeExp is ConstantExpression
? (sizeExp.constant as IntConstant).value
: (sizeExp as IntLiteral).value;
return declaredTables[field] = m.addTable(elementType, size);
return declaredTables[field] = m.tables.define(elementType, size);
}
return null;
}
@@ -935,12 +938,12 @@ class Translator with KernelNodes {
}
w.ValueType makeList(
w.DefinedFunction function,
void generateType(w.Instructions b),
w.FunctionBuilder function,
void generateType(w.InstructionsBuilder b),
int length,
void Function(w.ValueType, int) generateItem,
{bool isGrowable = false}) {
final w.Instructions b = function.body;
final b = function.body;
final Class cls = isGrowable ? growableListClass : fixedLengthListClass;
final ClassInfo info = classInfo[cls]!;
@@ -980,7 +983,8 @@ class Translator with KernelNodes {
}
/// Indexes a Dart `List` on the stack.
void indexList(w.Instructions b, void pushIndex(w.Instructions b)) {
void indexList(
w.InstructionsBuilder b, void pushIndex(w.InstructionsBuilder b)) {
ClassInfo info = classInfo[listBaseClass]!;
w.ArrayType arrayType =
(info.struct.fields[FieldIndex.listArray].type as w.RefType).heapType
@@ -991,7 +995,7 @@ class Translator with KernelNodes {
}
/// Pushes a Dart `List`'s length onto the stack as `i32`.
void getListLength(w.Instructions b) {
void getListLength(w.InstructionsBuilder b) {
ClassInfo info = classInfo[listBaseClass]!;
b.struct_get(info.struct, FieldIndex.listLength);
b.i32_wrap_i64();
@@ -1006,7 +1010,7 @@ abstract class _FunctionGenerator {
}
class _ClosureTrampolineGenerator implements _FunctionGenerator {
final w.DefinedFunction trampoline;
final w.FunctionBuilder trampoline;
final w.BaseFunction target;
final int typeCount;
final int posArgCount;
@@ -1024,7 +1028,7 @@ class _ClosureTrampolineGenerator implements _FunctionGenerator {
this.takesContextOrReceiver);
void generate(Translator translator) {
w.Instructions b = trampoline.body;
final b = trampoline.body;
int targetIndex = 0;
if (takesContextOrReceiver) {
w.Local receiver = trampoline.locals[0];
@@ -1083,13 +1087,13 @@ class _ClosureDynamicEntryGenerator implements _FunctionGenerator {
final w.BaseFunction target;
final ParameterInfo paramInfo;
final String name;
final w.DefinedFunction function;
final w.FunctionBuilder function;
_ClosureDynamicEntryGenerator(
this.functionNode, this.target, this.paramInfo, this.name, this.function);
void generate(Translator translator) {
final w.Instructions b = function.body;
final b = function.body;
final bool takesContextOrReceiver =
paramInfo.member == null || paramInfo.member!.isInstanceMember;
+12 -12
View File
@@ -202,7 +202,7 @@ class Types {
/// implement.
/// TODO(joshualitt): This implementation is just temporary. Eventually we
/// should move to a data structure more closely resembling [typeRules].
w.ValueType makeTypeRulesSupers(w.Instructions b) {
w.ValueType makeTypeRulesSupers(w.InstructionsBuilder b) {
w.ValueType expectedType =
translator.classInfo[translator.immutableListClass]!.nonNullableType;
DartType listIntType = InterfaceType(translator.immutableListClass,
@@ -222,7 +222,7 @@ class Types {
/// Similar to the above, but provides the substitutions required for each
/// supertype.
/// TODO(joshualitt): Like [makeTypeRulesSupers], this is just temporary.
w.ValueType makeTypeRulesSubstitutions(w.Instructions b) {
w.ValueType makeTypeRulesSubstitutions(w.InstructionsBuilder b) {
w.ValueType expectedType =
translator.classInfo[translator.immutableListClass]!.nonNullableType;
DartType listTypeType = InterfaceType(
@@ -252,7 +252,7 @@ class Types {
}
/// Returns a list of string type names for pretty printing types.
w.ValueType makeTypeNames(w.Instructions b) {
w.ValueType makeTypeNames(w.InstructionsBuilder b) {
w.ValueType expectedType =
translator.classInfo[translator.immutableListClass]!.nonNullableType;
List<StringConstant> listStringConstant = [];
@@ -295,17 +295,17 @@ class Types {
table[i] = category;
}
w.DataSegment segment = translator.m.addDataSegment(table);
final segment = translator.m.dataSegments.define(table);
w.ArrayType arrayType =
translator.wasmArrayType(w.PackedType.i8, "const i8", mutable: false);
w.DefinedGlobal global = translator.m
.addGlobal(w.GlobalType(w.RefType.def(arrayType, nullable: false)));
final global = translator.m.globals
.define(w.GlobalType(w.RefType.def(arrayType, nullable: false)));
// Initialize the global to a dummy array, since `array.new_data` is not
// a constant instruction and thus can't be used in the initializer.
global.initializer.array_new_fixed(arrayType, 0);
global.initializer.end();
// Create the actual table in the init function.
w.Instructions b = translator.initFunction.body;
final b = translator.initFunction.body;
b.i32_const(0);
b.i32_const(table.length);
b.array_new_data(arrayType, segment);
@@ -385,7 +385,7 @@ class Types {
}
void _makeInterfaceType(CodeGenerator codeGen, InterfaceType type) {
w.Instructions b = codeGen.b;
final b = codeGen.b;
ClassInfo typeInfo = translator.classInfo[type.classNode]!;
b.i32_const(encodedNullability(type));
b.i64_const(typeInfo.classId);
@@ -435,7 +435,7 @@ class Types {
}
void _makeFutureOrType(CodeGenerator codeGen, FutureOrType type) {
w.Instructions b = codeGen.b;
final b = codeGen.b;
b.i32_const(encodedNullability(type));
makeType(codeGen, type.typeArgument);
codeGen.call(translator.createNormalizedFutureOrType.reference);
@@ -443,7 +443,7 @@ class Types {
void _makeFunctionType(CodeGenerator codeGen, FunctionType type) {
int typeParameterOffset = computeFunctionTypeParameterOffset(type);
w.Instructions b = codeGen.b;
final b = codeGen.b;
b.i32_const(encodedNullability(type));
b.i64_const(typeParameterOffset);
@@ -500,7 +500,7 @@ class Types {
w.ValueType makeType(CodeGenerator codeGen, DartType type) {
// Always ensure type is normalized before making a type.
type = normalize(type);
w.Instructions b = codeGen.b;
final b = codeGen.b;
if (_isTypeConstant(type)) {
translator.constants.instantiateConstant(
codeGen.function, b, TypeLiteralConstant(type), nonNullableTypeType);
@@ -579,7 +579,7 @@ class Types {
/// TODO(joshualitt): Remove dependency on [CodeGenerator]
void emitTypeTest(
CodeGenerator codeGen, DartType type, DartType operandType) {
w.Instructions b = codeGen.b;
final b = codeGen.b;
if (type is! InterfaceType) {
makeType(codeGen, type);
codeGen.call(translator.isSubtype.reference);
@@ -0,0 +1,22 @@
// Copyright (c) 2023, 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.
export 'data_segments.dart' show DataSegmentBuilder, DataSegmentsBuilder;
export 'exports.dart' show ExportsBuilder;
export 'globals.dart' show GlobalsBuilder, GlobalBuilder;
export 'functions.dart' show FunctionsBuilder, FunctionBuilder;
export 'memories.dart' show MemoriesBuilder;
export 'module.dart' show ModuleBuilder;
export 'tables.dart' show TablesBuilder, TableBuilder;
export 'tags.dart' show TagsBuilder;
export 'types.dart' show TypesBuilder;
export 'instructions.dart' show InstructionsBuilder, Label, ValidationError;
mixin Builder<T> {
T? _built;
T build() => _built ??= forceBuild();
T forceBuild();
}
@@ -0,0 +1,31 @@
// Copyright (c) 2023, 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 'data_segments.dart';
/// A data segment builder in a module builder.
class DataSegmentBuilder extends ir.BaseDataSegment
with Builder<ir.DataSegment> {
final BytesBuilder content;
DataSegmentBuilder(
super.index, Uint8List initialContent, super.memory, super.offset)
: content = BytesBuilder()..add(initialContent);
bool get isActive => memory != null;
bool get isPassive => memory == null;
int get length => content.length;
/// Append content to the data segment.
void append(Uint8List data) {
content.add(data);
assert(isPassive ||
offset! >= 0 && offset! + content.length <= memory!.minSize);
}
@override
ir.DataSegment forceBuild() =>
ir.DataSegment(index, content.toBytes(), memory, offset);
}
@@ -0,0 +1,42 @@
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'dart:typed_data';
import '../ir/ir.dart' as ir;
import 'builder.dart';
part 'data_segment.dart';
/// The interface for building data segments in a module.
class DataSegmentsBuilder with Builder<ir.DataSegments> {
final _dataSegmentBuilders = <DataSegmentBuilder>[];
static const int memoryBlockSize = 0x10000;
/// Defines a new data segment in this module.
///
/// Either [memory] and [offset] must be both specified or both omitted. If
/// they are specified, the segment becomes an *active* segment, otherwise it
/// becomes a *passive* segment.
///
/// If [initialContent] is specified, it defines the initial content of the
/// segment. The content can be extended later.
DataSegmentBuilder define(
[Uint8List? initialContent, ir.Memory? memory, int? offset]) {
initialContent ??= Uint8List(0);
assert((memory != null) == (offset != null));
assert(memory == null ||
offset! >= 0 &&
offset + initialContent.length <= memory.minSize * memoryBlockSize);
final builder = DataSegmentBuilder(
_dataSegmentBuilders.length, initialContent, memory, offset);
_dataSegmentBuilders.add(builder);
return builder;
}
@override
ir.DataSegments forceBuild() =>
ir.DataSegments(_dataSegmentBuilders.map((b) => b.build()).toList());
}
@@ -0,0 +1,21 @@
// Copyright (c) 2023, 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 '../ir/ir.dart' as ir;
import 'builder.dart';
/// The interface for exports of this module.
class ExportsBuilder with Builder<ir.Exports> {
final _exports = <ir.Export>[];
/// Exports the provided [Exportable] under the provided name which must be
/// unique.
void export(String name, ir.Exportable exportable) {
assert(!_exports.any((e) => e.name == name), name);
_exports.add(exportable.export(name));
}
@override
ir.Exports forceBuild() => ir.Exports(_exports);
}
@@ -0,0 +1,33 @@
// Copyright (c) 2023, 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 'functions.dart';
/// A function defined in a module.
class FunctionBuilder extends ir.BaseFunction with Builder<ir.DefinedFunction> {
/// All local variables defined in the function, including its inputs.
List<ir.Local> get locals => body.locals;
/// The body of the function.
late final InstructionsBuilder body;
FunctionBuilder(ModuleBuilder module, super.index, super.type,
[super.functionName]) {
body = InstructionsBuilder(module, type.outputs);
for (ir.ValueType paramType in type.inputs) {
body.addLocal(paramType, isParameter: true);
}
}
/// Add a local variable to the function.
ir.Local addLocal(ir.ValueType type) =>
body.addLocal(type, isParameter: false);
@override
ir.DefinedFunction forceBuild() =>
ir.DefinedFunction(body.build(), index, type, functionName);
@override
String toString() => exportedName ?? "#$index";
}
@@ -0,0 +1,68 @@
// Copyright (c) 2023, 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 '../ir/ir.dart' as ir;
import 'builder.dart';
part 'function.dart';
/// The interface for the functions in a module.
class FunctionsBuilder with Builder<ir.Functions> {
final ModuleBuilder _module;
final _functions = <ir.BaseFunction>[];
final _functionBuilders = <FunctionBuilder>[];
final _importedFunctions = <ir.Import>[];
int _nameCount = 0;
bool _anyFunctionsDefined = false;
ir.BaseFunction? _start;
FunctionsBuilder(this._module);
/// This is guarded by [_anyFunctionsDefined].
int get _index => _importedFunctions.length + _functionBuilders.length;
set start(ir.BaseFunction init) {
assert(_start == null);
_start = init;
}
void _addName(String? name, ir.BaseFunction function) {
if (name != null) {
_nameCount++;
}
_functions.add(function);
}
/// Defines a new function in this module with the given function type.
///
/// The [DefinedFunction.body] must be completed (including the terminating
/// `end`) before the module can be serialized.
FunctionBuilder define(ir.FunctionType type, [String? name]) {
_anyFunctionsDefined = true;
final function = FunctionBuilder(_module, _index, type, name);
_functionBuilders.add(function);
_addName(name, function);
return function;
}
/// Import a function into the module.
///
/// All imported functions must be specified before any functions are declared
/// using [FunctionsBuilder.define].
ir.ImportedFunction import(String module, String name, ir.FunctionType type,
[String? functionName]) {
if (_anyFunctionsDefined) {
throw "All function imports must be specified before any definitions.";
}
final function =
ir.ImportedFunction(module, name, _index, type, functionName);
_importedFunctions.add(function);
_addName(functionName, function);
return function;
}
@override
ir.Functions forceBuild() => ir.Functions(_start, _importedFunctions,
_functionBuilders.map((f) => f.build()).toList(), _functions, _nameCount);
}
@@ -0,0 +1,18 @@
// Copyright (c) 2023, 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 'globals.dart';
/// A global variable defined in a module.
class GlobalBuilder extends ir.Global with Builder<ir.DefinedGlobal> {
final InstructionsBuilder initializer;
GlobalBuilder(ModuleBuilder module, super.index, super.type)
: initializer =
InstructionsBuilder(module, [type.type], isGlobalInitializer: true);
@override
ir.DefinedGlobal forceBuild() =>
ir.DefinedGlobal(initializer.build(), index, type);
}
@@ -0,0 +1,45 @@
// Copyright (c) 2023, 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 '../ir/ir.dart' as ir;
import 'builder.dart';
part 'global.dart';
class GlobalsBuilder with Builder<ir.Globals> {
final ModuleBuilder _module;
final _importedGlobals = <ir.Import>[];
final _globalBuilders = <GlobalBuilder>[];
bool _anyGlobalsDefined = false;
GlobalsBuilder(this._module);
/// This is guarded by [_anyGlobalsDefined].
int get _index => _importedGlobals.length + _globalBuilders.length;
/// Defines a new global variable in this module.
GlobalBuilder define(ir.GlobalType type) {
_anyGlobalsDefined = true;
final global = GlobalBuilder(_module, _index, type);
_globalBuilders.add(global);
return global;
}
/// Imports a global variable into this module.
///
/// All imported globals must be specified before any globals are declared
/// using [Globals.define].
ir.ImportedGlobal import(String module, String name, ir.GlobalType type) {
if (_anyGlobalsDefined) {
throw "All global imports must be specified before any definitions.";
}
final global = ir.ImportedGlobal(module, name, _index, type);
_importedGlobals.add(global);
return global;
}
@override
ir.Globals forceBuild() => ir.Globals(
_importedGlobals, _globalBuilders.map((g) => g.build()).toList());
}
@@ -0,0 +1,41 @@
// Copyright (c) 2023, 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 '../ir/ir.dart' as ir;
import 'builder.dart';
class MemoriesBuilder with Builder<ir.Memories> {
final _definedMemories = <ir.DefinedMemory>[];
final _importedMemories = <ir.Import>[];
bool _anyMemoriesDefined = false;
/// This is guarded by [_anyMemoriesDefined].
int get _index => _importedMemories.length + _definedMemories.length;
/// Add a new memory to the module.
ir.DefinedMemory define(bool shared, int minSize, [int? maxSize]) {
_anyMemoriesDefined = true;
final memory = ir.DefinedMemory(_index, shared, minSize, maxSize);
_definedMemories.add(memory);
return memory;
}
/// Imports a memory into this module.
///
/// All imported memories must be specified before any memories are declared
/// using [defined].
ir.ImportedMemory import(String module, String name, bool shared, int minSize,
[int? maxSize]) {
if (_anyMemoriesDefined) {
throw "All memory imports must be specified before any definitions.";
}
final memory =
ir.ImportedMemory(module, name, _index, shared, minSize, maxSize);
_importedMemories.add(memory);
return memory;
}
@override
ir.Memories forceBuild() => ir.Memories(_importedMemories, _definedMemories);
}
@@ -0,0 +1,53 @@
// Copyright (c) 2023, 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 '../ir/ir.dart' as ir;
import 'builder.dart';
// TODO(joshualitt): Get rid of cycles in the builder graph.
/// A Wasm module builder.
class ModuleBuilder with Builder<ir.Module> {
final List<int>? watchPoints;
final types = TypesBuilder();
late final functions = FunctionsBuilder(this);
final tables = TablesBuilder();
final memories = MemoriesBuilder();
final tags = TagsBuilder();
final dataSegments = DataSegmentsBuilder();
late final globals = GlobalsBuilder(this);
final exports = ExportsBuilder();
bool dataReferencedFromGlobalInitializer = false;
/// Create a new, initially empty, module.
///
/// The [watchPoints] is a list of byte offsets within the final module of
/// bytes to watch. When the module is serialized, the stack traces leading to
/// the production of all watched bytes are printed. This can be used to debug
/// runtime errors happening at specific offsets within the module.
ModuleBuilder({this.watchPoints});
@override
ir.Module forceBuild() {
final finalFunctions = functions.build();
final finalTables = tables.build();
final finalMemories = memories.build();
final finalGlobals = globals.build();
return ir.Module(
finalFunctions,
finalTables,
tags.build(),
finalMemories,
exports.build(),
finalGlobals,
types.build(),
dataSegments.build(),
finalFunctions.imported
.followedBy(finalTables.imported)
.followedBy(finalMemories.imported)
.followedBy(finalGlobals.imported)
.toList(),
watchPoints,
dataReferencedFromGlobalInitializer);
}
}
@@ -0,0 +1,23 @@
// Copyright (c) 2023, 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 'tables.dart';
/// A table defined in a module.
class TableBuilder extends ir.Table with Builder<ir.DefinedTable> {
final List<ir.BaseFunction?> elements;
TableBuilder(super.index, super.type, super.minSize, super.maxSize)
: elements = List.filled(minSize, null);
void setElement(int index, ir.BaseFunction function) {
assert(type == ir.RefType.func(nullable: true),
"Elements are only supported for funcref tables");
elements[index] = function;
}
@override
ir.DefinedTable forceBuild() =>
ir.DefinedTable(elements, index, type, minSize, maxSize);
}
@@ -0,0 +1,46 @@
// Copyright (c) 2023, 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 '../ir/ir.dart' as ir;
import 'builder.dart';
part 'table.dart';
/// The interface for the tables in a module.
class TablesBuilder with Builder<ir.Tables> {
final _tableBuilders = <TableBuilder>[];
final _importedTables = <ir.Import>[];
bool _anyTablesDefined = false;
/// This is guarded by [_anyTableDefined].
int get _index => _importedTables.length + _tableBuilders.length;
/// Defines a new table in this module.
TableBuilder define(ir.RefType type, int minSize, [int? maxSize]) {
_anyTablesDefined = true;
final table = TableBuilder(_index, type, minSize, maxSize);
_tableBuilders.add(table);
return table;
}
/// Imports a table into this module.
///
/// All imported tables must be specified before any tables are declared
/// using [Tables.define].
ir.ImportedTable import(
String module, String name, ir.RefType type, int minSize,
[int? maxSize]) {
if (_anyTablesDefined) {
throw "All table imports must be specified before any definitions.";
}
final table =
ir.ImportedTable(module, name, _index, type, minSize, maxSize);
_importedTables.add(table);
return table;
}
@override
ir.Tables forceBuild() =>
ir.Tables(_importedTables, _tableBuilders.map((t) => t.build()).toList());
}
@@ -0,0 +1,21 @@
// Copyright (c) 2023, 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 '../ir/ir.dart' as ir;
import 'builder.dart';
/// The interface for the tags in a module.
class TagsBuilder with Builder<ir.Tags> {
final List<ir.Tag> _tags = [];
/// Defines a new tag in the module.
ir.Tag define(ir.FunctionType type) {
final tag = ir.Tag(_tags.length, type);
_tags.add(tag);
return tag;
}
@override
ir.Tags forceBuild() => ir.Tags(_tags);
}
+112
View File
@@ -0,0 +1,112 @@
// Copyright (c) 2023, 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 '../ir/ir.dart' as ir;
import 'builder.dart';
class TypesBuilder with Builder<ir.Types> {
final _recursionGroupSplits = <int>[];
final _functionTypeMap = <_FunctionTypeKey, ir.FunctionType>{};
final _defTypes = <ir.DefType>[];
int _nameCount = 0;
/// Add a new function type to the module.
///
/// All function types are canonicalized, such that identical types become
/// the same type definition in the module, assuming nominal type identity
/// of all inputs and outputs.
///
/// Inputs and outputs can't be changed after the function type is created.
/// This means that recursive function types (without any non-function types
/// on the recursion path) are not supported.
ir.FunctionType defineFunction(
Iterable<ir.ValueType> inputs, Iterable<ir.ValueType> outputs,
{ir.DefType? superType}) {
final List<ir.ValueType> inputList = List.unmodifiable(inputs);
final List<ir.ValueType> outputList = List.unmodifiable(outputs);
final _FunctionTypeKey key = _FunctionTypeKey(inputList, outputList);
return _functionTypeMap.putIfAbsent(key, () {
final type = ir.FunctionType(inputList, outputList, superType: superType)
..index = _defTypes.length;
_defTypes.add(type);
return type;
});
}
/// Add a new struct type to the module.
///
/// Fields can be added later, by adding to the [fields] list. This enables
/// struct types to be recursive.
ir.StructType defineStruct(String name,
{Iterable<ir.FieldType>? fields, ir.DefType? superType}) {
final type = ir.StructType(name, fields: fields, superType: superType)
..index = _defTypes.length;
_defTypes.add(type);
_nameCount++;
return type;
}
/// Add a new array type to the module.
///
/// The element type can be specified later. This enables array types to be
/// recursive.
ir.ArrayType defineArray(String name,
{ir.FieldType? elementType, ir.DefType? superType}) {
final type =
ir.ArrayType(name, elementType: elementType, superType: superType)
..index = _defTypes.length;
_defTypes.add(type);
_nameCount++;
return type;
}
/// Insert a recursion group split in the list of type definitions. Types can
/// only reference other types in the same or earlier recursion groups.
void splitRecursionGroup() {
int typeCount = _defTypes.length;
if (typeCount > 0 &&
(_recursionGroupSplits.isEmpty ||
_recursionGroupSplits.last != typeCount)) {
_recursionGroupSplits.add(typeCount);
}
}
@override
ir.Types forceBuild() =>
ir.Types(_defTypes, _recursionGroupSplits, _nameCount);
}
class _FunctionTypeKey {
final List<ir.ValueType> inputs;
final List<ir.ValueType> outputs;
_FunctionTypeKey(this.inputs, this.outputs);
@override
bool operator ==(Object other) {
if (other is! _FunctionTypeKey) return false;
if (inputs.length != other.inputs.length) return false;
if (outputs.length != other.outputs.length) return false;
for (int i = 0; i < inputs.length; i++) {
if (inputs[i] != other.inputs[i]) return false;
}
for (int i = 0; i < outputs.length; i++) {
if (outputs[i] != other.outputs[i]) return false;
}
return true;
}
@override
int get hashCode {
int inputHash = 13;
for (var input in inputs) {
inputHash = inputHash * 17 + input.hashCode;
}
int outputHash = 23;
for (var output in outputs) {
outputHash = outputHash * 29 + output.hashCode;
}
return (inputHash * 2 + 1) * (outputHash * 2 + 1);
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,41 @@
// Copyright (c) 2023, 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 'data_segments.dart';
class BaseDataSegment {
final int index;
final Memory? memory;
final int? offset;
BaseDataSegment(this.index, this.memory, this.offset);
}
/// A data segment in a module.
class DataSegment extends BaseDataSegment implements Serializable {
final Uint8List content;
DataSegment(super.index, this.content, super.memory, super.offset);
@override
void serialize(Serializer s) {
if (memory != null) {
// Active segment
if (memory!.index == 0) {
s.writeByte(0x00);
} else {
s.writeByte(0x02);
s.writeUnsigned(memory!.index);
}
s.writeByte(0x41); // i32.const
s.writeSigned(offset!);
s.writeByte(0x0B); // end
} else {
// Passive segment
s.writeByte(0x01);
}
s.writeUnsigned(content.length);
s.writeBytes(content);
}
}
@@ -0,0 +1,17 @@
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'dart:typed_data';
import '../serialize/serialize.dart';
import 'ir.dart';
part 'data_segment.dart';
class DataSegments {
/// Data segments defined in this module.
final List<DataSegment> defined;
DataSegments(this.defined);
}
+25
View File
@@ -0,0 +1,25 @@
// Copyright (c) 2023, 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 '../serialize/serialize.dart';
/// Any class which can be exported from a module.
abstract class Exportable {
/// All exports must have unique names.
Export export(String name);
}
/// Any export (function, table, memory or global).
abstract class Export implements Serializable {
final String name;
Export(this.name);
}
class Exports {
/// All exports from this module.
final List<Export> exported;
Exports(this.exported);
}
+108
View File
@@ -0,0 +1,108 @@
// Copyright (c) 2023, 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 'functions.dart';
/// A local variable defined in a function.
class Local {
final int index;
final ValueType type;
Local(this.index, this.type);
@override
String toString() => "$index";
}
/// An (imported or defined) function.
abstract class BaseFunction implements Exportable {
final int index;
final FunctionType type;
final String? functionName;
String? exportedName;
BaseFunction(this.index, this.type, this.functionName);
/// Creates an export of this function in this module.
@override
Export export(String name) {
assert(exportedName == null);
exportedName = name;
return FunctionExport(name, this);
}
}
/// A function defined in a module.
class DefinedFunction extends BaseFunction implements Serializable {
final Instructions body;
/// All local variables defined in the function, including its inputs.
List<Local> get locals => body.locals;
DefinedFunction(this.body, super.index, super.type, [super.functionName]);
@override
void serialize(Serializer s) {
// Serialize locals internally first in order to compute the total size of
// the serialized data.
final localS = Serializer();
int paramCount = type.inputs.length;
int entries = 0;
for (int i = paramCount + 1; i <= locals.length; i++) {
if (i == locals.length || locals[i - 1].type != locals[i].type) entries++;
}
localS.writeUnsigned(entries);
int start = paramCount;
for (int i = paramCount + 1; i <= locals.length; i++) {
if (i == locals.length || locals[i - 1].type != locals[i].type) {
localS.writeUnsigned(i - start);
localS.write(locals[i - 1].type);
start = i;
}
}
// Bundle locals and body
localS.write(body);
s.writeUnsigned(localS.data.length);
s.writeData(localS);
}
@override
String toString() => exportedName ?? "#$index";
}
/// An imported function.
class ImportedFunction extends BaseFunction implements Import {
@override
final String module;
@override
final String name;
ImportedFunction(this.module, this.name, super.index, super.type,
[super.functionName]);
@override
void serialize(Serializer s) {
s.writeName(module);
s.writeName(name);
s.writeByte(0x00);
s.writeUnsigned(type.index);
}
@override
String toString() => "$module.$name";
}
class FunctionExport extends Export {
final BaseFunction function;
FunctionExport(super.name, this.function);
@override
void serialize(Serializer s) {
s.writeName(name);
s.writeByte(0x00);
s.writeUnsigned(function.index);
}
}
@@ -0,0 +1,28 @@
// Copyright (c) 2023, 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 '../serialize/serialize.dart';
import 'ir.dart';
part 'function.dart';
/// The interface for the functions in a module.
class Functions {
/// The start function.
final BaseFunction? start;
/// Imported functions.
final List<Import> imported;
/// Defined functions.
final List<DefinedFunction> defined;
/// All functions, in the order they were emitted.
final List<BaseFunction> all;
/// Named functions.
final int namedCount;
Functions(this.start, this.imported, this.defined, this.all, this.namedCount);
}
+63
View File
@@ -0,0 +1,63 @@
// Copyright (c) 2023, 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 'globals.dart';
/// An (imported or defined) global variable.
abstract class Global implements Exportable {
final int index;
final GlobalType type;
Global(this.index, this.type);
@override
String toString() => "$index";
@override
Export export(String name) => GlobalExport(name, this);
}
/// A global variable defined in a module.
class DefinedGlobal extends Global implements Serializable {
final Instructions initializer;
DefinedGlobal(this.initializer, super.index, super.type);
@override
void serialize(Serializer s) {
s.write(type);
s.write(initializer);
}
}
/// An imported global variable.
class ImportedGlobal extends Global implements Import {
@override
final String module;
@override
final String name;
ImportedGlobal(this.module, this.name, super.index, super.type);
@override
void serialize(Serializer s) {
s.writeName(module);
s.writeName(name);
s.writeByte(0x03);
s.write(type);
}
}
class GlobalExport extends Export {
final Global global;
GlobalExport(super.name, this.global);
@override
void serialize(Serializer s) {
s.writeName(name);
s.writeByte(0x03);
s.writeUnsigned(global.index);
}
}
+18
View File
@@ -0,0 +1,18 @@
// Copyright (c) 2023, 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 '../serialize/serialize.dart';
import 'ir.dart';
part 'global.dart';
class Globals {
/// Imported globals.
final List<Import> imported;
/// Defined globals.
final List<DefinedGlobal> defined;
Globals(this.imported, this.defined);
}
+11
View File
@@ -0,0 +1,11 @@
// Copyright (c) 2023, 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 '../serialize/serialize.dart';
/// Any import (function, table, memory or global).
abstract class Import implements Serializable {
String get module;
String get name;
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,31 @@
// Copyright (c) 2023, 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 '../serialize/serialize.dart';
import 'ir.dart';
part 'instruction.dart';
class Instructions implements Serializable {
/// The locals used by this group of instructions.
final List<Local> locals;
/// A sequence of Wasm instructions.
final List<Instruction> instructions;
final List<String> _traceLines;
/// A string trace.
late final trace = _traceLines.join();
/// Create a new instruction sequence.
Instructions(this.locals, this.instructions, this._traceLines);
@override
void serialize(Serializer s) {
for (final i in instructions) {
i.serialize(s);
}
}
}
+34
View File
@@ -0,0 +1,34 @@
// Copyright (c) 2023, 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.
/// IR types are considered logically immutable.
// TODO(joshualitt): Make all of the ir types full immutable.
export 'data_segments.dart' show BaseDataSegment, DataSegment, DataSegments;
export 'exports.dart' show Export, Exportable, Exports;
export 'imports.dart' show Import;
export 'globals.dart' show DefinedGlobal, Global, Globals, ImportedGlobal;
export 'functions.dart'
show BaseFunction, DefinedFunction, Functions, ImportedFunction, Local;
export 'memories.dart' show DefinedMemory, ImportedMemory, Memories, Memory;
export 'module.dart' show Module;
export 'tables.dart' show DefinedTable, ImportedTable, Table, Tables;
export 'tags.dart' show Tag, Tags;
export 'types.dart'
show
ArrayType,
DataType,
DefType,
FieldType,
FunctionType,
GlobalType,
HeapType,
NumType,
PackedType,
RefType,
StorageType,
StructType,
Types,
ValueType;
export 'instructions.dart';
+18
View File
@@ -0,0 +1,18 @@
// Copyright (c) 2023, 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 '../serialize/serialize.dart';
import 'ir.dart';
part 'memory.dart';
class Memories {
/// Imported memories.
final List<Import> imported;
/// Defined memories.
final List<DefinedMemory> defined;
Memories(this.imported, this.defined);
}
+79
View File
@@ -0,0 +1,79 @@
// Copyright (c) 2023, 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 'memories.dart';
/// An (imported or defined) memory.
class Memory implements Exportable {
final int index;
final bool shared;
final int minSize;
final int? maxSize;
Memory(this.index, this.shared, this.minSize, [this.maxSize]) {
if (shared && maxSize == null) {
throw "Shared memory must specify a maximum size.";
}
}
void _serializeLimits(Serializer s) {
if (shared) {
assert(maxSize != null);
s.writeByte(0x03);
s.writeUnsigned(minSize);
s.writeUnsigned(maxSize!);
} else if (maxSize == null) {
s.writeByte(0x00);
s.writeUnsigned(minSize);
} else {
s.writeByte(0x01);
s.writeUnsigned(minSize);
s.writeUnsigned(maxSize!);
}
}
/// Export a memory from the module.
@override
Export export(String name) => MemoryExport(name, this);
}
/// A memory defined in a module.
class DefinedMemory extends Memory implements Serializable {
DefinedMemory(super.index, super.shared, super.minSize, super.maxSize);
@override
void serialize(Serializer s) => _serializeLimits(s);
}
/// An imported memory.
class ImportedMemory extends Memory implements Import {
@override
final String module;
@override
final String name;
ImportedMemory(this.module, this.name, super.index, super.shared,
super.minSize, super.maxSize);
@override
void serialize(Serializer s) {
s.writeName(module);
s.writeName(name);
s.writeByte(0x02);
_serializeLimits(s);
}
}
class MemoryExport extends Export {
final Memory memory;
MemoryExport(super.name, this.memory);
@override
void serialize(Serializer s) {
s.writeName(name);
s.writeByte(0x02);
s.writeUnsigned(memory.index);
}
}
+68
View File
@@ -0,0 +1,68 @@
// Copyright (c) 2023, 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 '../serialize/serialize.dart';
import 'ir.dart';
/// A logically const wasm module ready to encode. Created with `ModuleBuilder`.
class Module implements Serializable {
final Functions functions;
final Tables tables;
final Tags tags;
final Memories memories;
final Exports exports;
final Globals globals;
final Types types;
final DataSegments dataSegments;
final List<Import> imports;
final List<int>? watchPoints;
final bool dataReferencedFromGlobalInitializer;
Module(
this.functions,
this.tables,
this.tags,
this.memories,
this.exports,
this.globals,
this.types,
this.dataSegments,
this.imports,
this.watchPoints,
this.dataReferencedFromGlobalInitializer);
/// Serialize a module to its binary representation.
@override
void serialize(Serializer s) {
if (watchPoints != null) {
Serializer.traceEnabled = true;
}
// Wasm module preamble: magic number, version 1.
s.writeBytes(const [0x00, 0x61, 0x73, 0x6D, 0x01, 0x00, 0x00, 0x00]);
TypeSection(types, watchPoints).serialize(s);
ImportSection(imports, watchPoints).serialize(s);
FunctionSection(functions.defined, watchPoints).serialize(s);
TableSection(tables.defined, watchPoints).serialize(s);
MemorySection(memories.defined, watchPoints).serialize(s);
TagSection(tags.defined, watchPoints).serialize(s);
if (dataReferencedFromGlobalInitializer) {
DataCountSection(dataSegments.defined, watchPoints).serialize(s);
}
GlobalSection(globals.defined, watchPoints).serialize(s);
ExportSection(exports.exported, watchPoints).serialize(s);
StartSection(functions.start, watchPoints).serialize(s);
ElementSection(tables.defined, watchPoints).serialize(s);
if (!dataReferencedFromGlobalInitializer) {
DataCountSection(dataSegments.defined, watchPoints).serialize(s);
}
CodeSection(functions.defined, watchPoints).serialize(s);
DataSection(dataSegments.defined, watchPoints).serialize(s);
if (functions.namedCount > 0 || types.namedCount > 0) {
NameSection(functions.all, types.defined, watchPoints,
functionNameCount: functions.namedCount,
typeNameCount: types.namedCount)
.serialize(s);
}
}
}
+72
View File
@@ -0,0 +1,72 @@
// Copyright (c) 2023, 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 'tables.dart';
/// An (imported or defined) table.
class Table implements Exportable, Serializable {
final int index;
final RefType type;
final int minSize;
final int? maxSize;
Table(this.index, this.type, this.minSize, this.maxSize);
@override
void serialize(Serializer s) {
s.write(type);
if (maxSize == null) {
s.writeByte(0x00);
s.writeUnsigned(minSize);
} else {
s.writeByte(0x01);
s.writeUnsigned(minSize);
s.writeUnsigned(maxSize!);
}
}
/// Export a table from the module.
@override
Export export(String name) => TableExport(name, this);
}
/// A table defined in a module.
class DefinedTable extends Table {
final List<BaseFunction?> elements;
DefinedTable(
this.elements, super.index, super.type, super.minSize, super.maxSize);
}
/// An imported table.
class ImportedTable extends Table implements Import {
@override
final String module;
@override
final String name;
ImportedTable(this.module, this.name, super.index, super.type, super.minSize,
super.maxSize);
@override
void serialize(Serializer s) {
s.writeName(module);
s.writeName(name);
s.writeByte(0x01);
super.serialize(s);
}
}
class TableExport extends Export {
final Table table;
TableExport(super.name, this.table);
@override
void serialize(Serializer s) {
s.writeName(name);
s.writeByte(0x01);
s.writeUnsigned(table.index);
}
}
+18
View File
@@ -0,0 +1,18 @@
// Copyright (c) 2023, 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 '../serialize/serialize.dart';
import 'ir.dart';
part 'table.dart';
class Tables {
/// Imported tables.
final List<Import> imported;
/// Defined tables.
final List<DefinedTable> defined;
Tables(this.imported, this.defined);
}
+31
View File
@@ -0,0 +1,31 @@
// Copyright (c) 2023, 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 '../serialize/serialize.dart';
import 'ir.dart';
/// A tag in a module.
class Tag implements Serializable {
final int index;
final FunctionType type;
Tag(this.index, this.type);
@override
void serialize(Serializer s) {
// 0 byte for exception.
s.writeByte(0x00);
s.write(type);
}
@override
String toString() => "#$index";
}
class Tags {
/// All tags defined in this module.
final List<Tag> defined;
Tags(this.defined);
}
@@ -1,8 +1,8 @@
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
// Copyright (c) 2023, 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 'serialize.dart';
part of 'types.dart';
// Representations of all Wasm types.
+20
View File
@@ -0,0 +1,20 @@
// Copyright (c) 2023, 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 '../serialize/serialize.dart';
part 'type.dart';
class Types {
/// Types defined in this module.
final List<DefType> defined;
/// Recursion group splits.
final List<int> recursionGroupSplits;
/// Name count.
final int namedCount;
Types(this.defined, this.recursionGroupSplits, this.namedCount);
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,373 @@
// Copyright (c) 2023, 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 '../ir/ir.dart' as ir;
import 'serializer.dart';
abstract class Section implements Serializable {
final List<int>? watchPoints;
Section(this.watchPoints);
@override
void serialize(Serializer s) {
if (isNotEmpty) {
final contents = Serializer();
serializeContents(contents);
s.writeByte(id);
s.writeUnsigned(contents.data.length);
s.writeData(contents, watchPoints);
}
}
int get id;
bool get isNotEmpty;
void serializeContents(Serializer s);
}
class TypeSection extends Section {
final ir.Types types;
TypeSection(this.types, super.watchPoints);
List<ir.DefType> get defTypes => types.defined;
@override
int get id => 1;
@override
bool get isNotEmpty => defTypes.isNotEmpty;
@override
void serializeContents(Serializer s) {
s.writeUnsigned(types.recursionGroupSplits.length + 1);
int typeIndex = 0;
for (int split
in types.recursionGroupSplits.followedBy([defTypes.length])) {
s.writeByte(0x4F);
s.writeUnsigned(split - typeIndex);
for (; typeIndex < split; typeIndex++) {
ir.DefType defType = defTypes[typeIndex];
assert(defType.superType == null || defType.superType!.index < split,
"Type '$defType' has a supertype in a later recursion group");
assert(
defType.constituentTypes
.whereType<ir.RefType>()
.map((t) => t.heapType)
.whereType<ir.DefType>()
.every((d) => d.index < split),
"Type '$defType' depends on a type in a later recursion group");
defType.serializeDefinition(s);
}
}
}
}
class ImportSection extends Section {
final List<ir.Import> imports;
ImportSection(this.imports, super.watchPoints);
@override
int get id => 2;
@override
bool get isNotEmpty => imports.isNotEmpty;
@override
void serializeContents(Serializer s) {
s.writeList(imports);
}
}
class FunctionSection extends Section {
final List<ir.DefinedFunction> functions;
FunctionSection(this.functions, super.watchPoints);
@override
int get id => 3;
@override
bool get isNotEmpty => functions.isNotEmpty;
@override
void serializeContents(Serializer s) {
s.writeUnsigned(functions.length);
for (final function in functions) {
s.writeUnsigned(function.type.index);
}
}
}
class TableSection extends Section {
final List<ir.DefinedTable> tables;
TableSection(this.tables, super.watchPOints);
@override
int get id => 4;
@override
bool get isNotEmpty => tables.isNotEmpty;
@override
void serializeContents(Serializer s) {
s.writeList(tables);
}
}
class MemorySection extends Section {
final List<ir.DefinedMemory> memories;
MemorySection(this.memories, super.watchPoints);
@override
int get id => 5;
@override
bool get isNotEmpty => memories.isNotEmpty;
@override
void serializeContents(Serializer s) {
s.writeList(memories);
}
}
class TagSection extends Section {
final List<ir.Tag> tags;
TagSection(this.tags, super.watchPoints);
@override
int get id => 13;
@override
bool get isNotEmpty => tags.isNotEmpty;
@override
void serializeContents(Serializer s) {
s.writeList(tags);
}
}
class GlobalSection extends Section {
final List<ir.DefinedGlobal> globals;
GlobalSection(this.globals, super.watchPoints);
@override
int get id => 6;
@override
bool get isNotEmpty => globals.isNotEmpty;
@override
void serializeContents(Serializer s) {
s.writeList(globals);
}
}
class ExportSection extends Section {
final List<ir.Export> exports;
ExportSection(this.exports, super.watchPoints);
@override
int get id => 7;
@override
bool get isNotEmpty => exports.isNotEmpty;
@override
void serializeContents(Serializer s) {
s.writeList(exports);
}
}
class StartSection extends Section {
final ir.BaseFunction? startFunction;
StartSection(this.startFunction, super.watchPoints);
@override
int get id => 8;
@override
bool get isNotEmpty => startFunction != null;
@override
void serializeContents(Serializer s) {
s.writeUnsigned(startFunction!.index);
}
}
class _Element implements Serializable {
final ir.Table table;
final int startIndex;
final List<ir.BaseFunction> entries = [];
_Element(this.table, this.startIndex);
@override
void serialize(Serializer s) {
if (table.index != 0) {
s.writeByte(0x02);
s.writeUnsigned(table.index);
} else {
s.writeByte(0x00);
}
s.writeByte(0x41); // i32.const
s.writeSigned(startIndex);
s.writeByte(0x0B); // end
if (table.index != 0) {
s.writeByte(0x00); // elemkind
}
s.writeUnsigned(entries.length);
for (var entry in entries) {
s.writeUnsigned(entry.index);
}
}
}
class ElementSection extends Section {
final List<ir.DefinedTable> tables;
ElementSection(this.tables, super.watchPoints);
@override
int get id => 9;
@override
bool get isNotEmpty =>
tables.any((table) => table.elements.any((e) => e != null));
@override
void serializeContents(Serializer s) {
// Group nonempty element entries into contiguous stretches and serialize
// each stretch as an element.
List<_Element> elements = [];
for (final table in tables) {
_Element? current;
for (int i = 0; i < table.elements.length; i++) {
ir.BaseFunction? function = table.elements[i];
if (function != null) {
if (current == null) {
current = _Element(table, i);
elements.add(current);
}
current.entries.add(function);
} else {
current = null;
}
}
}
s.writeList(elements);
}
}
class DataCountSection extends Section {
final List<ir.DataSegment> dataSegments;
DataCountSection(this.dataSegments, super.watchPoints);
@override
int get id => 12;
@override
bool get isNotEmpty => dataSegments.isNotEmpty;
@override
void serializeContents(Serializer s) {
s.writeUnsigned(dataSegments.length);
}
}
class CodeSection extends Section {
final List<ir.DefinedFunction> functions;
CodeSection(this.functions, super.watchPoints);
@override
int get id => 10;
@override
bool get isNotEmpty => functions.isNotEmpty;
@override
void serializeContents(Serializer s) {
s.writeList(functions);
}
}
class DataSection extends Section {
final List<ir.DataSegment> dataSegments;
DataSection(this.dataSegments, super.watchPoints);
@override
int get id => 11;
@override
bool get isNotEmpty => dataSegments.isNotEmpty;
@override
void serializeContents(Serializer s) {
s.writeList(dataSegments);
}
}
abstract class CustomSection extends Section {
CustomSection(super.watchPoints);
@override
int get id => 0;
}
class NameSection extends CustomSection {
final List<ir.BaseFunction> functions;
final List<ir.DefType> types;
final int functionNameCount;
final int typeNameCount;
NameSection(this.functions, this.types, super.watchPoints,
{required this.functionNameCount, required this.typeNameCount});
@override
bool get isNotEmpty => functionNameCount > 0 || typeNameCount > 0;
@override
void serializeContents(Serializer s) {
s.writeName("name");
final functionNameSubsection = Serializer();
functionNameSubsection.writeUnsigned(functionNameCount);
for (int i = 0; i < functions.length; i++) {
String? functionName = functions[i].functionName;
if (functionName != null) {
functionNameSubsection.writeUnsigned(i);
functionNameSubsection.writeName(functionName);
}
}
final typeNameSubsection = Serializer();
typeNameSubsection.writeUnsigned(typeNameCount);
for (int i = 0; i < types.length; i++) {
final ty = types[i];
if (ty is ir.DataType) {
typeNameSubsection.writeUnsigned(i);
typeNameSubsection.writeName(ty.name);
}
}
s.writeByte(1); // Function names subsection
s.writeUnsigned(functionNameSubsection.data.length);
s.writeData(functionNameSubsection);
s.writeByte(4); // Type names subsection
s.writeUnsigned(typeNameSubsection.data.length);
s.writeData(typeNameSubsection);
}
}
@@ -0,0 +1,21 @@
// Copyright (c) 2023, 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.
export 'serializer.dart' show Serializable, Serializer;
export 'sections.dart'
show
CodeSection,
DataCountSection,
DataSection,
ElementSection,
ExportSection,
FunctionSection,
ImportSection,
GlobalSection,
MemorySection,
NameSection,
StartSection,
TagSection,
TableSection,
TypeSection;
@@ -1,4 +1,4 @@
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
// Copyright (c) 2023, 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.
@@ -10,6 +10,8 @@ abstract class Serializable {
void serialize(Serializer s);
}
// TODO(joshualitt): Now that we have an IR, we should consider switching to a
// visitor pattern.
class Serializer {
static bool traceEnabled = false;
+3 -35
View File
@@ -2,38 +2,6 @@
// 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.
export 'src/module.dart'
show
DataSegment,
DefinedFunction,
DefinedGlobal,
DefinedMemory,
DefinedTable,
BaseFunction,
Global,
Import,
ImportedFunction,
ImportedGlobal,
ImportedMemory,
ImportedTable,
Local,
Memory,
Module,
Table,
Tag;
export 'src/types.dart'
show
ArrayType,
DataType,
DefType,
FieldType,
FunctionType,
GlobalType,
HeapType,
NumType,
PackedType,
RefType,
StorageType,
StructType,
ValueType;
export 'src/instructions.dart' show Instructions, Label, ValidationError;
export 'src/ir/ir.dart';
export 'src/builder/builder.dart';
export 'src/serialize/serialize.dart';