blob: c4564d2868f1dc168310cc301863e1aacfdec310 [file] [log] [blame] [edit]
// 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';
/// A Wasm module builder.
///
/// NOTE: The [ModuleBuilder] contains builders for various constituents and
/// those in return will refer back to the [ModuleBuilder], making the builders
/// be cyclic data structures.
///
/// The main reason for this is that e.g. an [InstructionsBuilder] may insert
/// new instructions. Doing so may require e.g. defining new function types. So
/// it does so by calling `moduleBuilder.functions.defineFunction()`.
///
/// We could avoid some of the cyclic dependencies by passing the individual
/// builders down to other builders that need it, instead of passing an
/// [ModuleBuilder] down that contains all builders.
class ModuleBuilder with Builder<ir.Module> {
final ir.Module module = ir.Module.uninitialized();
final String moduleName;
final Uri? sourceMapUrl;
final List<int> watchPoints;
late final TypesBuilder types;
late final functions = FunctionsBuilder(this);
late final elements = ElementsBuilder(this);
late final tables = TablesBuilder(this);
late final memories = MemoriesBuilder(module);
late final tags = TagsBuilder(module);
final dataSegments = DataSegmentsBuilder();
late final globals = GlobalsBuilder(this);
final exports = ExportsBuilder();
FunctionBuilder? _startFunction;
/// 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.moduleName, this.sourceMapUrl,
{ModuleBuilder? parent, this.watchPoints = const []}) {
types = TypesBuilder(this, parent: parent?.types);
}
FunctionBuilder get startFunction => _startFunction ??=
functions.define(types.defineFunction(const [], const []), "#init");
@override
ir.Module forceBuild() {
if (_startFunction case final start?) {
start.body.end();
}
final finalFunctions = functions.build();
final finalTables = tables.build();
final finalElements = elements.build();
final finalMemories = memories.build();
final finalGlobals = globals.build();
final finalTags = tags.build();
final imports = ir.Imports(
finalFunctions.imported,
finalTags.imported,
finalGlobals.imported,
finalTables.imported,
finalMemories.imported,
);
return module
..initialize(
moduleName,
finalFunctions,
_startFunction,
finalTables,
finalElements,
finalTags,
finalMemories,
exports.build(),
finalGlobals,
types.build(),
dataSegments.build(),
imports,
watchPoints,
sourceMapUrl);
}
}