mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 00:04:10 -08:00
wasm gc: improve disassembler to print type section
This commit is contained in:
parent
7324e99e6a
commit
5a513fd6fd
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -15,11 +15,6 @@
|
|||
*/
|
||||
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.expression.WasmFloatBinaryOperation;
|
||||
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.CodeListener;
|
||||
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.ModuleParser;
|
||||
import org.teavm.backend.wasm.parser.Opcode;
|
||||
import org.teavm.backend.wasm.parser.WasmHollowType;
|
||||
import org.teavm.common.ByteArrayAsyncInputStream;
|
||||
|
||||
public class DisassemblyCodeSectionListener implements AddressListener, CodeSectionListener, CodeListener {
|
||||
private DisassemblyWriter writer;
|
||||
private int address;
|
||||
private int addressOffset;
|
||||
public class DisassemblyCodeSectionListener extends BaseDisassemblyListener implements AddressListener,
|
||||
CodeSectionListener, CodeListener {
|
||||
private int blockIdGen;
|
||||
|
||||
public DisassemblyCodeSectionListener(DisassemblyWriter writer) {
|
||||
this.writer = 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();
|
||||
super(writer);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -80,7 +56,9 @@ public class DisassemblyCodeSectionListener implements AddressListener, CodeSect
|
|||
public void local(int start, int count, WasmHollowType type) {
|
||||
writer.address(address);
|
||||
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();
|
||||
}
|
||||
|
||||
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
|
||||
public void error(int depth) {
|
||||
|
@ -160,8 +92,9 @@ public class DisassemblyCodeSectionListener implements AddressListener, CodeSect
|
|||
public int startBlock(boolean loop, WasmHollowType type) {
|
||||
writer.address(address);
|
||||
var label = blockIdGen++;
|
||||
writer.write(loop ? "loop" : "block").write(" $label_" + label).write(blockTypeToString(type))
|
||||
.indent().eol();
|
||||
writer.write(loop ? "loop" : "block").write(" $label_" + label);
|
||||
writeBlockType(type);
|
||||
writer.indent().eol();
|
||||
return label;
|
||||
}
|
||||
|
||||
|
@ -169,7 +102,9 @@ public class DisassemblyCodeSectionListener implements AddressListener, CodeSect
|
|||
public int startConditionalBlock(WasmHollowType type) {
|
||||
writer.address(address);
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -183,7 +118,9 @@ public class DisassemblyCodeSectionListener implements AddressListener, CodeSect
|
|||
public int startTry(WasmHollowType type) {
|
||||
writer.address(address);
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -827,7 +764,9 @@ public class DisassemblyCodeSectionListener implements AddressListener, CodeSect
|
|||
|
||||
@Override
|
||||
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
|
||||
|
@ -836,7 +775,8 @@ public class DisassemblyCodeSectionListener implements AddressListener, CodeSect
|
|||
if (!nullable) {
|
||||
writer.write("null ");
|
||||
}
|
||||
writer.write(typeToString(type)).write(")").eol();
|
||||
writeType(type);
|
||||
writer.write(")").eol();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -845,7 +785,8 @@ public class DisassemblyCodeSectionListener implements AddressListener, CodeSect
|
|||
if (!nullable) {
|
||||
writer.write("null ");
|
||||
}
|
||||
writer.write(typeToString(type)).write(")").eol();
|
||||
writeType(type);
|
||||
writer.write(")").eol();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -914,28 +855,4 @@ public class DisassemblyCodeSectionListener implements AddressListener, CodeSect
|
|||
public void int31Get(WasmSignedType signedType) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -15,10 +15,7 @@
|
|||
*/
|
||||
package org.teavm.backend.wasm.parser;
|
||||
|
||||
public interface CodeSectionListener {
|
||||
default void sectionStart(int functionCount) {
|
||||
}
|
||||
|
||||
public interface CodeSectionListener extends SectionListener {
|
||||
default boolean functionStart(int index, int size) {
|
||||
return false;
|
||||
}
|
||||
|
@ -35,7 +32,4 @@ public interface CodeSectionListener {
|
|||
|
||||
default void functionEnd() {
|
||||
}
|
||||
|
||||
default void sectionEnd() {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.WasmSignedType;
|
||||
|
||||
public class CodeSectionParser {
|
||||
private AddressListener addressListener;
|
||||
public class CodeSectionParser extends BaseSectionParser {
|
||||
private CodeSectionListener listener;
|
||||
private byte[] data;
|
||||
private int ptr;
|
||||
private CodeListener codeListener;
|
||||
private int lastReportedPtr = -1;
|
||||
private List<Block> blockStack = new ArrayList<>();
|
||||
|
||||
public CodeSectionParser(AddressListener addressListener, CodeSectionListener listener) {
|
||||
this.addressListener = addressListener;
|
||||
super(addressListener);
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
public void parse(byte[] data) {
|
||||
this.data = data;
|
||||
ptr = 0;
|
||||
try {
|
||||
parseFunctions();
|
||||
} finally {
|
||||
this.data = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void parseFunctions() {
|
||||
@Override
|
||||
protected void parseContent() {
|
||||
reportAddress();
|
||||
int count = readLEB();
|
||||
listener.sectionStart(count);
|
||||
|
@ -853,144 +840,6 @@ public class CodeSectionParser {
|
|||
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 {
|
||||
int token;
|
||||
|
||||
|
|
|
@ -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() {
|
||||
}
|
||||
}
|
|
@ -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() {
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user