Skip to content

Commit

Permalink
Cleanup API and code in wasm module
Browse files Browse the repository at this point in the history
  • Loading branch information
electrum committed Aug 18, 2024
1 parent f9cf28b commit bdcc7f2
Show file tree
Hide file tree
Showing 48 changed files with 259 additions and 269 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
* 19 local.get 1)
* </pre>
*/
public class ControlTree {
final class ControlTree {
private final Instruction instruction;
private final int initialInstructionNumber;
private int finalInstructionNumber = -1; // to be set when END is reached
Expand Down
2 changes: 2 additions & 0 deletions wasm/src/main/java/com/dylibso/chicory/wasm/Encoding.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ public final class Encoding {
public static final int MAX_VARINT_LEN_32 = 5; // ceil(32/7)
public static final int MAX_VARINT_LEN_64 = 10; // ceil(64/7)

private Encoding() {}

/**
* Reads an unsigned integer from {@code byteBuffer}.
*/
Expand Down
4 changes: 2 additions & 2 deletions wasm/src/main/java/com/dylibso/chicory/wasm/Module.java
Original file line number Diff line number Diff line change
Expand Up @@ -151,8 +151,8 @@ public static class Builder {
private CodeSection codeSection = CodeSection.builder().build();
private DataSection dataSection = DataSection.builder().build();
private Optional<DataCountSection> dataCountSection = Optional.empty();
private HashMap<String, CustomSection> customSections = new HashMap<>();
private List<Integer> ignoredSections = new ArrayList<>();
private final HashMap<String, CustomSection> customSections = new HashMap<>();
private final List<Integer> ignoredSections = new ArrayList<>();
private boolean validate = true;

private Builder() {}
Expand Down
72 changes: 34 additions & 38 deletions wasm/src/main/java/com/dylibso/chicory/wasm/Parser.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package com.dylibso.chicory.wasm;

import static com.dylibso.chicory.wasm.Encoding.MAX_VARINT_LEN_32;
import static com.dylibso.chicory.wasm.Encoding.readSigned32Leb128;
import static com.dylibso.chicory.wasm.Encoding.readSigned64Leb128;
import static com.dylibso.chicory.wasm.Encoding.readUnsignedLeb128;
import static com.dylibso.chicory.wasm.WasmLimits.MAX_FUNCTION_LOCALS;
import static java.util.Objects.requireNonNull;

Expand Down Expand Up @@ -68,6 +71,7 @@
/**
* Parser for Web Assembly binaries.
*/
@SuppressWarnings("UnnecessaryCodeBlock")
public final class Parser {

private static final int MAGIC_BYTES = 1836278016; // Magic prefix \0asm
Expand All @@ -89,7 +93,7 @@ public Parser(
this.customParsers = Map.copyOf(customParsers);
}

private ByteBuffer readByteBuffer(InputStream is) {
private static ByteBuffer readByteBuffer(InputStream is) {
try {
var buffer = ByteBuffer.wrap(is.readAllBytes());
buffer.order(ByteOrder.LITTLE_ENDIAN);
Expand Down Expand Up @@ -152,15 +156,15 @@ public static Module parse(InputStream input) {
}

public static Module parse(ByteBuffer buffer) {
return new Parser().parse(buffer.array());
return parse(buffer.array());
}

public static Module parse(byte[] buffer) {
return new Parser().parse(() -> new ByteArrayInputStream(buffer));
}

public static Module parse(File file) {
return new Parser().parse(file.toPath());
return parse(file.toPath());
}

public static Module parse(Path path) {
Expand All @@ -178,7 +182,7 @@ public static Module parse(Path path) {

public Module parse(Supplier<InputStream> inputStreamSupplier) {
Module.Builder moduleBuilder = Module.builder();
try (final InputStream is = inputStreamSupplier.get()) {
try (InputStream is = inputStreamSupplier.get()) {
parse(is, (s) -> onSection(moduleBuilder, s));
} catch (IOException e) {
throw new ChicoryException(e);
Expand Down Expand Up @@ -214,7 +218,7 @@ private static void readBytes(ByteBuffer buffer, byte[] dest) {

// https://webassembly.github.io/spec/core/binary/modules.html#binary-module
private static class SectionsValidator {
private boolean hasStart = false;
private boolean hasStart;

SectionsValidator() {}

Expand Down Expand Up @@ -657,16 +661,10 @@ private static Element parseSingleElement(ByteBuffer buffer) {
type = ValueType.FuncRef;
} else if (hasElemKind) {
int ek = (int) readVarUInt32(buffer);
switch (ek) {
case 0x00:
{
type = ValueType.FuncRef;
break;
}
default:
{
throw new ChicoryException("Invalid element kind");
}
if (ek == 0x00) {
type = ValueType.FuncRef;
} else {
throw new ChicoryException("Invalid element kind");
}
} else {
assert hasRefType;
Expand All @@ -691,12 +689,12 @@ private static Element parseSingleElement(ByteBuffer buffer) {
}
if (declarative) {
return new DeclarativeElement(type, inits);
} else if (passive) {
}
if (passive) {
return new PassiveElement(type, inits);
} else {
assert active;
return new ActiveElement(type, inits, tableIdx, offset);
}
assert active;
return new ActiveElement(type, inits, tableIdx, offset);
}

private static List<ValueType> parseCodeSectionLocalTypes(ByteBuffer buffer) {
Expand Down Expand Up @@ -753,14 +751,16 @@ private static CodeSection parseCodeSection(ByteBuffer buffer) {
case LOOP:
case IF:
{
instruction.setDepth(++depth);
depth++;
instruction.setDepth(depth);
blockScope.push(instruction);
instruction.setScope(blockScope.peek());
break;
}
case END:
{
instruction.setDepth(depth--);
instruction.setDepth(depth);
depth--;
instruction.setScope(
blockScope.isEmpty() ? instruction : blockScope.pop());
break;
Expand Down Expand Up @@ -813,6 +813,7 @@ private static CodeSection parseCodeSection(ByteBuffer buffer) {
{
instruction.setLabelFalse(instructions.size() + 1);
}
// fallthrough
case BR:
{
var offset = (int) instruction.operands()[0];
Expand Down Expand Up @@ -970,7 +971,9 @@ private static Instruction parseInstruction(ByteBuffer buffer) {
}
}
var operandsArray = new long[operands.size()];
for (var i = 0; i < operands.size(); i++) operandsArray[i] = operands.get(i);
for (var i = 0; i < operands.size(); i++) {
operandsArray[i] = operands.get(i);
}
verifyAlignment(op, operandsArray);
return new Instruction(address, op, operandsArray);
}
Expand Down Expand Up @@ -1010,7 +1013,7 @@ private static void verifyAlignment(OpCode op, long[] operands) {
align = 64;
break;
}
if (align > 0 && !(Math.pow(2, operands[0]) <= align / 8)) {
if ((align > 0) && ((1 << operands[0]) > (align >> 3))) {
throw new InvalidException(
"alignment must not be larger than natural alignment (" + operands[0] + ")");
}
Expand All @@ -1030,12 +1033,9 @@ private static Instruction[] parseExpression(ByteBuffer buffer) {
}

// https://webassembly.github.io/spec/core/syntax/values.html#integers
public static final long MIN_SIGNED_INT = -2147483648l; // -2^(32-1)
public static final long MAX_SIGNED_INT = 2147483647l; // 2^(32-1)-1
public static final long MIN_UNSIGNED_INT = 0l;
public static final long MAX_UNSIGNED_INT = 0xFFFFFFFFl; // 2^(32)-1
public static final long MIN_SIGNED_LONG = -0x8000000000000000l; // -2^(64-1)
public static final long MAX_SIGNED_LONG = 0x7FFFFFFFFFFFFFFFl; // 2^(64-1)-1
public static final long MIN_SIGNED_INT = Integer.MIN_VALUE; // -2^(32-1)
public static final long MAX_SIGNED_INT = Integer.MAX_VALUE; // 2^(32-1)-1
public static final long MAX_UNSIGNED_INT = 0xFFFFFFFFL; // 2^(32)-1

/**
* Read an unsigned I32 from the buffer. We can't fit an unsigned 32bit int
Expand All @@ -1046,8 +1046,8 @@ private static Instruction[] parseExpression(ByteBuffer buffer) {
* @return the resulting long
*/
public static long readVarUInt32(ByteBuffer buffer) {
var value = Encoding.readUnsignedLeb128(buffer, MAX_VARINT_LEN_32);
if (value < MIN_UNSIGNED_INT || value > MAX_UNSIGNED_INT) {
var value = readUnsignedLeb128(buffer, MAX_VARINT_LEN_32);
if (value < 0 || value > MAX_UNSIGNED_INT) {
throw new MalformedException("integer too large");
}
return value;
Expand All @@ -1061,8 +1061,8 @@ public static long readVarUInt32(ByteBuffer buffer) {
* @return the resulting long
*/
public static long readVarSInt32(ByteBuffer buffer) {
var value = Encoding.readSigned32Leb128(buffer);
if (value > MAX_SIGNED_INT || value < MIN_SIGNED_INT) {
var value = readSigned32Leb128(buffer);
if (value < MIN_SIGNED_INT || value > MAX_SIGNED_INT) {
throw new MalformedException("integer too large");
}
return value;
Expand All @@ -1076,11 +1076,7 @@ public static long readVarSInt32(ByteBuffer buffer) {
* @return the resulting long
*/
public static long readVarSInt64(ByteBuffer buffer) {
var value = Encoding.readSigned64Leb128(buffer);
if (value > MAX_SIGNED_LONG || value < MIN_SIGNED_LONG) {
throw new MalformedException("integer too large");
}
return value;
return readSigned64Leb128(buffer);
}

/**
Expand Down
65 changes: 30 additions & 35 deletions wasm/src/main/java/com/dylibso/chicory/wasm/Validator.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.dylibso.chicory.wasm;

import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toSet;

Expand All @@ -24,7 +25,6 @@
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

// Heavily inspired by wazero
Expand All @@ -33,27 +33,28 @@
// https://webassembly.github.io/spec/core/appendix/algorithm.html
final class Validator {

private boolean isNum(ValueType t) {
private static boolean isNum(ValueType t) {
return t.isNumeric() || t == ValueType.UNKNOWN;
}

private boolean isRef(ValueType t) {
private static boolean isRef(ValueType t) {
return t.isReference() || t == ValueType.UNKNOWN;
}

@SuppressWarnings("PublicField")
private static class CtrlFrame {
// OpCode of the current Control Flow instruction
private final OpCode opCode;
public final OpCode opCode;
// params or inputs
private final List<ValueType> startTypes;
public final List<ValueType> startTypes;
// returns or outputs
private final List<ValueType> endTypes;
public final List<ValueType> endTypes;
// the height of the stack before entering the current Control Flow instruction
private final int height;
public final int height;
// set after uncoditional jumps
private boolean unreachable;
public boolean unreachable;
// if there is no else, we explicit check that the enclosing IF is not returning values
private boolean hasElse;
public boolean hasElse;

public CtrlFrame(
OpCode opCode,
Expand Down Expand Up @@ -206,12 +207,8 @@ private CtrlFrame getCtrl(int n) {
return ctrlFrameStack.get(ctrlFrameStack.size() - 1 - n);
}

private List<ValueType> labelTypes(CtrlFrame frame) {
if (frame.opCode == OpCode.LOOP) {
return frame.startTypes;
} else {
return frame.endTypes;
}
private static List<ValueType> labelTypes(CtrlFrame frame) {
return (frame.opCode == OpCode.LOOP) ? frame.startTypes : frame.endTypes;
}

private void resetAtStackLimit() {
Expand Down Expand Up @@ -243,25 +240,25 @@ private List<ValueType> getReturns(Instruction op) {
var typeId = (int) op.operands()[0];
if (typeId == 0x40) { // epsilon
return List.of();
} else if (ValueType.isValid(typeId)) {
}
if (ValueType.isValid(typeId)) {
return List.of(ValueType.forId(typeId));
} else {
return getType(typeId).returns();
}
return getType(typeId).returns();
}

private List<ValueType> getParams(Instruction op) {
var typeId = (int) op.operands()[0];
if (typeId == 0x40) { // epsilon
return List.of();
} else if (ValueType.isValid(typeId)) {
}
if (ValueType.isValid(typeId)) {
return List.of();
} else {
if (typeId >= module.typeSection().typeCount()) {
throw new MalformedException("unexpected end");
}
return getType(typeId).params();
}
if (typeId >= module.typeSection().typeCount()) {
throw new MalformedException("unexpected end");
}
return getType(typeId).params();
}

private static ValueType getLocalType(List<ValueType> localTypes, int idx) {
Expand Down Expand Up @@ -348,6 +345,7 @@ public void validateFunctions() {
}
}

@SuppressWarnings("UnnecessaryCodeBlock")
public void validateFunction(int funcIdx, FunctionBody body, FunctionType functionType) {
var localTypes = body.localTypes();
var inputLen = functionType.params().size();
Expand All @@ -359,16 +357,13 @@ public void validateFunction(int funcIdx, FunctionBody body, FunctionType functi
// control flow instructions handling
switch (op.opcode()) {
case UNREACHABLE:
{
unreachable();
break;
}
unreachable();
break;
case IF:
{
popVal(ValueType.I32);
// fallthrough
}
case LOOP: // t1* -> t2*
popVal(ValueType.I32);
// fallthrough
case LOOP:
// t1* -> t2*
// fallthrough
case BLOCK:
{
Expand Down Expand Up @@ -432,7 +427,7 @@ public void validateFunction(int funcIdx, FunctionBody body, FunctionType functi
var arity = defaultBranchLabelTypes.size();
for (var idx = 0; idx < op.operands().length - 1; idx++) {
var n = (int) op.operands()[idx];
CtrlFrame ctrlFrame = null;
CtrlFrame ctrlFrame;
try {
ctrlFrame = getCtrl(n);
} catch (IndexOutOfBoundsException e) {
Expand Down Expand Up @@ -1072,7 +1067,7 @@ public void validateFunction(int funcIdx, FunctionBody body, FunctionType functi

if (!errors.isEmpty()) {
throw new InvalidException(
errors.stream().map(e -> e.getMessage()).collect(Collectors.joining(" - ")));
errors.stream().map(Throwable::getMessage).collect(joining(" - ")));
}

// to satisfy the check mentioned in the NOTE
Expand Down
4 changes: 3 additions & 1 deletion wasm/src/main/java/com/dylibso/chicory/wasm/WasmLimits.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

// Spec: https://webassembly.github.io/spec/core/appendix/implementation.html#syntactic-limits
// From: https://github.com/WebKit/webkit/blob/main/Source/JavaScriptCore/wasm/WasmLimits.h
public class WasmLimits {
public final class WasmLimits {

public static final int MAX_TYPES = 1000000;
public static final int MAX_FUNCTIONS = 1000000;
Expand All @@ -27,4 +27,6 @@ public class WasmLimits {

public static final int MAX_TABLE_ENTRIES = 10000000;
public static final int MAX_TABLES = 1000000;

private WasmLimits() {}
}
Loading

0 comments on commit bdcc7f2

Please sign in to comment.