blob: 4e172dd774dc1beb71949481d9515acd7d745bdb [file] [log] [blame] [edit]
// Copyright (c) 2025, 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';
sealed class ElementSegment implements Serializable {}
sealed class ActiveElementSegment extends ElementSegment {
final Table table;
final int startIndex;
ActiveElementSegment(this.table, this.startIndex);
}
/// Initializes a specific table with function references.
///
/// NOTE: Can only be used if the table type is `RefType.func(nullable: true)`.
final class ActiveFunctionElementSegment extends ActiveElementSegment {
final List<BaseFunction> entries;
ActiveFunctionElementSegment(super.table, super.startIndex,
[List<BaseFunction>? entries])
: entries = entries ?? [];
@override
void serialize(Serializer s) {
final useTable0Encoding = table.index == 0;
if (useTable0Encoding) {
s.writeByte(0x00);
} else {
s.writeByte(0x02);
s.writeUnsigned(table.index);
}
s.serializeTableOffset(startIndex);
if (!useTable0Encoding) {
s.writeByte(_ElemKind.refFunc);
}
s.writeUnsigned(entries.length);
for (var entry in entries) {
s.writeUnsigned(entry.index);
}
}
static ActiveFunctionElementSegment deserialize(
Deserializer d,
Module module,
Types types,
Functions functions,
Tables tables,
Globals globals,
) {
const int tableIndex = 0;
final byte = d.readByte();
assert(byte == 0x00 || byte == 0x02);
final offset = d.deserializeTableOffset(types, functions, globals);
if (byte == 0x02) {
final elemKind = d.readByte();
if (elemKind != _ElemKind.refFunc) {
throw UnimplementedError('Unsupported element kind.');
}
}
final table = tables[tableIndex];
final tableElement = ActiveFunctionElementSegment(table, offset);
final count = d.readUnsigned();
for (int i = 0; i < count; i++) {
tableElement.entries.add(functions[d.readUnsigned()]);
}
return tableElement;
}
}
/// Initializes a specific table with initialization expressions.
final class ActiveExpressionElementSegment extends ActiveElementSegment {
final RefType type;
final List<List<Instruction>> expressions = [];
ActiveExpressionElementSegment(super.table, this.type, super.startIndex);
@override
void serialize(Serializer s) {
final useTableTable0RefNullFuncEncoding =
table.index == 0 && type == RefType.func(nullable: true);
if (useTableTable0RefNullFuncEncoding) {
s.writeByte(0x04);
} else {
s.writeByte(0x06);
s.writeUnsigned(table.index);
}
s.serializeTableOffset(startIndex);
if (!useTableTable0RefNullFuncEncoding) {
s.write(type);
}
s.writeUnsigned(expressions.length);
for (final expression in expressions) {
for (final instruction in expression) {
instruction.serialize(s);
}
}
}
static ActiveExpressionElementSegment deserialize(
Deserializer d,
Module module,
Types types,
Functions functions,
Tables tables,
Globals globals,
) {
final kind = d.readByte();
assert(kind == 0x04 || kind == 0x06);
final useTableTable0RefNullFuncEncoding = kind == 0x04;
final tableIndex = useTableTable0RefNullFuncEncoding ? 0 : d.readUnsigned();
final offset = d.deserializeTableOffset(types, functions, globals);
final type = useTableTable0RefNullFuncEncoding
? RefType.func(nullable: true)
: RefType.deserialize(d, types.defined);
final table = tables[tableIndex];
final tableElement = ActiveExpressionElementSegment(table, type, offset);
final count = d.readUnsigned();
for (int i = 0; i < count; i++) {
final instructions = <Instruction>[];
while (true) {
final instruction =
Instruction.deserializeConst(d, types, functions, globals);
instructions.add(instruction);
if (instruction is End) break;
}
tableElement.expressions.add(instructions);
}
return tableElement;
}
}
final class DeclarativeElementSegment implements ElementSegment {
final List<BaseFunction> entries;
DeclarativeElementSegment(this.entries);
@override
void serialize(Serializer s) {
if (entries.isEmpty) return;
s.writeByte(0x03);
s.writeByte(_ElemKind.refFunc);
s.writeUnsigned(entries.length);
for (final entry in entries) {
s.writeUnsigned(entry.index);
}
}
static DeclarativeElementSegment deserialize(
Deserializer d, Functions functions) {
if (d.readByte() != 0x03) {
throw StateError('Expected declarative segment to start with 0x03.');
}
final elemkind = d.readByte();
if (elemkind != _ElemKind.refFunc) {
throw UnsupportedError('Unsupported element kind: $elemkind');
}
final declaredFunctions = d.readList((d) => functions[d.readUnsigned()]);
return DeclarativeElementSegment(declaredFunctions);
}
}
abstract class _ElemKind {
static const refFunc = 0;
}
extension on Serializer {
void serializeTableOffset(int offset) {
I32Const(offset).serialize(this);
End().serialize(this);
}
}
extension on Deserializer {
int deserializeTableOffset(
Types types, Functions functions, Globals globals) {
final i0 = Instruction.deserializeConst(this, types, functions, globals);
final i1 = Instruction.deserializeConst(this, types, functions, globals);
if (i0 is! I32Const || i1 is! End) {
throw StateError('Expected offset to be encoded as '
'`(i32.const <value>) (end)`. '
'Got instead: (${i0.name}) (${i1.name})');
}
return i0.value;
}
}