| // 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 '../../debug_info.dart' show DebugInfoSerializer; | |
| 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<int, String> 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); | |
| } | |
| void collectUsedTypes(Set<DefType> usedTypes) { | |
| usedTypes.add(type); | |
| } | |
| } | |
| /// 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<Local> get locals => body.locals; | |
| Map<int, String> 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 collectUsedTypes(Set<DefType> usedTypes) { | |
| super.collectUsedTypes(usedTypes); | |
| body.collectUsedTypes(usedTypes); | |
| } | |
| @override | |
| void serialize(Serializer s, [DebugInfoSerializer? debugInfoSerializer]) { | |
| // 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; | |
| } | |
| } | |
| final localsLength = localS.offset; | |
| final byteDebugInfo = body.serialize(localS, debugInfoSerializer != null); | |
| s.writeUnsigned(localS.data.length); | |
| final functionCodeOffset = s.offset + localsLength; | |
| s.writeData(localS); | |
| if (byteDebugInfo != null && debugInfoSerializer != null) { | |
| debugInfoSerializer.addFunction(functionCodeOffset, byteDebugInfo); | |
| } | |
| } | |
| 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); | |
| } | |
| } |