[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:
committed by
Commit Queue
parent
c1d081fbcd
commit
96d6c2e0d3
@@ -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
|
||||
|
||||
@@ -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,
|
||||
));
|
||||
|
||||
@@ -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 ||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
+682
-588
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.
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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';
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
+3
-1
@@ -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;
|
||||
|
||||
@@ -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';
|
||||
|
||||
Reference in New Issue
Block a user