package org.graalvm.wasm;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.RootCallTarget;
import com.oracle.truffle.api.Truffle;
import com.oracle.truffle.api.nodes.LoopNode;
import com.oracle.truffle.api.nodes.Node;
import org.graalvm.wasm.collection.ByteArrayList;
import org.graalvm.wasm.constants.CallIndirect;
import org.graalvm.wasm.constants.ExportIdentifier;
import org.graalvm.wasm.constants.GlobalModifier;
import org.graalvm.wasm.constants.ImportIdentifier;
import org.graalvm.wasm.constants.Instructions;
import org.graalvm.wasm.constants.LimitsPrefix;
import org.graalvm.wasm.constants.Section;
import org.graalvm.wasm.exception.Failure;
import org.graalvm.wasm.exception.WasmException;
import org.graalvm.wasm.memory.WasmMemory;
import org.graalvm.wasm.nodes.WasmBlockNode;
import org.graalvm.wasm.nodes.WasmCallStubNode;
import org.graalvm.wasm.nodes.WasmIfNode;
import org.graalvm.wasm.nodes.WasmIndirectCallNode;
import org.graalvm.wasm.nodes.WasmRootNode;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CodingErrorAction;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import static org.graalvm.wasm.WasmType.F32_TYPE;
import static org.graalvm.wasm.WasmType.F64_TYPE;
import static org.graalvm.wasm.WasmType.I32_TYPE;
import static org.graalvm.wasm.WasmType.I64_TYPE;
import static org.graalvm.wasm.WasmUtil.unsignedInt32ToLong;
public class BinaryParser extends BinaryStreamParser {
private class ParsingExceptionHandler implements Thread.UncaughtExceptionHandler {
private Throwable parsingException = null;
@Override
public void uncaughtException(Thread t, Throwable e) {
this.parsingException = e;
}
public Throwable parsingException() {
return parsingException;
}
}
private static final int MIN_DEFAULT_STACK_SIZE = 1_000_000;
private static final int MAX_DEFAULT_ASYNC_STACK_SIZE = 10_000_000;
private static final int MAGIC = 0x6d736100;
private static final int VERSION = 0x00000001;
private static final long TABLE_MAX_SIZE = Integer.MAX_VALUE;
private static final long MEMORY_MAX_PAGES = 1 << 16;
private final WasmLanguage language;
private final WasmModule module;
private final ModuleLimits moduleLimits;
private final int[] limitsResult;
public BinaryParser(WasmLanguage language, WasmModule module) {
this(language, module, null);
}
@CompilerDirectives.TruffleBoundary
public BinaryParser(WasmLanguage language, WasmModule module, ModuleLimits moduleLimits) {
super(module.data());
this.language = language;
this.module = module;
this.limitsResult = new int[2];
this.moduleLimits = moduleLimits;
}
@CompilerDirectives.TruffleBoundary
public void readModule() {
if (moduleLimits != null) {
moduleLimits.checkModuleSize(data.length);
}
validateMagicNumberAndVersion();
readSymbolSections();
}
@CompilerDirectives.TruffleBoundary
public void readInstance(WasmContext context, WasmInstance instance) {
int binarySize = instance.module().data().length;
final int asyncParsingBinarySize = WasmOptions.AsyncParsingBinarySize.getValue(context.environment().getOptions());
if (binarySize < asyncParsingBinarySize) {
readInstanceSynchronously(context, instance);
} else {
final Runnable parsing = new Runnable() {
@Override
public void run() {
readInstanceSynchronously(context, instance);
}
};
final String name = "wasm-parsing-thread(" + instance.name() + ")";
final int requestedSize = WasmOptions.AsyncParsingStackSize.getValue(context.environment().getOptions()) * 1000;
final int defaultSize = Math.max(MIN_DEFAULT_STACK_SIZE, Math.min(2 * binarySize, MAX_DEFAULT_ASYNC_STACK_SIZE));
final int stackSize = requestedSize != 0 ? requestedSize : defaultSize;
final Thread parsingThread = new Thread(null, parsing, name, stackSize);
final ParsingExceptionHandler handler = new ParsingExceptionHandler();
parsingThread.setUncaughtExceptionHandler(handler);
parsingThread.start();
try {
parsingThread.join();
if (handler.parsingException() != null) {
throw WasmException.create(Failure.UNSPECIFIED_INVALID, "Asynchronous parsing failed.");
}
} catch (InterruptedException e) {
throw WasmException.create(Failure.UNSPECIFIED_INVALID, "Asynchronous parsing interrupted.");
}
}
}
private void readInstanceSynchronously(WasmContext context, WasmInstance instance) {
if (tryJumpToSection(Section.CODE)) {
readCodeSection(context, instance);
}
}
private void validateMagicNumberAndVersion() {
Assert.assertIntEqual(read4(), MAGIC, "Invalid MAGIC number", Failure.UNSPECIFIED_MALFORMED);
Assert.assertIntEqual(read4(), VERSION, "Invalid VERSION number", Failure.UNSPECIFIED_MALFORMED);
}
private void readSymbolSections() {
int lastNonCustomSection = -1;
while (!isEOF()) {
byte sectionID = read1();
if (sectionID != Section.CUSTOM) {
if (sectionID > lastNonCustomSection) {
lastNonCustomSection = sectionID;
} else if (lastNonCustomSection == sectionID) {
Assert.fail("Duplicated section " + sectionID, Failure.UNSPECIFIED_MALFORMED);
} else {
Assert.fail("Section " + sectionID + " defined after section " + lastNonCustomSection, Failure.UNSPECIFIED_MALFORMED);
}
}
int size = readUnsignedInt32();
int startOffset = offset;
switch (sectionID) {
case Section.CUSTOM:
readCustomSection(size);
break;
case Section.TYPE:
readTypeSection();
break;
case Section.IMPORT:
readImportSection();
break;
case Section.FUNCTION:
readFunctionSection();
break;
case Section.TABLE:
readTableSection();
break;
case Section.MEMORY:
readMemorySection();
break;
case Section.GLOBAL:
readGlobalSection();
break;
case Section.EXPORT:
readExportSection();
break;
case Section.START:
readStartSection();
break;
case Section.ELEMENT:
readElementSection();
break;
case Section.CODE:
skipCodeSection();
break;
case Section.DATA:
readDataSection(null, null);
break;
default:
Assert.fail("invalid section ID: " + sectionID, Failure.UNSPECIFIED_MALFORMED);
}
Assert.assertIntEqual(offset - startOffset, size, String.format("Declared section (0x%02X) size is incorrect", sectionID), Failure.UNSPECIFIED_MALFORMED);
}
}
private void readCustomSection(int size) {
int nextSectionOffset = offset + size;
String name = readName();
int dataLength = Math.max(0, nextSectionOffset - offset);
module.allocateCustomSection(name, offset, dataLength);
offset += dataLength;
}
private void readTypeSection() {
int numTypes = readVectorLength();
if (moduleLimits != null) {
moduleLimits.checkTypeCount(numTypes);
}
for (int t = 0; t != numTypes; ++t) {
byte type = read1();
switch (type) {
case 0x60:
readFunctionType();
break;
default:
Assert.fail("Only function types are supported in the type section", Failure.UNSPECIFIED_MALFORMED);
}
}
}
private void readImportSection() {
Assert.assertIntEqual(module.symbolTable().maxGlobalIndex(), -1,
"The global index should be -1 when the import section is first read.", Failure.UNSPECIFIED_INVALID);
int numImports = readVectorLength();
if (moduleLimits != null) {
moduleLimits.checkImportCount(numImports);
}
for (int i = 0; i != numImports; ++i) {
String moduleName = readName();
String memberName = readName();
byte importType = readImportType();
switch (importType) {
case ImportIdentifier.FUNCTION: {
int typeIndex = readTypeIndex();
module.symbolTable().importFunction(moduleName, memberName, typeIndex);
break;
}
case ImportIdentifier.TABLE: {
byte elemType = readElemType();
Assert.assertIntEqual(elemType, ReferenceTypes.FUNCREF, "Invalid element type for table import", Failure.UNSPECIFIED_MALFORMED);
readTableLimits(limitsResult);
module.symbolTable().importTable(moduleName, memberName, limitsResult[0], limitsResult[1]);
break;
}
case ImportIdentifier.MEMORY: {
readMemoryLimits(limitsResult);
module.symbolTable().importMemory(moduleName, memberName, limitsResult[0], limitsResult[1]);
break;
}
case ImportIdentifier.GLOBAL: {
byte type = readValueType();
byte mutability = readMutability();
int index = module.symbolTable().maxGlobalIndex() + 1;
module.symbolTable().importGlobal(moduleName, memberName, index, type, mutability);
break;
}
default: {
Assert.fail(String.format("Invalid import type identifier: 0x%02X", importType), Failure.UNSPECIFIED_MALFORMED);
}
}
}
}
private void readFunctionSection() {
int numFunctions = readVectorLength();
if (moduleLimits != null) {
moduleLimits.checkFunctionCount(numFunctions);
}
for (int i = 0; i != numFunctions; ++i) {
int functionTypeIndex = readUnsignedInt32();
module.symbolTable().declareFunction(functionTypeIndex);
}
}
private void readTableSection() {
int numTables = readVectorLength();
Assert.assertIntLessOrEqual(module.symbolTable().tableCount() + numTables, 1, "Can import or declare at most one table per module", Failure.UNSPECIFIED_MALFORMED);
for (byte tableIndex = 0; tableIndex != numTables; ++tableIndex) {
byte elemType = readElemType();
Assert.assertIntEqual(elemType, ReferenceTypes.FUNCREF, "Invalid element type for table", Failure.UNSPECIFIED_MALFORMED);
readTableLimits(limitsResult);
module.symbolTable().allocateTable(limitsResult[0], limitsResult[1]);
}
}
private void readMemorySection() {
int numMemories = readVectorLength();
Assert.assertIntLessOrEqual(module.symbolTable().memoryCount() + numMemories, 1, "Can import or declare at most one memory per module", Failure.UNSPECIFIED_MALFORMED);
for (int i = 0; i != numMemories; ++i) {
readMemoryLimits(limitsResult);
module.symbolTable().allocateMemory(limitsResult[0], limitsResult[1]);
}
}
private void skipCodeSection() {
int numImportedFunctions = module.importedFunctions().size();
int expectedNumCodeEntries = module.numFunctions() - numImportedFunctions;
int numCodeEntries = readVectorLength();
if (expectedNumCodeEntries != numCodeEntries) {
throw WasmException.format(Failure.UNSPECIFIED_INVALID, "Unexpected number of code entries: %d (%d expected).", numCodeEntries, expectedNumCodeEntries);
}
for (int entryIndex = 0; entryIndex != numCodeEntries; ++entryIndex) {
int codeEntrySize = readUnsignedInt32();
int nextCodeEntryOffset = offset + codeEntrySize;
if (moduleLimits != null) {
moduleLimits.checkFunctionSize(codeEntrySize);
int localCount = readCodeEntryLocals().size() + module.function(numImportedFunctions + entryIndex).numArguments();
moduleLimits.checkLocalCount(localCount);
}
offset = nextCodeEntryOffset;
}
}
private void readCodeSection(WasmContext context, WasmInstance instance) {
final int functionIndexOffset = instance.module().importedFunctions().size();
final int numCodeEntries = readVectorLength();
WasmRootNode[] rootNodes = new WasmRootNode[numCodeEntries];
for (int entry = 0; entry != numCodeEntries; ++entry) {
rootNodes[entry] = createCodeEntry(instance, functionIndexOffset + entry);
}
for (int entryIndex = 0; entryIndex != numCodeEntries; ++entryIndex) {
int codeEntrySize = readUnsignedInt32();
int startOffset = offset;
readCodeEntry(instance, functionIndexOffset + entryIndex, rootNodes[entryIndex]);
Assert.assertIntEqual(offset - startOffset, codeEntrySize, String.format("Code entry %d size is incorrect", entryIndex), Failure.UNSPECIFIED_MALFORMED);
final int currentEntryIndex = entryIndex;
context.linker().resolveCodeEntry(module, currentEntryIndex);
}
}
private WasmRootNode createCodeEntry(WasmInstance instance, int funcIndex) {
final WasmFunction function = module.symbolTable().function(funcIndex);
WasmCodeEntry codeEntry = new WasmCodeEntry(function, data);
function.setCodeEntry(codeEntry);
WasmRootNode rootNode = new WasmRootNode(language, instance, codeEntry);
RootCallTarget callTarget = Truffle.getRuntime().createCallTarget(rootNode);
instance.setTarget(funcIndex, callTarget);
return rootNode;
}
private void readCodeEntry(WasmInstance instance, int funcIndex, WasmRootNode rootNode) {
initCodeEntryLocals(funcIndex);
final WasmFunction function = module.symbolTable().function(funcIndex);
final byte returnTypeId = function.returnType();
final int returnTypeLength = function.returnTypeLength();
ExecutionState state = new ExecutionState();
WasmBlockNode bodyBlock = readBlockBody(instance, rootNode.codeEntry(), state, returnTypeId, false);
Assert.assertIntEqual(state.stackSize(), returnTypeLength,
"Stack size must match the return type length at the function end", Failure.RETURN_SIZE_MISMATCH);
rootNode.setBody(bodyBlock);
rootNode.codeEntry().setIntConstants(state.intConstants());
if (state.branchTables().length > 0) {
rootNode.codeEntry().setBranchTables(state.branchTables());
}
rootNode.codeEntry().setProfileCount(state.profileCount());
rootNode.codeEntry().initStackLocals(rootNode.getFrameDescriptor(), state.maxStackSize());
}
private ByteArrayList readCodeEntryLocals() {
int numLocalsGroups = readVectorLength();
ByteArrayList localTypes = new ByteArrayList();
for (int localGroup = 0; localGroup < numLocalsGroups; localGroup++) {
int groupLength = readVectorLength();
byte t = readValueType();
for (int i = 0; i != groupLength; ++i) {
localTypes.add(t);
}
}
return localTypes;
}
private void initCodeEntryLocals(int funcIndex) {
WasmCodeEntry codeEntry = module.symbolTable().function(funcIndex).codeEntry();
int typeIndex = module.symbolTable().function(funcIndex).typeIndex();
ByteArrayList argumentTypes = module.symbolTable().functionTypeArgumentTypes(typeIndex);
ByteArrayList localTypes = readCodeEntryLocals();
byte[] allLocalTypes = ByteArrayList.concat(argumentTypes, localTypes);
codeEntry.setLocalTypes(allLocalTypes);
}
private WasmBlockNode readBlock(WasmInstance instance, WasmCodeEntry codeEntry, ExecutionState state) {
byte blockTypeId = readBlockType();
return readBlockBody(instance, codeEntry, state, blockTypeId, false);
}
private LoopNode readLoop(WasmInstance instance, WasmCodeEntry codeEntry, ExecutionState state) {
byte blockTypeId = readBlockType();
return readLoop(instance, codeEntry, state, blockTypeId);
}
private WasmBlockNode readBlockBody(WasmInstance instance, WasmCodeEntry codeEntry, ExecutionState state, byte returnTypeId, boolean isLoopBody) {
ArrayList<Node> children = new ArrayList<>();
int startStackSize = state.stackSize();
int startOffset = offset();
int startIntConstantOffset = state.intConstantOffset();
int startBranchTableOffset = state.branchTableOffset();
int startProfileCount = state.profileCount();
final WasmBlockNode currentBlock = new WasmBlockNode(instance, codeEntry, startOffset, returnTypeId, startStackSize, startIntConstantOffset,
startBranchTableOffset, startProfileCount);
state.startBlock(currentBlock, isLoopBody);
int opcode;
do {
opcode = read1() & 0xFF;
switch (opcode) {
case Instructions.UNREACHABLE:
state.setReachable(false);
break;
case Instructions.NOP:
break;
case Instructions.BLOCK: {
boolean reachable = state.isReachable();
WasmBlockNode nestedBlock = readBlock(instance, codeEntry, state);
children.add(nestedBlock);
state.setReachable(reachable);
break;
}
case Instructions.LOOP: {
boolean reachable = state.isReachable();
LoopNode loopBlock = readLoop(instance, codeEntry, state);
children.add(loopBlock);
state.setReachable(reachable);
break;
}
case Instructions.IF: {
state.popChecked(I32_TYPE);
boolean reachable = state.isReachable();
WasmIfNode ifNode = readIf(instance, codeEntry, state);
children.add(ifNode);
state.setReachable(reachable);
break;
}
case Instructions.ELSE:
case Instructions.END:
if (state.isReachable()) {
final int actualReturnLength = state.stackSize() - state.getStackSize(0);
Assert.assertIntEqual(actualReturnLength, currentBlock.returnLength(), "Wrong number of values on the stack on the end of the block", Failure.RETURN_SIZE_MISMATCH);
if (currentBlock.returnLength() == 1) {
state.popChecked(currentBlock.returnTypeId());
state.push(currentBlock.returnTypeId());
}
} else {
state.unwindStack(state.getStackSize(0));
if (currentBlock.returnLength() == 1) {
state.push(currentBlock.returnTypeId());
}
}
break;
case Instructions.BR: {
final int unwindLevel = readTargetOffset();
final int targetStackSize = state.getStackSize(unwindLevel);
state.useIntConstant(targetStackSize);
state.useIntConstant(state.getContinuationLength(unwindLevel));
state.checkContinuationType(unwindLevel);
state.setReachable(false);
break;
}
case Instructions.BR_IF: {
state.popChecked(I32_TYPE);
final int unwindLevel = readTargetOffset();
final int targetStackSize = state.getStackSize(unwindLevel);
state.useIntConstant(targetStackSize);
final int continuationReturnLength = state.getContinuationLength(unwindLevel);
state.useIntConstant(continuationReturnLength);
state.checkContinuationType(unwindLevel);
state.incrementProfileCount();
break;
}
case Instructions.BR_TABLE: {
state.popChecked(I32_TYPE);
final int numLabels = readVectorLength();
final int[] branchTable = new int[2 * (numLabels + 1) + 1];
int continuationReturnLength = -1;
for (int i = 0; i != numLabels + 1; ++i) {
final int unwindLevel = readTargetOffset();
branchTable[1 + 2 * i + 0] = unwindLevel;
branchTable[1 + 2 * i + 1] = state.getStackSize(unwindLevel);
final int targetContinuationLength = state.getContinuationLength(unwindLevel);
state.checkContinuationType(unwindLevel);
if (continuationReturnLength == -1) {
continuationReturnLength = targetContinuationLength;
} else {
Assert.assertIntEqual(continuationReturnLength, targetContinuationLength,
"All target blocks in br.table must have the same return type length.", Failure.TABLE_TARGET_MISMATCH);
}
}
branchTable[0] = continuationReturnLength;
state.saveBranchTable(branchTable);
state.setReachable(false);
break;
}
case Instructions.RETURN: {
Assert.assertIntLessOrEqual(codeEntry.function().returnTypeLength(), 1, Failure.MULTIPLE_RETURN_VALUES);
if (codeEntry.function().returnTypeLength() == 1) {
state.popChecked(codeEntry.function().returnType());
}
state.useIntConstant(state.depth());
state.useIntConstant(state.getRootBlockReturnLength());
state.setReachable(false);
break;
}
case Instructions.CALL: {
final int functionIndex = readFunctionIndex();
final WasmFunction function = module.symbolTable().function(functionIndex);
for (int i = function.numArguments() - 1; i >= 0; --i) {
state.popChecked(function.argumentTypeAt(i));
}
Assert.assertIntLessOrEqual(function.returnTypeLength(), 1, Failure.MULTIPLE_RETURN_VALUES);
if (function.returnTypeLength() == 1) {
state.push(function.returnType());
}
children.add(new WasmCallStubNode(function));
final int stubIndex = children.size() - 1;
module.addLinkAction((context, inst) -> context.linker().resolveCallsite(inst, currentBlock, stubIndex, function));
break;
}
case Instructions.CALL_INDIRECT: {
int expectedFunctionTypeIndex = readTypeIndex();
state.popChecked(I32_TYPE);
for (int i = module.symbolTable().functionTypeArgumentCount(expectedFunctionTypeIndex) - 1; i >= 0; --i) {
state.popChecked(module.symbolTable().functionTypeArgumentTypeAt(expectedFunctionTypeIndex, i));
}
final int returnLength = module.symbolTable().functionTypeReturnTypeLength(expectedFunctionTypeIndex);
Assert.assertIntLessOrEqual(returnLength, 1, Failure.MULTIPLE_RETURN_VALUES);
if (returnLength == 1) {
state.push(module.symbolTable().functionTypeReturnType(expectedFunctionTypeIndex));
}
children.add(WasmIndirectCallNode.create());
Assert.assertIntEqual(read1(), CallIndirect.ZERO_TABLE, "CALL_INDIRECT: Instruction must end with 0x00", Failure.UNSPECIFIED_MALFORMED);
break;
}
case Instructions.DROP:
state.pop();
break;
case Instructions.SELECT:
state.popChecked(I32_TYPE);
final byte t = state.pop();
state.popChecked(t);
state.push(t);
break;
case Instructions.LOCAL_GET: {
final int localIndex = readLocalIndex();
Assert.assertIntLessOrEqual(localIndex, codeEntry.numLocals(), "Invalid local index for local.get", Failure.UNSPECIFIED_MALFORMED);
state.push(codeEntry.localType(localIndex));
break;
}
case Instructions.LOCAL_SET: {
final int localIndex = readLocalIndex();
Assert.assertIntLessOrEqual(localIndex, codeEntry.numLocals(), "Invalid local index for local.set", Failure.UNSPECIFIED_MALFORMED);
state.popChecked(codeEntry.localType(localIndex));
break;
}
case Instructions.LOCAL_TEE: {
final int localIndex = readLocalIndex();
Assert.assertIntLessOrEqual(localIndex, codeEntry.numLocals(), "Invalid local index for local.tee", Failure.UNSPECIFIED_MALFORMED);
state.popChecked(codeEntry.localType(localIndex));
state.push(codeEntry.localType(localIndex));
break;
}
case Instructions.GLOBAL_GET: {
final int index = readGlobalIndex();
Assert.assertIntLessOrEqual(index, module.symbolTable().maxGlobalIndex(),
"Invalid global index for global.get.", Failure.UNSPECIFIED_MALFORMED);
state.push(module.symbolTable().globalValueType(index));
break;
}
case Instructions.GLOBAL_SET: {
final int index = readGlobalIndex();
Assert.assertIntLessOrEqual(index, module.symbolTable().maxGlobalIndex(),
"Invalid global index for global.set.", Failure.UNSPECIFIED_MALFORMED);
Assert.assertTrue(module.symbolTable().globalMutability(index) == GlobalModifier.MUTABLE,
"Immutable globals cannot be set: " + index, Failure.UNSPECIFIED_MALFORMED);
state.popChecked(module.symbolTable().globalValueType(index));
break;
}
case Instructions.F32_LOAD:
load(state, F32_TYPE);
break;
case Instructions.F64_LOAD:
load(state, F64_TYPE);
break;
case Instructions.I32_LOAD:
case Instructions.I32_LOAD8_S:
case Instructions.I32_LOAD8_U:
case Instructions.I32_LOAD16_S:
case Instructions.I32_LOAD16_U:
load(state, I32_TYPE);
break;
case Instructions.I64_LOAD:
case Instructions.I64_LOAD8_S:
case Instructions.I64_LOAD8_U:
case Instructions.I64_LOAD16_S:
case Instructions.I64_LOAD16_U:
case Instructions.I64_LOAD32_S:
case Instructions.I64_LOAD32_U:
load(state, I64_TYPE);
break;
case Instructions.F32_STORE:
store(state, F32_TYPE);
break;
case Instructions.F64_STORE:
store(state, F64_TYPE);
break;
case Instructions.I32_STORE:
case Instructions.I32_STORE_8:
case Instructions.I32_STORE_16:
store(state, I32_TYPE);
break;
case Instructions.I64_STORE:
case Instructions.I64_STORE_8:
case Instructions.I64_STORE_16:
case Instructions.I64_STORE_32:
store(state, I64_TYPE);
break;
case Instructions.MEMORY_SIZE:
read1();
state.push(I32_TYPE);
break;
case Instructions.MEMORY_GROW: {
read1();
state.popChecked(I32_TYPE);
state.push(I32_TYPE);
break;
}
case Instructions.I32_CONST:
readSignedInt32();
state.push(I32_TYPE);
break;
case Instructions.I64_CONST:
readSignedInt64();
state.push(I64_TYPE);
break;
case Instructions.F32_CONST:
read4();
state.push(F32_TYPE);
break;
case Instructions.F64_CONST:
read8();
state.push(F64_TYPE);
break;
case Instructions.I32_EQZ:
state.popChecked(I32_TYPE);
state.push(I32_TYPE);
break;
case Instructions.I32_EQ:
case Instructions.I32_NE:
case Instructions.I32_LT_S:
case Instructions.I32_LT_U:
case Instructions.I32_GT_S:
case Instructions.I32_GT_U:
case Instructions.I32_LE_S:
case Instructions.I32_LE_U:
case Instructions.I32_GE_S:
case Instructions.I32_GE_U:
state.popChecked(I32_TYPE);
state.popChecked(I32_TYPE);
state.push(I32_TYPE);
break;
case Instructions.I64_EQZ:
state.popChecked(I64_TYPE);
state.push(I32_TYPE);
break;
case Instructions.I64_EQ:
case Instructions.I64_NE:
case Instructions.I64_LT_S:
case Instructions.I64_LT_U:
case Instructions.I64_GT_S:
case Instructions.I64_GT_U:
case Instructions.I64_LE_S:
case Instructions.I64_LE_U:
case Instructions.I64_GE_S:
case Instructions.I64_GE_U:
state.popChecked(I64_TYPE);
state.popChecked(I64_TYPE);
state.push(I32_TYPE);
break;
case Instructions.F32_EQ:
case Instructions.F32_NE:
case Instructions.F32_LT:
case Instructions.F32_GT:
case Instructions.F32_LE:
case Instructions.F32_GE:
state.popChecked(F32_TYPE);
state.popChecked(F32_TYPE);
state.push(I32_TYPE);
break;
case Instructions.F64_EQ:
case Instructions.F64_NE:
case Instructions.F64_LT:
case Instructions.F64_GT:
case Instructions.F64_LE:
case Instructions.F64_GE:
state.popChecked(F64_TYPE);
state.popChecked(F64_TYPE);
state.push(I32_TYPE);
break;
case Instructions.I32_CLZ:
case Instructions.I32_CTZ:
case Instructions.I32_POPCNT:
state.popChecked(I32_TYPE);
state.push(I32_TYPE);
break;
case Instructions.I32_ADD:
case Instructions.I32_SUB:
case Instructions.I32_MUL:
case Instructions.I32_DIV_S:
case Instructions.I32_DIV_U:
case Instructions.I32_REM_S:
case Instructions.I32_REM_U:
case Instructions.I32_AND:
case Instructions.I32_OR:
case Instructions.I32_XOR:
case Instructions.I32_SHL:
case Instructions.I32_SHR_S:
case Instructions.I32_SHR_U:
case Instructions.I32_ROTL:
case Instructions.I32_ROTR:
state.popChecked(I32_TYPE);
state.popChecked(I32_TYPE);
state.push(I32_TYPE);
break;
case Instructions.I64_CLZ:
case Instructions.I64_CTZ:
case Instructions.I64_POPCNT:
state.popChecked(I64_TYPE);
state.push(I64_TYPE);
break;
case Instructions.I64_ADD:
case Instructions.I64_SUB:
case Instructions.I64_MUL:
case Instructions.I64_DIV_S:
case Instructions.I64_DIV_U:
case Instructions.I64_REM_S:
case Instructions.I64_REM_U:
case Instructions.I64_AND:
case Instructions.I64_OR:
case Instructions.I64_XOR:
case Instructions.I64_SHL:
case Instructions.I64_SHR_S:
case Instructions.I64_SHR_U:
case Instructions.I64_ROTL:
case Instructions.I64_ROTR:
state.popChecked(I64_TYPE);
state.popChecked(I64_TYPE);
state.push(I64_TYPE);
break;
case Instructions.F32_ABS:
case Instructions.F32_NEG:
case Instructions.F32_CEIL:
case Instructions.F32_FLOOR:
case Instructions.F32_TRUNC:
case Instructions.F32_NEAREST:
case Instructions.F32_SQRT:
state.popChecked(F32_TYPE);
state.push(F32_TYPE);
break;
case Instructions.F32_ADD:
case Instructions.F32_SUB:
case Instructions.F32_MUL:
case Instructions.F32_DIV:
case Instructions.F32_MIN:
case Instructions.F32_MAX:
case Instructions.F32_COPYSIGN:
state.popChecked(F32_TYPE);
state.popChecked(F32_TYPE);
state.push(F32_TYPE);
break;
case Instructions.F64_ABS:
case Instructions.F64_NEG:
case Instructions.F64_CEIL:
case Instructions.F64_FLOOR:
case Instructions.F64_TRUNC:
case Instructions.F64_NEAREST:
case Instructions.F64_SQRT:
state.popChecked(F64_TYPE);
state.push(F64_TYPE);
break;
case Instructions.F64_ADD:
case Instructions.F64_SUB:
case Instructions.F64_MUL:
case Instructions.F64_DIV:
case Instructions.F64_MIN:
case Instructions.F64_MAX:
case Instructions.F64_COPYSIGN:
state.popChecked(F64_TYPE);
state.popChecked(F64_TYPE);
state.push(F64_TYPE);
break;
case Instructions.I32_WRAP_I64:
state.popChecked(I64_TYPE);
state.push(I32_TYPE);
break;
case Instructions.I32_TRUNC_F32_S:
case Instructions.I32_TRUNC_F32_U:
state.popChecked(F32_TYPE);
state.push(I32_TYPE);
break;
case Instructions.I32_TRUNC_F64_S:
case Instructions.I32_TRUNC_F64_U:
state.popChecked(F64_TYPE);
state.push(I32_TYPE);
break;
case Instructions.I64_EXTEND_I32_S:
case Instructions.I64_EXTEND_I32_U:
state.popChecked(I32_TYPE);
state.push(I64_TYPE);
break;
case Instructions.I64_TRUNC_F32_S:
case Instructions.I64_TRUNC_F32_U:
state.popChecked(F32_TYPE);
state.push(I64_TYPE);
break;
case Instructions.I64_TRUNC_F64_S:
case Instructions.I64_TRUNC_F64_U:
state.popChecked(F64_TYPE);
state.push(I64_TYPE);
break;
case Instructions.F32_CONVERT_I32_S:
case Instructions.F32_CONVERT_I32_U:
state.popChecked(I32_TYPE);
state.push(F32_TYPE);
break;
case Instructions.F32_CONVERT_I64_S:
case Instructions.F32_CONVERT_I64_U:
state.popChecked(I64_TYPE);
state.push(F32_TYPE);
break;
case Instructions.F32_DEMOTE_F64:
state.popChecked(F64_TYPE);
state.push(F32_TYPE);
break;
case Instructions.F64_CONVERT_I32_S:
case Instructions.F64_CONVERT_I32_U:
state.popChecked(I32_TYPE);
state.push(F64_TYPE);
break;
case Instructions.F64_CONVERT_I64_S:
case Instructions.F64_CONVERT_I64_U:
state.popChecked(I64_TYPE);
state.push(F64_TYPE);
break;
case Instructions.F64_PROMOTE_F32:
state.popChecked(F32_TYPE);
state.push(F64_TYPE);
break;
case Instructions.I32_REINTERPRET_F32:
state.popChecked(F32_TYPE);
state.push(I32_TYPE);
break;
case Instructions.I64_REINTERPRET_F64:
state.popChecked(F64_TYPE);
state.push(I64_TYPE);
break;
case Instructions.F32_REINTERPRET_I32:
state.popChecked(I32_TYPE);
state.push(F32_TYPE);
break;
case Instructions.F64_REINTERPRET_I64:
state.popChecked(I64_TYPE);
state.push(F64_TYPE);
break;
default:
Assert.fail(Assert.format("Unknown opcode: 0x%02x", opcode), Failure.UNSPECIFIED_MALFORMED);
break;
}
} while (opcode != Instructions.END && opcode != Instructions.ELSE);
currentBlock.initialize(toArray(children),
offset() - startOffset, state.intConstantOffset() - startIntConstantOffset,
state.branchTableOffset() - startBranchTableOffset, state.profileCount() - startProfileCount);
state.endBlock();
return currentBlock;
}
private void store(ExecutionState state, byte type) {
readUnsignedInt32();
readUnsignedInt32();
state.popChecked(type);
state.popChecked(I32_TYPE);
}
private void load(ExecutionState state, byte type) {
readUnsignedInt32();
readUnsignedInt32();
state.popChecked(I32_TYPE);
state.push(type);
}
static Node[] toArray(ArrayList<Node> list) {
if (list.size() == 0) {
return null;
}
return list.toArray(new Node[list.size()]);
}
private LoopNode readLoop(WasmInstance instance, WasmCodeEntry codeEntry, ExecutionState state, byte returnTypeId) {
final int initialStackPointer = state.stackSize();
WasmBlockNode loopBlock = readBlockBody(instance, codeEntry, state, returnTypeId, true);
state.unwindStack(returnTypeId != WasmType.VOID_TYPE ? initialStackPointer + 1 : initialStackPointer);
return Truffle.getRuntime().createLoopNode(loopBlock);
}
private WasmIfNode readIf(WasmInstance instance, WasmCodeEntry codeEntry, ExecutionState state) {
byte blockTypeId = readBlockType();
int stackSizeAfterCondition = state.stackSize();
int startOffset = offset();
WasmBlockNode trueBranchBlock = readBlockBody(instance, codeEntry, state, blockTypeId, false);
state.unwindStack(stackSizeAfterCondition);
WasmBlockNode falseBranchBlock = null;
if (peek1(-1) == Instructions.ELSE) {
falseBranchBlock = readBlockBody(instance, codeEntry, state, blockTypeId, false);
} else if (blockTypeId != WasmType.VOID_TYPE) {
Assert.fail("An if statement without an else branch block cannot return values.", Failure.UNSPECIFIED_MALFORMED);
}
int stackSizeBeforeCondition = stackSizeAfterCondition + 1;
return new WasmIfNode(instance, codeEntry, trueBranchBlock, falseBranchBlock, offset() - startOffset, blockTypeId, stackSizeBeforeCondition);
}
private void readElementSection() {
int numElements = readVectorLength();
if (moduleLimits != null) {
moduleLimits.checkElementSegmentCount(numElements);
}
for (int elemSegmentId = 0; elemSegmentId != numElements; ++elemSegmentId) {
int tableIndex = readUnsignedInt32();
Assert.assertIntEqual(tableIndex, 0, "Invalid table index", Failure.UNSPECIFIED_MALFORMED);
byte instruction = read1();
int offsetAddress = -1;
int offsetGlobalIndex = -1;
switch (instruction) {
case Instructions.I32_CONST: {
offsetAddress = readSignedInt32();
readEnd();
break;
}
case Instructions.GLOBAL_GET: {
offsetGlobalIndex = readGlobalIndex();
readEnd();
break;
}
default: {
Assert.fail(String.format("Invalid instruction for table offset expression: 0x%02X", instruction), Failure.UNSPECIFIED_MALFORMED);
}
}
int segmentLength = readVectorLength();
final SymbolTable symbolTable = module.symbolTable();
final int currentElemSegmentId = elemSegmentId;
final int currentOffsetAddress = offsetAddress;
final int currentOffsetGlobalIndex = offsetGlobalIndex;
final int[] functionIndices = new int[segmentLength];
for (int index = 0; index != segmentLength; ++index) {
final int functionIndex = readDeclaredFunctionIndex();
functionIndices[index] = functionIndex;
}
module.addLinkAction((context, instance) -> {
WasmFunction[] elements = new WasmFunction[segmentLength];
for (int index = 0; index != segmentLength; ++index) {
final int functionIndex = functionIndices[index];
final WasmFunction function = symbolTable.function(functionIndex);
elements[index] = function;
}
context.linker().resolveElemSegment(context, instance, currentElemSegmentId, currentOffsetAddress, currentOffsetGlobalIndex, segmentLength, elements);
});
}
}
private void readEnd() {
byte instruction = read1();
Assert.assertByteEqual(instruction, (byte) Instructions.END, "Initialization expression must end with an END", Failure.UNSPECIFIED_MALFORMED);
}
private void readStartSection() {
int startFunctionIndex = readDeclaredFunctionIndex();
module.symbolTable().setStartFunction(startFunctionIndex);
}
private void readExportSection() {
int numExports = readVectorLength();
if (moduleLimits != null) {
moduleLimits.checkExportCount(numExports);
}
for (int i = 0; i != numExports; ++i) {
String exportName = readName();
byte exportType = readExportType();
switch (exportType) {
case ExportIdentifier.FUNCTION: {
int functionIndex = readDeclaredFunctionIndex();
module.symbolTable().exportFunction(functionIndex, exportName);
break;
}
case ExportIdentifier.TABLE: {
int tableIndex = readTableIndex();
Assert.assertTrue(module.symbolTable().tableExists(), "No table was imported or declared, so cannot export a table", Failure.UNSPECIFIED_MALFORMED);
Assert.assertIntEqual(tableIndex, 0, "Cannot export table index different than zero (only one table per module allowed)", Failure.UNSPECIFIED_MALFORMED);
module.symbolTable().exportTable(exportName);
break;
}
case ExportIdentifier.MEMORY: {
readMemoryIndex();
module.symbolTable().exportMemory(exportName);
break;
}
case ExportIdentifier.GLOBAL: {
int index = readGlobalIndex();
module.symbolTable().exportGlobal(exportName, index);
break;
}
default: {
Assert.fail(String.format("Invalid export type identifier: 0x%02X", exportType), Failure.UNSPECIFIED_MALFORMED);
}
}
}
}
private void readGlobalSection() {
int numGlobals = readVectorLength();
if (moduleLimits != null) {
moduleLimits.checkGlobalCount(numGlobals);
}
int startingGlobalIndex = module.symbolTable().maxGlobalIndex() + 1;
for (int globalIndex = startingGlobalIndex; globalIndex != startingGlobalIndex + numGlobals; globalIndex++) {
byte type = readValueType();
byte mutability = readMutability();
long value = 0;
int existingIndex = -1;
byte instruction = read1();
final boolean isInitialized;
switch (instruction) {
case Instructions.I32_CONST:
value = readSignedInt32();
isInitialized = true;
break;
case Instructions.I64_CONST:
value = readSignedInt64();
isInitialized = true;
break;
case Instructions.F32_CONST:
value = readFloatAsInt32();
isInitialized = true;
break;
case Instructions.F64_CONST:
value = readFloatAsInt64();
isInitialized = true;
break;
case Instructions.GLOBAL_GET:
existingIndex = readGlobalIndex();
isInitialized = false;
break;
default:
throw Assert.fail(String.format("Invalid instruction for global initialization: 0x%02X", instruction), Failure.UNSPECIFIED_MALFORMED);
}
instruction = read1();
Assert.assertByteEqual(instruction, (byte) Instructions.END, "Global initialization must end with END", Failure.UNSPECIFIED_MALFORMED);
module.symbolTable().declareGlobal(globalIndex, type, mutability);
final int currentGlobalIndex = globalIndex;
final int currentExistingIndex = existingIndex;
final long currentValue = value;
module.addLinkAction((context, instance) -> {
final GlobalRegistry globals = context.globals();
final int address = instance.globalAddress(currentGlobalIndex);
if (isInitialized) {
globals.storeLong(address, currentValue);
context.linker().resolveGlobalInitialization(instance, currentGlobalIndex);
} else {
if (!module.symbolTable().importedGlobals().containsKey(currentExistingIndex)) {
Assert.fail("The initializer for global " + currentGlobalIndex + " in module '" + module.name() +
"' refers to a non-imported global.", Failure.UNSPECIFIED_MALFORMED);
}
context.linker().resolveGlobalInitialization(context, instance, currentGlobalIndex, currentExistingIndex);
}
});
}
}
private void readDataSection(WasmContext linkedContext, WasmInstance linkedInstance) {
int numDataSegments = readVectorLength();
if (moduleLimits != null) {
moduleLimits.checkDataSegmentCount(numDataSegments);
}
for (int dataSegmentId = 0; dataSegmentId != numDataSegments; ++dataSegmentId) {
int memIndex = readUnsignedInt32();
Assert.assertIntEqual(memIndex, 0, "Invalid memory index, only the memory index 0 is currently supported.", Failure.UNSPECIFIED_MALFORMED);
byte instruction = read1();
int offsetAddress = -1;
int offsetGlobalIndex = -1;
switch (instruction) {
case Instructions.I32_CONST:
offsetAddress = readSignedInt32();
readEnd();
break;
case Instructions.GLOBAL_GET:
offsetGlobalIndex = readGlobalIndex();
readEnd();
break;
default:
Assert.fail(String.format("Invalid instruction for data offset expression: 0x%02X", instruction), Failure.UNSPECIFIED_MALFORMED);
}
int byteLength = readVectorLength();
if (linkedInstance != null) {
if (offsetGlobalIndex != -1) {
int offsetGlobalAddress = linkedInstance.globalAddress(offsetGlobalIndex);
offsetAddress = linkedContext.globals().loadAsInt(offsetGlobalAddress);
}
final WasmMemory memory = linkedInstance.memory();
for (int writeOffset = 0; writeOffset != byteLength; ++writeOffset) {
byte b = read1();
memory.store_i32_8(null, offsetAddress + writeOffset, b);
}
} else {
byte[] dataSegment = new byte[byteLength];
for (int writeOffset = 0; writeOffset != byteLength; ++writeOffset) {
byte b = read1();
dataSegment[writeOffset] = b;
}
final int currentDataSegmentId = dataSegmentId;
final int currentOffsetAddress = offsetAddress;
final int currentOffsetGlobalIndex = offsetGlobalIndex;
module.addLinkAction((context, instance) -> context.linker().resolveDataSegment(context, instance, currentDataSegmentId, currentOffsetAddress, currentOffsetGlobalIndex, byteLength,
dataSegment));
}
}
}
private void readFunctionType() {
int paramsLength = readVectorLength();
int resultLength = peekUnsignedInt32(paramsLength, true);
resultLength = (resultLength == 0x40) ? 0 : resultLength;
if (moduleLimits != null) {
moduleLimits.checkParamCount(paramsLength);
moduleLimits.checkReturnCount(resultLength);
}
int idx = module.symbolTable().allocateFunctionType(paramsLength, resultLength);
readParameterList(idx, paramsLength);
readResultList(idx);
}
private void readParameterList(int funcTypeIdx, int numParams) {
for (int paramIdx = 0; paramIdx != numParams; ++paramIdx) {
byte type = readValueType();
module.symbolTable().registerFunctionTypeParameterType(funcTypeIdx, paramIdx, type);
}
}
private void readResultList(int funcTypeIdx) {
byte b = read1();
switch (b) {
case WasmType.VOID_TYPE:
break;
case 0x00:
break;
case 0x01:
byte type = readValueType();
module.symbolTable().registerFunctionTypeReturnType(funcTypeIdx, 0, type);
break;
default:
Assert.fail(String.format("Invalid return value specifier: 0x%02X", b), Failure.UNSPECIFIED_MALFORMED);
}
}
private boolean isEOF() {
return offset == data.length;
}
private int readVectorLength() {
return readUnsignedInt32();
}
private int readDeclaredFunctionIndex() {
final int index = readUnsignedInt32();
module.symbolTable().checkFunctionIndex(index);
return index;
}
private int readTypeIndex() {
return readUnsignedInt32();
}
private int readFunctionIndex() {
return readUnsignedInt32();
}
private int readTableIndex() {
return readUnsignedInt32();
}
private int readMemoryIndex() {
return readUnsignedInt32();
}
private int readGlobalIndex() {
return readUnsignedInt32();
}
private int readLocalIndex() {
return readUnsignedInt32();
}
private int readTargetOffset() {
return readUnsignedInt32();
}
private byte readExportType() {
return read1();
}
private byte readImportType() {
return read1();
}
private byte readElemType() {
return read1();
}
private void readTableLimits(int[] out) {
long upperBound = (moduleLimits == null) ? TABLE_MAX_SIZE : moduleLimits.getTableSizeLimit();
readLimits(upperBound, "initial table size", "max table size", out);
}
private void readMemoryLimits(int[] out) {
long upperBound = (moduleLimits == null) ? MEMORY_MAX_PAGES : moduleLimits.getMemorySizeLimit();
readLimits(upperBound, "initial memory size", "max memory size", out);
}
private void readLimits(long upperBound, String minName, String maxName, int[] out) {
byte limitsPrefix = readLimitsPrefix();
switch (limitsPrefix) {
case LimitsPrefix.NO_MAX: {
out[0] = readUnsignedInt32();
out[1] = -1;
break;
}
case LimitsPrefix.WITH_MAX: {
out[0] = readUnsignedInt32();
out[1] = readUnsignedInt32();
break;
}
default:
Assert.fail(String.format("Invalid limits prefix (expected 0x00 or 0x01, got 0x%02X", limitsPrefix), Failure.UNSPECIFIED_MALFORMED);
}
long longMin = unsignedInt32ToLong(out[0]);
long longMax = unsignedInt32ToLong(out[1]);
Assert.assertLongLessOrEqual(longMin, upperBound, "Invalid " + minName + ", must be less than upper bound", Failure.UNSPECIFIED_MALFORMED);
if (out[1] != -1) {
Assert.assertLongLessOrEqual(longMax, upperBound, "Invalid " + maxName + ", must be less than upper bound", Failure.UNSPECIFIED_MALFORMED);
Assert.assertLongLessOrEqual(longMin, longMax, "Invalid " + minName + ", must be less than " + maxName, Failure.UNSPECIFIED_MALFORMED);
}
}
private byte readLimitsPrefix() {
return read1();
}
private String readName() {
int nameLength = readVectorLength();
int afterNameOffset = nameLength + offset;
if (afterNameOffset < 0 || data.length < afterNameOffset) {
throw WasmException.format(Failure.UNSPECIFIED_MALFORMED, "The binary is truncated at: %d", data.length);
}
CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder();
decoder.onMalformedInput(CodingErrorAction.REPORT);
decoder.onUnmappableCharacter(CodingErrorAction.REPORT);
CharBuffer result;
try {
result = decoder.decode(ByteBuffer.wrap(data, offset, nameLength));
} catch (CharacterCodingException ex) {
throw WasmException.format(Failure.UNSPECIFIED_MALFORMED, "Invalid UTF-8 encoding of the name at: %d", offset);
}
offset += nameLength;
return result.toString();
}
protected int readUnsignedInt32() {
int value = peekUnsignedInt32(data, offset, true);
byte length = peekLeb128Length(data, offset);
offset += length;
return value;
}
protected int readSignedInt32() {
long valueAndLength = peekSignedInt32AndLength(data, offset);
int length = (int) ((valueAndLength >>> 32) & 0xffff_ffffL);
offset += length;
return (int) (valueAndLength & 0xffff_ffffL);
}
private long readSignedInt64() {
long value = peekSignedInt64(data, offset);
byte length = peekLeb128Length(data, offset);
offset += length;
return value;
}
private boolean tryJumpToSection(int targetSectionId) {
offset = 0;
validateMagicNumberAndVersion();
while (!isEOF()) {
byte sectionID = read1();
int size = readUnsignedInt32();
if (sectionID == targetSectionId) {
return true;
}
offset += size;
}
return false;
}
@SuppressWarnings("unused")
public void resetGlobalState(WasmContext context, WasmInstance instance) {
int globalIndex = 0;
if (tryJumpToSection(Section.IMPORT)) {
int numImports = readVectorLength();
for (int i = 0; i != numImports; ++i) {
String moduleName = readName();
String memberName = readName();
byte importType = readImportType();
switch (importType) {
case ImportIdentifier.FUNCTION: {
readTableIndex();
break;
}
case ImportIdentifier.TABLE: {
readElemType();
readTableLimits(limitsResult);
break;
}
case ImportIdentifier.MEMORY: {
readMemoryLimits(limitsResult);
break;
}
case ImportIdentifier.GLOBAL: {
readValueType();
byte mutability = readMutability();
if (mutability == GlobalModifier.MUTABLE) {
throw WasmException.create(Failure.UNSPECIFIED_UNLINKABLE, "Cannot reset imports of mutable global variables (not implemented).");
}
globalIndex++;
break;
}
default: {
}
}
}
}
if (tryJumpToSection(Section.GLOBAL)) {
final GlobalRegistry globals = context.globals();
int numGlobals = readVectorLength();
int startingGlobalIndex = globalIndex;
for (; globalIndex != startingGlobalIndex + numGlobals; globalIndex++) {
readValueType();
read1();
byte instruction = read1();
long value = 0;
switch (instruction) {
case Instructions.I32_CONST: {
value = readSignedInt32();
break;
}
case Instructions.I64_CONST: {
value = readSignedInt64();
break;
}
case Instructions.F32_CONST: {
value = readFloatAsInt32();
break;
}
case Instructions.F64_CONST: {
value = readFloatAsInt64();
break;
}
case Instructions.GLOBAL_GET: {
int existingIndex = readGlobalIndex();
if (module.symbolTable().globalMutability(existingIndex) == GlobalModifier.MUTABLE) {
throw WasmException.create(Failure.UNSPECIFIED_UNLINKABLE, "Cannot reset global variables that were initialized " +
"with a non-constant global variable (not implemented).");
}
final int existingAddress = instance.globalAddress(existingIndex);
value = globals.loadAsLong(existingAddress);
break;
}
}
read1();
final int address = instance.globalAddress(globalIndex);
globals.storeLong(address, value);
}
}
}
public void resetMemoryState(WasmContext context, WasmInstance instance) {
if (tryJumpToSection(Section.DATA)) {
readDataSection(context, instance);
}
}
}