wasm gc: improve disassembler to print type section

This commit is contained in:
Alexey Andreev 2024-08-27 21:11:34 +02:00
parent 7324e99e6a
commit 5a513fd6fd
11 changed files with 846 additions and 267 deletions

View File

@ -0,0 +1,117 @@
/*
* Copyright 2024 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.backend.wasm.disasm;
import org.teavm.backend.wasm.parser.AddressListener;
import org.teavm.backend.wasm.parser.WasmHollowStorageType;
import org.teavm.backend.wasm.parser.WasmHollowType;
public abstract class BaseDisassemblyListener implements AddressListener {
protected final DisassemblyWriter writer;
protected int address;
private int addressOffset;
public BaseDisassemblyListener(DisassemblyWriter writer) {
this.writer = writer;
}
public void setAddressOffset(int addressOffset) {
this.addressOffset = addressOffset;
}
@Override
public void address(int address) {
this.address = address + addressOffset;
}
protected void writeBlockType(WasmHollowType type) {
if (type != null) {
writer.write(" ");
writeType(type);
}
}
protected void writeType(WasmHollowStorageType type) {
if (type instanceof WasmHollowStorageType.Packed) {
switch (((WasmHollowStorageType.Packed) type).type) {
case INT8:
writer.write("i8");
return;
case INT16:
writer.write("i16");
return;
}
} else {
writeType(type.asUnpackedType());
}
}
protected void writeType(WasmHollowType type) {
if (type != null) {
if (type instanceof WasmHollowType.Number) {
switch (((WasmHollowType.Number) type).number) {
case INT32:
writer.write("i32");
return;
case INT64:
writer.write("i64");
return;
case FLOAT32:
writer.write("f32");
return;
case FLOAT64:
writer.write("f64");
return;
default:
break;
}
} else if (type instanceof WasmHollowType.SpecialReference) {
switch (((WasmHollowType.SpecialReference) type).kind) {
case ANY:
writer.write("anyref");
return;
case FUNC:
writer.write("funcref");
return;
case ARRAY:
writer.write("arrayref");
return;
case EXTERN:
writer.write("externref");
return;
case STRUCT:
writer.write("structref");
return;
case I31:
writer.write("i31ref");
return;
default:
throw new IllegalArgumentException();
}
} else if (type instanceof WasmHollowType.CompositeReference) {
writer.write("(ref null ");
writeTypeId(((WasmHollowType.CompositeReference) type).index);
writer.write(")");
return;
}
}
writer.write("unknown");
}
protected void writeTypeId(int index) {
writer.write(String.valueOf(index));
}
}

View File

@ -0,0 +1,97 @@
/*
* Copyright 2024 konsoletyper.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.backend.wasm.disasm;
import java.io.File;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.Writer;
import java.nio.file.Files;
import java.util.function.Consumer;
import org.teavm.backend.wasm.parser.CodeSectionParser;
import org.teavm.backend.wasm.parser.ModuleParser;
import org.teavm.backend.wasm.parser.TypeSectionParser;
import org.teavm.common.AsyncInputStream;
import org.teavm.common.ByteArrayAsyncInputStream;
public final class Disassembler {
private PrintWriter out;
private DisassemblyWriter writer;
public Disassembler(Writer writer) {
out = new PrintWriter(writer);
this.writer = new DisassemblyWriter(out, true);
}
public void startModule() {
writer.write("(module").indent().eol();
}
public void endModule() {
writer.write(")").eol();
}
public void disassemble(byte[] bytes) {
startModule();
read(bytes);
endModule();
}
public void read(byte[] bytes) {
var input = new ByteArrayAsyncInputStream(bytes);
var parser = createParser(input);
input.readFully(parser::parse);
}
public ModuleParser createParser(AsyncInputStream input) {
return new ModuleParser(input) {
@Override
protected Consumer<byte[]> getSectionConsumer(int code, int pos, String name) {
return Disassembler.this.getSectionConsumer(code, pos, name);
}
};
}
public Consumer<byte[]> getSectionConsumer(int code, int pos, String name) {
if (code == 1) {
return bytes -> {
var disassembler = new DisassemblyTypeSectionListener(writer);
disassembler.setAddressOffset(pos);
var sectionParser = new TypeSectionParser(disassembler, disassembler);
sectionParser.parse(bytes);
out.flush();
};
} else if (code == 10) {
return bytes -> {
var disassembler = new DisassemblyCodeSectionListener(writer);
disassembler.setAddressOffset(pos);
var sectionParser = new CodeSectionParser(disassembler, disassembler);
sectionParser.parse(bytes);
out.flush();
};
} else {
return null;
}
}
public static void main(String[] args) throws IOException {
var file = new File(args[0]);
var bytes = Files.readAllBytes(file.toPath());
var disassembler = new Disassembler(new OutputStreamWriter(System.out));
disassembler.disassemble(bytes);
}
}

View File

@ -15,11 +15,6 @@
*/ */
package org.teavm.backend.wasm.disasm; package org.teavm.backend.wasm.disasm;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.file.Files;
import java.util.function.Consumer;
import org.teavm.backend.wasm.model.WasmNumType; import org.teavm.backend.wasm.model.WasmNumType;
import org.teavm.backend.wasm.model.expression.WasmFloatBinaryOperation; import org.teavm.backend.wasm.model.expression.WasmFloatBinaryOperation;
import org.teavm.backend.wasm.model.expression.WasmFloatType; import org.teavm.backend.wasm.model.expression.WasmFloatType;
@ -34,35 +29,16 @@ import org.teavm.backend.wasm.parser.AddressListener;
import org.teavm.backend.wasm.parser.BranchOpcode; import org.teavm.backend.wasm.parser.BranchOpcode;
import org.teavm.backend.wasm.parser.CodeListener; import org.teavm.backend.wasm.parser.CodeListener;
import org.teavm.backend.wasm.parser.CodeSectionListener; import org.teavm.backend.wasm.parser.CodeSectionListener;
import org.teavm.backend.wasm.parser.CodeSectionParser;
import org.teavm.backend.wasm.parser.LocalOpcode; import org.teavm.backend.wasm.parser.LocalOpcode;
import org.teavm.backend.wasm.parser.ModuleParser;
import org.teavm.backend.wasm.parser.Opcode; import org.teavm.backend.wasm.parser.Opcode;
import org.teavm.backend.wasm.parser.WasmHollowType; import org.teavm.backend.wasm.parser.WasmHollowType;
import org.teavm.common.ByteArrayAsyncInputStream;
public class DisassemblyCodeSectionListener implements AddressListener, CodeSectionListener, CodeListener { public class DisassemblyCodeSectionListener extends BaseDisassemblyListener implements AddressListener,
private DisassemblyWriter writer; CodeSectionListener, CodeListener {
private int address;
private int addressOffset;
private int blockIdGen; private int blockIdGen;
public DisassemblyCodeSectionListener(DisassemblyWriter writer) { public DisassemblyCodeSectionListener(DisassemblyWriter writer) {
this.writer = writer; super(writer);
}
public void setAddressOffset(int addressOffset) {
this.addressOffset = addressOffset;
}
@Override
public void address(int address) {
this.address = address + addressOffset;
}
@Override
public void sectionStart(int functionCount) {
writer.address(address).write("(; code section ;)").eol();
} }
@Override @Override
@ -80,7 +56,9 @@ public class DisassemblyCodeSectionListener implements AddressListener, CodeSect
public void local(int start, int count, WasmHollowType type) { public void local(int start, int count, WasmHollowType type) {
writer.address(address); writer.address(address);
for (int i = 0; i < count; ++i) { for (int i = 0; i < count; ++i) {
writer.write("(local (; " + (i + start) + " ;) " + typeToString(type) + ")").eol(); writer.write("(local (; " + (i + start) + " ;) ");
writeType(type);
writer.write(")").eol();
} }
} }
@ -100,52 +78,6 @@ public class DisassemblyCodeSectionListener implements AddressListener, CodeSect
writer.outdent().write(")").eol(); writer.outdent().write(")").eol();
} }
private String blockTypeToString(WasmHollowType type) {
if (type == null) {
return "";
} else {
return " " + typeToString(type);
}
}
private String typeToString(WasmHollowType type) {
if (type != null) {
if (type instanceof WasmHollowType.Number) {
switch (((WasmHollowType.Number) type).number) {
case INT32:
return "i32";
case INT64:
return "i64";
case FLOAT32:
return "f32";
case FLOAT64:
return "f64";
default:
break;
}
} else if (type instanceof WasmHollowType.SpecialReference) {
switch (((WasmHollowType.SpecialReference) type).kind) {
case ANY:
return "any";
case FUNC:
return "func";
case ARRAY:
return "array";
case EXTERN:
return "extern";
case STRUCT:
return "struct";
case I31:
return "i31";
default:
throw new IllegalArgumentException();
}
} else if (type instanceof WasmHollowType.CompositeReference) {
return String.valueOf(((WasmHollowType.CompositeReference) type).index);
}
}
return "unknown";
}
@Override @Override
public void error(int depth) { public void error(int depth) {
@ -160,8 +92,9 @@ public class DisassemblyCodeSectionListener implements AddressListener, CodeSect
public int startBlock(boolean loop, WasmHollowType type) { public int startBlock(boolean loop, WasmHollowType type) {
writer.address(address); writer.address(address);
var label = blockIdGen++; var label = blockIdGen++;
writer.write(loop ? "loop" : "block").write(" $label_" + label).write(blockTypeToString(type)) writer.write(loop ? "loop" : "block").write(" $label_" + label);
.indent().eol(); writeBlockType(type);
writer.indent().eol();
return label; return label;
} }
@ -169,7 +102,9 @@ public class DisassemblyCodeSectionListener implements AddressListener, CodeSect
public int startConditionalBlock(WasmHollowType type) { public int startConditionalBlock(WasmHollowType type) {
writer.address(address); writer.address(address);
var label = blockIdGen++; var label = blockIdGen++;
writer.write("if ").write(" $label_" + label).write(blockTypeToString(type)).indent().eol(); writer.write("if ").write(" $label_" + label);
writeBlockType(type);
writer.indent().eol();
return label; return label;
} }
@ -183,7 +118,9 @@ public class DisassemblyCodeSectionListener implements AddressListener, CodeSect
public int startTry(WasmHollowType type) { public int startTry(WasmHollowType type) {
writer.address(address); writer.address(address);
var label = blockIdGen++; var label = blockIdGen++;
writer.write("try ").write(" $label_" + label).write(blockTypeToString(type)).indent().eol(); writer.write("try ").write(" $label_" + label);
writeBlockType(type);
writer.indent().eol();
return label; return label;
} }
@ -827,7 +764,9 @@ public class DisassemblyCodeSectionListener implements AddressListener, CodeSect
@Override @Override
public void nullConstant(WasmHollowType.Reference type) { public void nullConstant(WasmHollowType.Reference type) {
writer.address(address).write("ref.null ").write(typeToString(type)).eol(); writer.address(address).write("ref.null ");
writeType(type);
writer.eol();
} }
@Override @Override
@ -836,7 +775,8 @@ public class DisassemblyCodeSectionListener implements AddressListener, CodeSect
if (!nullable) { if (!nullable) {
writer.write("null "); writer.write("null ");
} }
writer.write(typeToString(type)).write(")").eol(); writeType(type);
writer.write(")").eol();
} }
@Override @Override
@ -845,7 +785,8 @@ public class DisassemblyCodeSectionListener implements AddressListener, CodeSect
if (!nullable) { if (!nullable) {
writer.write("null "); writer.write("null ");
} }
writer.write(typeToString(type)).write(")").eol(); writeType(type);
writer.write(")").eol();
} }
@Override @Override
@ -914,28 +855,4 @@ public class DisassemblyCodeSectionListener implements AddressListener, CodeSect
public void int31Get(WasmSignedType signedType) { public void int31Get(WasmSignedType signedType) {
writer.address(address).write("ref.i31_").write(signedType == WasmSignedType.SIGNED ? "s" : "u").eol(); writer.address(address).write("ref.i31_").write(signedType == WasmSignedType.SIGNED ? "s" : "u").eol();
} }
public static void main(String[] args) throws IOException {
var file = new File(args[0]);
var bytes = Files.readAllBytes(file.toPath());
var input = new ByteArrayAsyncInputStream(bytes);
var parser = new ModuleParser(input) {
@Override
protected Consumer<byte[]> getSectionConsumer(int code, int pos, String name) {
if (code == 10) {
return bytes -> {
var out = new PrintWriter(System.out);
var writer = new DisassemblyWriter(out, true);
var disassembler = new DisassemblyCodeSectionListener(writer);
disassembler.setAddressOffset(pos);
var sectionParser = new CodeSectionParser(disassembler, disassembler);
sectionParser.parse(bytes);
out.flush();
};
}
return null;
}
};
input.readFully(parser::parse);
}
} }

