// 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/printer.dart'; import '../serialize/serialize.dart'; import 'ir.dart'; /// A local variable defined in a function. class Local { final int index; final ValueType type; Local(this.index, this.type); void printTo( IrPrinter p, Map localNames, { bool isParam = false, }) { p.write(isParam ? 'param' : 'local'); p.write(' '); p.writeLocalIndexReference(index); p.write(' '); p.writeValueType(type); } @override String toString() => "$index"; } /// An (imported or defined) function. abstract class BaseFunction with Indexable, Exportable { @override final FinalizableIndex finalizableIndex; final FunctionType type; String? functionName; @override final Module enclosingModule; /// Whether this function is pure and has no effect. /// /// If marked as pure, we'll emit metadata in the /// `binaryen.removable.if.unused` custom section. bool isPure = false; /// Whether this function is called from JS. /// /// If marked as isJSCalled, we'll emit metadata in the /// `binaryen.js.called` custom section. bool isJSCalled = false; /// Inline hint for this function. /// /// If set, we'll emit metadata in the `binaryen.inline` custom section. /// Value must be in range [0..127]. int? inlineHint; BaseFunction( this.enclosingModule, this.finalizableIndex, this.type, [ this.functionName, ]); @override String get name => functionName ?? super.name; /// Creates an export of this function in this module. @override Export buildExport(String name) { return FunctionExport(name, this); } } /// A function defined in a module. class DefinedFunction extends BaseFunction implements Serializable { late final Instructions body; /// All local variables defined in the function, including its inputs. List get locals => body.locals; Map get localNames => body.localNames; DefinedFunction( super.enclosingModule, this.body, super.finalizableIndex, super.type, [ super.functionName, ]); DefinedFunction.withoutBody( super.enclosingModule, super.finalizableIndex, 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.sourceMapSerializer.copyMappings(localS.sourceMapSerializer, s.offset); s.writeData(localS); } void printTo(IrPrinter p) { if (isPure) { p.writeln('(@binaryen.removable.if.unused)'); } if (isJSCalled) { p.writeln('(@binaryen.js.called)'); } if (inlineHint != null) { p.writeln('(@binaryen.inline $inlineHint)'); } p.write('(func '); p.writeFunctionReference(this); String? exportName; for (final f in enclosingModule.exports.exported) { if (f is FunctionExport && f.function == this) { exportName = f.name; break; } } if (exportName != null) { p.write(' '); p.writeExport(exportName); } p.withLocalNames(localNames, () { if (type.inputs.isNotEmpty || type.outputs.isNotEmpty) { p.write(' '); type.printSignatureWithNamesTo(p, oneLine: true); } p.writeln(''); p.withIndent(() { for (int i = type.inputs.length; i < locals.length; ++i) { p.write('('); locals[i].printTo(p, localNames); p.writeln(')'); } body.printTo(p); }); }); p.write(')'); } void printDeclarationTo(IrPrinter p) { p.write('(func \$$functionName '); p.withLocalNames(localNames, () { type.printSignatureWithNamesTo(p, oneLine: true); }); p.write(' <...>'); p.writeln(')'); } @override String toString() => functionName ?? "#$finalizableIndex"; } /// An imported function. class ImportedFunction extends BaseFunction implements Import { @override final String module; @override final String name; ImportedFunction( super.enclosingModule, this.module, this.name, super.finalizableIndex, super.type, [ super.functionName, ]); @override void serialize(Serializer s) { s.writeName(module); s.writeName(name); s.writeByte(0x00); s.writeUnsigned(type.index); } void printTo(IrPrinter p) { assert(!isJSCalled); assert(inlineHint == null); if (isPure) { p.writeln('(@binaryen.removable.if.unused)'); } p.write('(func '); p.writeFunctionReference(this); p.write(' '); p.writeImport(module, name); p.write(' '); type.printOneLineSignatureTo(p); p.write(')'); } @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); } }