View File

@ -0,0 +1,130 @@
/*
* Copyright 2024 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.backend.wasm.disasm;
import org.teavm.backend.wasm.parser.AddressListener;
import org.teavm.backend.wasm.parser.TypeSectionListener;
import org.teavm.backend.wasm.parser.WasmHollowStorageType;
import org.teavm.backend.wasm.parser.WasmHollowType;
public class DisassemblyTypeSectionListener extends BaseDisassemblyListener implements AddressListener,
TypeSectionListener {
private boolean currentTypeNeedsClosing;
private boolean emittingReturn;
private int fieldIndex;
private boolean needsFieldIndex;
public DisassemblyTypeSectionListener(DisassemblyWriter writer) {
super(writer);
}
@Override
public void startRecType(int count) {
writer.address(address).write("(rec ").indent().eol();
}
@Override
public void endRecType() {
writer.outdent().write(")").eol();
}
@Override
public void startType(int index, boolean open, int[] supertypes) {
writer.address(address).write("(type (; ").write(String.valueOf(index)).write(" ;) ");
if (!open || supertypes.length > 0) {
currentTypeNeedsClosing = true;
writer.write("(sub ");
if (!open) {
writer.write("final");
}
for (var supertype : supertypes) {
writer.write(supertype + " ");
}
}
writer.indent().eol();
}
@Override
public void startArrayType() {
writer.address(address).write("(array ").indent().eol();
}
@Override
public void endArrayType() {
writer.outdent().write(")").eol();
}
@Override
public void startStructType(int fieldCount) {
needsFieldIndex = true;
writer.address(address).write("(struct ").indent().eol();
}
@Override
public void field(WasmHollowStorageType hollowType, boolean mutable) {
writer.address(address).write("(field ");
if (needsFieldIndex) {
writer.write("(; " + fieldIndex++ + " ;) ");
}
if (mutable) {
writer.write("(mut ");
}
writeType(hollowType);
if (mutable) {
writer.write(")");
}
writer.write(")").eol();
}
@Override
public void endStructType() {
writer.outdent().write(")").eol();
fieldIndex = 0;
}
@Override
public void funcType(int paramCount) {
writer.address(address).write("(struct ").indent().eol();
}
@Override
public void funcTypeResults(int returnCount) {
emittingReturn = true;
}
@Override
public void resultType(WasmHollowType type) {
writer.address(address).write("(").write(emittingReturn ? "result" : "param").write(" ");
writeType(type);
writer.write(")").eol();
}
@Override
public void endFuncType() {
emittingReturn = false;
writer.outdent().write(")").eol();
}
@Override
public void endType() {
writer.outdent();
if (currentTypeNeedsClosing) {
writer.write(")");
currentTypeNeedsClosing = false;
}
writer.write(")").eol();
}
}

View File

@ -0,0 +1,191 @@
/*
* Copyright 2024 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.backend.wasm.parser;
public abstract class BaseSectionParser {
protected AddressListener addressListener;
protected byte[] data;
protected int ptr;
private int lastReportedPtr = -1;
public BaseSectionParser(AddressListener addressListener) {
this.addressListener = addressListener;
}
public void parse(byte[] data) {
this.data = data;
ptr = 0;
try {
parseContent();
} finally {
this.data = null;
}
}
protected abstract void parseContent();
protected WasmHollowStorageType readStorageType() {
var typeId = data[ptr];
switch (typeId) {
case 0x78:
++ptr;
return WasmHollowStorageType.INT8;
case 0x77:
++ptr;
return WasmHollowStorageType.INT16;
default:
return new WasmHollowStorageType.Regular(readType());
}
}
protected WasmHollowType readType() {
var typeId = data[ptr++];
switch (typeId) {
case 0x7F:
return WasmHollowType.INT32;
case 0x7E:
return WasmHollowType.INT64;
case 0x7D:
return WasmHollowType.FLOAT32;
case 0x7C:
return WasmHollowType.FLOAT64;
case 0x63:
return readHeapType();
case 0x40:
return null;
default:
return readAbsHeapType(typeId);
}
}
protected WasmHollowType.Reference readHeapType() {
var typeId = data[ptr];
if ((typeId & 0xC0) == 0x40) {
var result = readAbsHeapType(typeId);
++ptr;
return result;
}
return new WasmHollowType.CompositeReference(readLEB());
}
protected WasmHollowType.SpecialReference readAbsHeapType(int typeId) {
switch (typeId) {
case 0x70:
return WasmHollowType.Reference.FUNC;
case 0x6F:
return WasmHollowType.Reference.EXTERN;
case 0x6E:
return WasmHollowType.Reference.ANY;
case 0x6C:
return WasmHollowType.Reference.I31;
case 0x6B:
return WasmHollowType.Reference.STRUCT;
case 0x6A:
return WasmHollowType.Reference.ARRAY;
default:
throw new ParseException("Unknown type", ptr);
}
}
protected void reportAddress() {
if (ptr != lastReportedPtr) {
lastReportedPtr = ptr;
if (addressListener != null) {
addressListener.address(ptr);
}
}
}
protected int readSignedLEB() {
var result = 0;
var shift = 0;
while (true) {
var digit = data[ptr++];
result |= (digit & 0x7F) << shift;
if ((digit & 0x80) == 0) {
if ((digit & 0x40) != 0) {
result |= -1 << (shift + 7);
}
break;
}
shift += 7;
}
return result;
}
protected int readLEB() {
var result = 0;
var shift = 0;
while (true) {
var digit = data[ptr++];
result |= (digit & 0x7F) << shift;
if ((digit & 0x80) == 0) {
break;
}
shift += 7;
}
return result;
}
protected long readSignedLongLEB() {
var result = 0L;
var shift = 0;
while (true) {
var digit = data[ptr++];
result |= (digit & 0x7FL) << shift;
if ((digit & 0x80) == 0) {
if ((digit & 0x40) != 0) {
result |= -1L << (shift + 7);
}
break;
}
shift += 7;
}
return result;
}
protected long readLongLEB() {
var result = 0L;
var shift = 0;
while (true) {
var digit = data[ptr++];
result |= (digit & 0x7FL) << shift;
if ((digit & 0x80) == 0) {
break;
}
shift += 7;
}
return result;
}
protected int readFixedInt() {
return ((data[ptr++] & 0xFF) << 24)
| ((data[ptr++] & 0xFF) << 16)
| ((data[ptr++] & 0xFF) << 8)
| (data[ptr++] & 0xFF);
}
protected long readFixedLong() {
return ((data[ptr++] & 0xFFL) << 56)
| ((data[ptr++] & 0xFFL) << 48)
| ((data[ptr++] & 0xFFL) << 40)
| ((data[ptr++] & 0xFFL) << 32)
| ((data[ptr++] & 0xFFL) << 24)
| ((data[ptr++] & 0xFF) << 16)
| ((data[ptr++] & 0xFF) << 8)
| (data[ptr++] & 0xFF);
}
}

View File

@ -15,10 +15,7 @@
*/ */
package org.teavm.backend.wasm.parser; package org.teavm.backend.wasm.parser;
public interface CodeSectionListener { public interface CodeSectionListener extends SectionListener {
default void sectionStart(int functionCount) {
}
default boolean functionStart(int index, int size) { default boolean functionStart(int index, int size) {
return false; return false;
} }
@ -35,7 +32,4 @@ public interface CodeSectionListener {
default void functionEnd() { default void functionEnd() {
} }
default void sectionEnd() {
}
} }

View File

@ -28,31 +28,18 @@ import org.teavm.backend.wasm.model.expression.WasmIntType;
import org.teavm.backend.wasm.model.expression.WasmIntUnaryOperation; import org.teavm.backend.wasm.model.expression.WasmIntUnaryOperation;
import org.teavm.backend.wasm.model.expression.WasmSignedType; import org.teavm.backend.wasm.model.expression.WasmSignedType;
public class CodeSectionParser { public class CodeSectionParser extends BaseSectionParser {
private AddressListener addressListener;
private CodeSectionListener listener; private CodeSectionListener listener;
private byte[] data;
private int ptr;
private CodeListener codeListener; private CodeListener codeListener;
private int lastReportedPtr = -1;
private List<Block> blockStack = new ArrayList<>(); private List<Block> blockStack = new ArrayList<>();
public CodeSectionParser(AddressListener addressListener, CodeSectionListener listener) { public CodeSectionParser(AddressListener addressListener, CodeSectionListener listener) {
this.addressListener = addressListener; super(addressListener);
this.listener = listener; this.listener = listener;
} }
public void parse(byte[] data) { @Override
this.data = data; protected void parseContent() {
ptr = 0;
try {
parseFunctions();
} finally {
this.data = null;
}
}
private void parseFunctions() {
reportAddress(); reportAddress();
int count = readLEB(); int count = readLEB();
listener.sectionStart(count); listener.sectionStart(count);
@ -853,144 +840,6 @@ public class CodeSectionParser {
codeListener.tableBranch(depths, targets, defaultDepth, defaultTarget); codeListener.tableBranch(depths, targets, defaultDepth, defaultTarget);
} }
private WasmHollowType readType() {
var typeId = data[ptr++];
switch (typeId) {
case 0x7F:
return WasmHollowType.INT32;
case 0x7E:
return WasmHollowType.INT64;
case 0x7D:
return WasmHollowType.FLOAT32;
case 0x7C:
return WasmHollowType.FLOAT64;
case 0x63:
return readHeapType();
case 0x40:
return null;
default:
return readAbsHeapType(typeId);
}
}
private WasmHollowType.Reference readHeapType() {
var typeId = data[ptr];
if ((typeId & 0xC0) == 0x40) {
var result = readAbsHeapType(typeId);
++ptr;
return result;
}
return new WasmHollowType.CompositeReference(readLEB());
}
private WasmHollowType.SpecialReference readAbsHeapType(int typeId) {
switch (typeId) {
case 0x70:
return WasmHollowType.Reference.FUNC;
case 0x6F:
return WasmHollowType.Reference.EXTERN;
case 0x6E:
return WasmHollowType.Reference.ANY;
case 0x6C:
return WasmHollowType.Reference.I31;
case 0x6B:
return WasmHollowType.Reference.STRUCT;
case 0x6A:
return WasmHollowType.Reference.ARRAY;
default:
throw new ParseException("Unknown type", ptr);
}
}
private void reportAddress() {
if (ptr != lastReportedPtr) {
lastReportedPtr = ptr;
if (addressListener != null) {
addressListener.address(ptr);
}
}
}
private int readSignedLEB() {
var result = 0;
var shift = 0;
while (true) {
var digit = data[ptr++];
result |= (digit & 0x7F) << shift;
if ((digit & 0x80) == 0) {
if ((digit & 0x40) != 0) {
result |= -1 << (shift + 7);
}
break;
}
shift += 7;
}
return result;
}
private int readLEB() {
var result = 0;
var shift = 0;
while (true) {
var digit = data[ptr++];
result |= (digit & 0x7F) << shift;
if ((digit & 0x80) == 0) {
break;
}
shift += 7;
}
return result;
}
private long readSignedLongLEB() {
var result = 0L;
var shift = 0;
while (true) {
var digit = data[ptr++];
result |= (digit & 0x7FL) << shift;
if ((digit & 0x80) == 0) {
if ((digit & 0x40) != 0) {
result |= -1L << (shift + 7);
}
break;
}
shift += 7;
}
return result;
}
private long readLongLEB() {
var result = 0L;
var shift = 0;
while (true) {
var digit = data[ptr++];
result |= (digit & 0x7FL) << shift;
if ((digit & 0x80) == 0) {
break;
}
shift += 7;
}
return result;
}
private int readFixedInt() {
return ((data[ptr++] & 0xFF) << 24)
| ((data[ptr++] & 0xFF) << 16)
| ((data[ptr++] & 0xFF) << 8)
| (data[ptr++] & 0xFF);
}
private long readFixedLong() {
return ((data[ptr++] & 0xFFL) << 56)
| ((data[ptr++] & 0xFFL) << 48)
| ((data[ptr++] & 0xFFL) << 40)
| ((data[ptr++] & 0xFFL) << 32)
| ((data[ptr++] & 0xFFL) << 24)
| ((data[ptr++] & 0xFF) << 16)
| ((data[ptr++] & 0xFF) << 8)
| (data[ptr++] & 0xFF);
}
private static class Block { private static class Block {
int token; int token;

View File

@ -0,0 +1,24 @@
/*
* Copyright 2024 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.backend.wasm.parser;
public interface SectionListener {
default void sectionStart(int functionCount) {
}
default void sectionEnd() {
}
}

View File

@ -0,0 +1,57 @@
/*
* Copyright 2024 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.backend.wasm.parser;
public interface TypeSectionListener extends SectionListener {
default void startRecType(int count) {
}
default void endRecType() {
}
default void startType(int index, boolean open, int[] supertypes) {
}
default void startArrayType() {
}
default void endArrayType() {
}
default void startStructType(int fieldCount) {
}
default void field(WasmHollowStorageType hollowType, boolean mutable) {
}
default void endStructType() {
}
default void funcType(int paramCount) {
}
default void funcTypeResults(int returnCount) {
}
default void resultType(WasmHollowType type) {
}
default void endFuncType() {
}
default void endType() {
}
}

View File

@ -0,0 +1,138 @@
/*
* Copyright 2024 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.backend.wasm.parser;
public class TypeSectionParser extends BaseSectionParser {
private TypeSectionListener listener;
private int typeIndex;
public TypeSectionParser(AddressListener addressListener, TypeSectionListener listener) {
super(addressListener);
this.listener = listener;
}
@Override
protected void parseContent() {
reportAddress();
int count = readLEB();
listener.sectionStart(count);
for (var i = 0; i < count; ++i) {
parseType();
}
reportAddress();
listener.sectionEnd();
typeIndex = 0;
}
private void parseType() {
if (data[ptr] == 0x4E) {
parseRecType();
} else {
parseSubtype();
}
}
private void parseRecType() {
reportAddress();
++ptr;
var count = readLEB();
listener.startRecType(count);
for (var i = 0; i < count; ++i) {
parseSubtype();
}
listener.endRecType();
}
private void parseSubtype() {
switch (data[ptr]) {
case 0x50:
reportAddress();
++ptr;
parseCompositeType(true, readSupertypes());
break;
case 0x4F:
reportAddress();
++ptr;
parseCompositeType(false, readSupertypes());
break;
default:
parseCompositeType(true, new int[0]);
break;
}
}
private void parseCompositeType(boolean open, int[] supertypes) {
reportAddress();
listener.startType(typeIndex++, open, supertypes);
switch (data[ptr]) {
case 0x5E:
reportAddress();
++ptr;
listener.startArrayType();
parseField();
listener.endArrayType();
break;
case 0x5F: {
reportAddress();
++ptr;
var fieldCount = readLEB();
listener.startStructType(fieldCount);
for (var i = 0; i < fieldCount; ++i) {
parseField();
}
listener.endStructType();
break;
}
case 0x60: {
reportAddress();
++ptr;
var paramCount = readLEB();
listener.funcType(paramCount);
for (var i = 0; i < paramCount; ++i) {
reportAddress();
listener.resultType(readType());
}
var resultCount = readLEB();
listener.funcTypeResults(resultCount);
for (var i = 0; i < resultCount; ++i) {
reportAddress();
listener.resultType(readType());
}
listener.endFuncType();
break;
}
default:
throw new ParseException("Unknown type declaration", ptr);
}
listener.endType();
}
private void parseField() {
reportAddress();
var type = readStorageType();
var mutable = data[ptr++] != 0;
listener.field(type, mutable);
}
private int[] readSupertypes() {
var count = readLEB();
var result = new int[count];
for (var i = 0; i < count; ++i) {
result[i] = readLEB();
}
return result;
}
}

View File

@ -0,0 +1,65 @@
/*
* Copyright 2024 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.backend.wasm.parser;
import org.teavm.backend.wasm.model.WasmPackedType;
public abstract class WasmHollowStorageType {
public static final Packed INT16 = new Packed(WasmPackedType.INT16);
public static final Packed INT8 = new Packed(WasmPackedType.INT8);
private WasmHollowStorageType() {
}
public abstract WasmHollowType asUnpackedType();
public static Packed packed(WasmPackedType type) {
switch (type) {
case INT8:
return INT8;
case INT16:
return INT16;
default:
throw new IllegalArgumentException();
}
}
public static final class Regular extends WasmHollowStorageType {
public final WasmHollowType type;
Regular(WasmHollowType type) {
this.type = type;
}
@Override
public WasmHollowType asUnpackedType() {
return type;
}
}
public static final class Packed extends WasmHollowStorageType {
public final WasmPackedType type;
private Packed(WasmPackedType type) {
this.type = type;
}
@Override
public WasmHollowType asUnpackedType() {
return WasmHollowType.INT32;
}
}
}