From 8aa3af6beb39589d50b1bda4018f84b630cbd3fc Mon Sep 17 00:00:00 2001 From: zxh0 Date: Tue, 9 Oct 2018 14:06:21 +0800 Subject: [PATCH] support WASM --- build.gradle | 2 +- .../zxh/classpy/common/FileComponent.java | 4 + .../com/github/zxh/classpy/gui/MyMenuBar.java | 1 + .../zxh/classpy/gui/support/FileType.java | 1 + .../classpy/gui/support/FileTypeInferer.java | 7 + .../zxh/classpy/gui/support/OpenFileTask.java | 2 + classpy-gui/src/main/resources/wasm.png | Bin 0 -> 1075 bytes classpy-wasm/build.gradle | 3 + .../com/github/zxh/classpy/wasm/Vector.java | 23 + .../zxh/classpy/wasm/WasmBinComponent.java | 107 ++++ .../github/zxh/classpy/wasm/WasmBinFile.java | 21 + .../zxh/classpy/wasm/WasmBinParser.java | 27 + .../zxh/classpy/wasm/WasmBinReader.java | 62 ++ .../zxh/classpy/wasm/instructions/Expr.java | 18 + .../zxh/classpy/wasm/instructions/Instr.java | 534 ++++++++++++++++++ .../zxh/classpy/wasm/sections/Code.java | 63 +++ .../zxh/classpy/wasm/sections/Data.java | 25 + .../zxh/classpy/wasm/sections/Element.java | 15 + .../zxh/classpy/wasm/sections/Export.java | 35 ++ .../zxh/classpy/wasm/sections/Global.java | 24 + .../zxh/classpy/wasm/sections/Import.java | 41 ++ .../zxh/classpy/wasm/sections/Section.java | 71 +++ .../zxh/classpy/wasm/types/BlockType.java | 22 + .../zxh/classpy/wasm/types/FuncType.java | 29 + .../zxh/classpy/wasm/types/GlobalType.java | 12 + .../github/zxh/classpy/wasm/types/Limits.java | 21 + .../zxh/classpy/wasm/types/TableType.java | 13 + .../zxh/classpy/wasm/types/ValType.java | 21 + .../github/zxh/classpy/wasm/values/Byte.java | 46 ++ .../github/zxh/classpy/wasm/values/Bytes.java | 23 + .../github/zxh/classpy/wasm/values/Name.java | 17 + .../github/zxh/classpy/wasm/values/S32.java | 23 + .../github/zxh/classpy/wasm/values/S64.java | 23 + .../github/zxh/classpy/wasm/values/U32.java | 23 + settings.gradle | 1 + 35 files changed, 1359 insertions(+), 1 deletion(-) create mode 100644 classpy-gui/src/main/resources/wasm.png create mode 100644 classpy-wasm/build.gradle create mode 100644 classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/Vector.java create mode 100644 classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/WasmBinComponent.java create mode 100644 classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/WasmBinFile.java create mode 100644 classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/WasmBinParser.java create mode 100644 classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/WasmBinReader.java create mode 100644 classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/instructions/Expr.java create mode 100644 classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/instructions/Instr.java create mode 100644 classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/sections/Code.java create mode 100644 classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/sections/Data.java create mode 100644 classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/sections/Element.java create mode 100644 classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/sections/Export.java create mode 100644 classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/sections/Global.java create mode 100644 classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/sections/Import.java create mode 100644 classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/sections/Section.java create mode 100644 classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/types/BlockType.java create mode 100644 classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/types/FuncType.java create mode 100644 classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/types/GlobalType.java create mode 100644 classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/types/Limits.java create mode 100644 classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/types/TableType.java create mode 100644 classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/types/ValType.java create mode 100644 classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/values/Byte.java create mode 100644 classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/values/Bytes.java create mode 100644 classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/values/Name.java create mode 100644 classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/values/S32.java create mode 100644 classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/values/S64.java create mode 100644 classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/values/U32.java diff --git a/build.gradle b/build.gradle index f071bfde..9a7ff7ab 100644 --- a/build.gradle +++ b/build.gradle @@ -28,7 +28,7 @@ subprojects { apply plugin: 'java' //apply plugin: 'application' - sourceCompatibility = "1.8"; + sourceCompatibility = "1.8" //mainClassName = 'com.github.zxh0.luago.Main' sourceCompatibility = '1.8' diff --git a/classpy-common/src/main/java/com/github/zxh/classpy/common/FileComponent.java b/classpy-common/src/main/java/com/github/zxh/classpy/common/FileComponent.java index 284c63f5..de5c0f88 100644 --- a/classpy-common/src/main/java/com/github/zxh/classpy/common/FileComponent.java +++ b/classpy-common/src/main/java/com/github/zxh/classpy/common/FileComponent.java @@ -55,6 +55,10 @@ protected final void add(String name, FileComponent subComponent) { components.add(subComponent); } + protected final void clear() { + components.clear(); + } + /** * The returned string will be displayed by BytesTreeItem. * diff --git a/classpy-gui/src/main/java/com/github/zxh/classpy/gui/MyMenuBar.java b/classpy-gui/src/main/java/com/github/zxh/classpy/gui/MyMenuBar.java index 118164dd..8f7b550c 100644 --- a/classpy-gui/src/main/java/com/github/zxh/classpy/gui/MyMenuBar.java +++ b/classpy-gui/src/main/java/com/github/zxh/classpy/gui/MyMenuBar.java @@ -46,6 +46,7 @@ private Menu createOpenMenu() { openMenu.getItems().add(createOpenMenuItem(FileType.JAVA_JAR)); openMenu.getItems().add(createOpenMenuItem(FileType.JAVA_CLASS)); openMenu.getItems().add(createOpenMenuItem(FileType.LUA_BC)); + openMenu.getItems().add(createOpenMenuItem(FileType.WASM)); openMenu.setMnemonicParsing(true); return openMenu; } diff --git a/classpy-gui/src/main/java/com/github/zxh/classpy/gui/support/FileType.java b/classpy-gui/src/main/java/com/github/zxh/classpy/gui/support/FileType.java index eeee20b6..d5319279 100644 --- a/classpy-gui/src/main/java/com/github/zxh/classpy/gui/support/FileType.java +++ b/classpy-gui/src/main/java/com/github/zxh/classpy/gui/support/FileType.java @@ -11,6 +11,7 @@ public enum FileType { JAVA_JAR("/jar.png", "Java JAR", "*.jar"), JAVA_CLASS("/java.png", "Java Class", "*.class"), LUA_BC("/lua.png", "Lua Binary Chunk", "*.luac"), + WASM("/wasm.png", "WebAssembly Binary Code", "*.wasm"), UNKNOWN("/file.png", "Unknown", "*.*"), ; diff --git a/classpy-gui/src/main/java/com/github/zxh/classpy/gui/support/FileTypeInferer.java b/classpy-gui/src/main/java/com/github/zxh/classpy/gui/support/FileTypeInferer.java index 5fded0ee..73006732 100644 --- a/classpy-gui/src/main/java/com/github/zxh/classpy/gui/support/FileTypeInferer.java +++ b/classpy-gui/src/main/java/com/github/zxh/classpy/gui/support/FileTypeInferer.java @@ -5,6 +5,7 @@ public class FileTypeInferer { + private static final byte[] wasmMagicNumber = {0, 'a', 's', 'm'}; private static final byte[] binaryChunkSig = {0x1B, 'L', 'u', 'a'}; private static final byte[] classMagicNumber = { (byte) 0xCA, @@ -24,6 +25,9 @@ public static FileType inferFileType(URL url) { if (filename.endsWith(".luac")) { return FileType.LUA_BC; } + if (filename.endsWith(".wasm")) { + return FileType.WASM; + } return FileType.UNKNOWN; } @@ -36,6 +40,9 @@ public static FileType inferFileType(byte[] data) { if (Arrays.equals(magicNumber, binaryChunkSig)) { return FileType.LUA_BC; } + if (Arrays.equals(magicNumber, wasmMagicNumber)) { + return FileType.WASM; + } } return FileType.UNKNOWN; } diff --git a/classpy-gui/src/main/java/com/github/zxh/classpy/gui/support/OpenFileTask.java b/classpy-gui/src/main/java/com/github/zxh/classpy/gui/support/OpenFileTask.java index 61c96737..aadd85b7 100644 --- a/classpy-gui/src/main/java/com/github/zxh/classpy/gui/support/OpenFileTask.java +++ b/classpy-gui/src/main/java/com/github/zxh/classpy/gui/support/OpenFileTask.java @@ -12,6 +12,7 @@ import java.net.URL; import java.util.function.Consumer; +import com.github.zxh.classpy.wasm.WasmBinParser; import javafx.concurrent.Task; public class OpenFileTask extends Task { @@ -49,6 +50,7 @@ private static FileComponent parse(byte[] data, FileType fileType) { switch (fileType) { case JAVA_CLASS: return new ClassFileParser().parse(data); case LUA_BC: return new BinaryChunkParser().parse(data); + case WASM: return new WasmBinParser().parse(data); default: return new FileComponent() {}; // todo } } diff --git a/classpy-gui/src/main/resources/wasm.png b/classpy-gui/src/main/resources/wasm.png new file mode 100644 index 0000000000000000000000000000000000000000..db40bc870141fb0d1af3ac5bf2782b50ed0ff00f GIT binary patch literal 1075 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|I14-?iy0V9 z4uCMDzol?6P(fs7NJL45ua8x7ey(0(N`6wRUPW#JP&EUCO@$SZnVVXYs8ErclUHn2 zVXFjIVFhG^g!Ppaz)DK8ZIvQ?0~DO|i&7O#^-S~(l1AfjnEK zjFOT9D}DX)@^Za$W4-*MbbUihOG|wNBYh(yU7!lx;>x^|#0uTKVr7USFmqf|i<65o z3raHc^AtelCMM;Vme?vOfh>Xph&xL%(-1c06+^uR^q@XSM&D4+Kp$>4P^%3{)XKjo zGZknv$b36P8?Z_gF{nK@`XI}Z90TzwSQO}0J1!f2c(B=V`5aP@WME(__H=O!u@KCi zY?~btDA1OE&emN1`U=Ml0?j`fJEnU0I7n+t&vw~x!O!58zJN%5!_@r>tr6@;M8uj} zGPoRF115?Da_{GIyzVjSY~H(>A1$TLE#>xhmU-*iJA16Q{qSyPGu|)Q zy~=BwmfpfMA5}9;v%)UkRk&^9vByt)A&2eq$=lQJupGL_HUEG=>nrZ{^O*WYJfHsQ zf4W&cTYGMZr034;8k6e>KQ)F~uk-m}f7;mT(xSTKE4Bq@OlEl=)@0-N?`ccU-6MK} z5$)2x#%uOXRoJ}#`?Qaxzh?{Zs9L#J?8x38r1oV&V1RUtso39)3x|U~EZT9&M{>KT z|DAc zp)1+-S%3b`SF-2c+rHFg+uQv?|7gyny|s=%UQgy{(+_^{tE2RAw$jtLkH0HRIlFpq z7CNj{{oCaNH=C``)Ab=z^CaT;cwCfZZm=l}U3NmMFG=59-s$=8d9MPwL|D$wdd;z~ z((wM`Bz2n`o6GjH=sa!faAQxEInl)9>&I`W=i*=ZwkzUyag=?>y7fMXob?`r5lJ(kK)R4%1`Gf9?`_~?PU&PAs*HbKh{oLqt z<}#0sA6h?Tj$HJ1yM`RX3)yk!n zTMx4sEP3}}JNJs#|1% supplier; + + public Vector(Supplier supplier) { + this.supplier = supplier; + } + + @Override + protected void readContent(WasmBinReader reader) { + int length = readU32(reader, "length"); + for (int i = 0; i < length; i++) { + WasmBinComponent element = supplier.get(); + add(null, element); + element.read(reader); + } + } + +} diff --git a/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/WasmBinComponent.java b/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/WasmBinComponent.java new file mode 100644 index 00000000..e7232898 --- /dev/null +++ b/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/WasmBinComponent.java @@ -0,0 +1,107 @@ +package com.github.zxh.classpy.wasm; + +import com.github.zxh.classpy.common.FileComponent; +import com.github.zxh.classpy.wasm.instructions.Expr; +import com.github.zxh.classpy.wasm.types.ValType; +import com.github.zxh.classpy.wasm.values.Byte; +import com.github.zxh.classpy.wasm.values.Bytes; +import com.github.zxh.classpy.wasm.values.Name; +import com.github.zxh.classpy.wasm.values.U32; + +import java.util.function.Supplier; + +public class WasmBinComponent extends FileComponent { + + public final void read(WasmBinReader reader) { + try { + int offset = reader.getPosition(); + readContent(reader); + int length = reader.getPosition() - offset; + super.setOffset(offset); + super.setLength(length); + } catch (Exception e) { + System.out.println("error parsing: " + getClass()); + throw e; + } + } + + protected void readContent(WasmBinReader reader) { + for (FileComponent fc : getComponents()) { + ((WasmBinComponent) fc).read(reader); + } + } + + protected void postRead() { + + } + + protected int readU32(WasmBinReader reader, String name) { + U32 u32 = new U32(); + add(name, u32); + u32.read(reader); + return u32.getIntValue(); + } + + protected int readByte(WasmBinReader reader, String name) { + Byte b = new Byte(); + add(name, b); + b.read(reader); + return b.getValue(); + } + + protected int readByte(WasmBinReader reader, String name, byte... expectedValues) { + Byte b = new Byte(expectedValues); + add(name, b); + b.read(reader); + return b.getValue(); + } + + protected byte[] readBytes(WasmBinReader reader, String name, int n) { + Bytes bytes = new Bytes(n); + add(name, bytes); + bytes.read(reader); + return bytes.getBytes(); + } + + protected T read(WasmBinReader reader, + String name, + Supplier supplier) { + T c = supplier.get(); + add(name, c); + c.read(reader); + return c; + } + + protected void readVector(WasmBinReader reader, String name, + Supplier supplier) { + Vector vec = new Vector(supplier); + add(name, vec); + vec.read(reader); + } + + protected void _byte(String name, byte... expectedValues) { + add(name, new Byte(expectedValues)); + } + + protected void u32(String name) { + add(name, new U32()); + } + + protected void name(String name) { + add(name, new Name()); + } + + protected void valType(String name) { + add(name, new ValType()); + } + + protected void expr(String name) { + add(name, new Expr()); + } + + protected void vector(String name, + Supplier supplier) { + add(name, new Vector(supplier)); + } + +} diff --git a/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/WasmBinFile.java b/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/WasmBinFile.java new file mode 100644 index 00000000..7e56e0f0 --- /dev/null +++ b/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/WasmBinFile.java @@ -0,0 +1,21 @@ +package com.github.zxh.classpy.wasm; + +import com.github.zxh.classpy.wasm.sections.Section; + +public class WasmBinFile extends WasmBinComponent { + + protected void readContent(WasmBinReader reader) { + readBytes(reader, "magic", 4); + readBytes(reader, "version", 4); + readSections(reader); + } + + private void readSections(WasmBinReader reader) { + while (reader.remaining() > 0) { + Section section = new Section(); + add("section", section); + section.read(reader); + } + } + +} diff --git a/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/WasmBinParser.java b/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/WasmBinParser.java new file mode 100644 index 00000000..bda68c4d --- /dev/null +++ b/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/WasmBinParser.java @@ -0,0 +1,27 @@ +package com.github.zxh.classpy.wasm; + +import com.github.zxh.classpy.common.FileComponent; +import com.github.zxh.classpy.common.FileParser; + +public class WasmBinParser implements FileParser { + + @Override + public WasmBinFile parse(byte[] data) { + WasmBinFile wasm = new WasmBinFile(); + try { + wasm.read(new WasmBinReader(data)); + postRead(wasm); + } catch (Exception e) { + e.printStackTrace(System.err); + } + return wasm; + } + + private static void postRead(WasmBinComponent bc) { + for (FileComponent c : bc.getComponents()) { + postRead((WasmBinComponent) c); + } + bc.postRead(); + } + +} diff --git a/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/WasmBinReader.java b/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/WasmBinReader.java new file mode 100644 index 00000000..59d7a180 --- /dev/null +++ b/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/WasmBinReader.java @@ -0,0 +1,62 @@ +package com.github.zxh.classpy.wasm; + +import com.github.zxh.classpy.common.BytesReader; + +import java.nio.ByteOrder; + +public class WasmBinReader extends BytesReader { + + public WasmBinReader(byte[] data) { + super(data, ByteOrder.LITTLE_ENDIAN); // ? + } + + public long readU32() { + return readUnsignedLEB128(32); + } + + public long readU64() { + return readUnsignedLEB128(64); + } + + public long readS32() { + return readSignedLEB128(32); + } + + public long readS64() { + return readSignedLEB128(64); + } + + private long readUnsignedLEB128(int nBits) { + long result = 0; + for (int shift = 0; shift < nBits; shift += 7) { + int b = readByte(); + result |= ((b & 0b0111_1111) << shift); + if ((b & 0b1000_0000) == 0) { + return result; + } + } + + throw new RuntimeException("can not decode unsigned LEB128"); + } + + private long readSignedLEB128(int size) { + long result = 0; + int shift = 0; + //size = number of bits in signed integer; + byte b; + do{ + b = readByte(); + result |= ((b & 0b0111_1111) << shift); + shift += 7; + } while ((b & 0b1000_0000) != 0); + + /* sign bit of byte is second high order bit (0x40) */ + if ((shift < size) && ((b & 0b0100_0000) != 0)) { + /* sign extend */ + result |= (~0 << shift); + } + + return result; + } + +} diff --git a/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/instructions/Expr.java b/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/instructions/Expr.java new file mode 100644 index 00000000..037fa442 --- /dev/null +++ b/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/instructions/Expr.java @@ -0,0 +1,18 @@ +package com.github.zxh.classpy.wasm.instructions; + +import com.github.zxh.classpy.wasm.WasmBinComponent; +import com.github.zxh.classpy.wasm.WasmBinReader; + +public class Expr extends WasmBinComponent { + + @Override + protected void readContent(WasmBinReader reader) { + while (reader.remaining() > 0) { + Instr instr = read(reader, null, Instr::new); + if (instr.getOpcode() == 0x0B) { // end + break; + } + } + } + +} diff --git a/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/instructions/Instr.java b/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/instructions/Instr.java new file mode 100644 index 00000000..6905774c --- /dev/null +++ b/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/instructions/Instr.java @@ -0,0 +1,534 @@ +package com.github.zxh.classpy.wasm.instructions; + +import com.github.zxh.classpy.common.ParseException; +import com.github.zxh.classpy.wasm.WasmBinComponent; +import com.github.zxh.classpy.wasm.WasmBinReader; +import com.github.zxh.classpy.wasm.types.BlockType; +import com.github.zxh.classpy.wasm.values.S32; +import com.github.zxh.classpy.wasm.values.S64; +import com.github.zxh.classpy.wasm.values.U32; + +public class Instr extends WasmBinComponent { + + private int opcode; + + public int getOpcode() { + return opcode; + } + + @Override + protected void readContent(WasmBinReader reader) { + opcode = readByte(reader, "opcode"); + //System.out.println(opcode.getDesc()); + + if (opcode == 0x05) { + setName("else"); + } else if (opcode == 0x0B) { + setName("end"); + } else if (opcode < 0x1A) { + readControlInstructions(reader); + } else if (opcode < 0x1C) { + readParametricInstructions(reader); + } else if (opcode < 0x25) { + readVariableInstructions(reader); + } else if (opcode < 0x3F) { + readMemoryInstructions1(reader); + } else if (opcode < 0x41) { + readMemoryInstructions2(reader); + } else if (opcode < 0x45) { + readNumericInstructions1(reader); + } else { + readNumericInstructions2(reader); + } + } + + /* +instr ::= 0x00 ⇒ unreachable + | 0x01 ⇒ nop + | 0x02 rt:blocktype (in:instr)* 0x0B ⇒ block rt in* end + | 0x03 rt:blocktype (in:instr)* 0x0B ⇒ loop rt in* end + | 0x04 rt:blocktype (in:instr)* 0x0B ⇒ if rt in* else 𝜖 end + | 0x04 rt:blocktype (in1:instr)* 0x05 (in2:instr)* 0x0B ⇒ if rt in* 1 else in* 2 end + | 0x0C 𝑙:labelidx ⇒ br 𝑙 + | 0x0D 𝑙:labelidx ⇒ br_if 𝑙 + | 0x0E 𝑙*:vec(labelidx) 𝑙𝑁:labelidx ⇒ br_table 𝑙* 𝑙𝑁 + | 0x0F ⇒ return + | 0x10 𝑥:funcidx ⇒ call 𝑥 + | 0x11 𝑥:typeidx 0x00 ⇒ call_indirect 𝑥 + */ + private void readControlInstructions(WasmBinReader reader) { + switch (opcode) { + case 0x00: + setName("unreachable"); + break; + case 0x01: + setName("nop"); + break; + case 0x02: + setName("block"); + readBlock(reader, false); + break; + case 0x03: + setName("loop"); + readBlock(reader, false); + break; + case 0x04: + setName("if"); + readBlock(reader, true); + break; + case 0x0C: + setName("br"); + readU32(reader, "label"); + break; + case 0x0D: + setName("br_if"); + readU32(reader, "label"); + break; + case 0x0E: + setName("br_table"); + readVector(reader, "labels", U32::new); + readU32(reader, "default"); + break; + case 0x0F: + setName("return"); + break; + case 0x10: + setName("call"); + readU32(reader, "func"); + break; + case 0x11: + setName("call_indirect"); + readU32(reader, "type"); + readByte(reader, null, (byte) 0x00); + break; + default: + throw new ParseException(String.format( + "Invalid opcode: 0x%02X", opcode)); + } + } + + /* +instr ::= . . . + | 0x1A ⇒ drop + | 0x1B ⇒ select + */ + private void readParametricInstructions(WasmBinReader reader) { + clear(); + switch (opcode) { + case 0x1A: setName("drop"); break; + case 0x1B: setName("select"); break; + default: throw new ParseException(String.format( + "Invalid opcode: 0x%02X", opcode)); + } + } + + /* +instr ::= . . . + | 0x20 𝑥:localidx ⇒ get_local 𝑥 + | 0x21 𝑥:localidx ⇒ set_local 𝑥 + | 0x22 𝑥:localidx ⇒ tee_local 𝑥 + | 0x23 𝑥:globalidx ⇒ get_global 𝑥 + | 0x24 𝑥:globalidx ⇒ set_global 𝑥 + */ + private void readVariableInstructions(WasmBinReader reader) { + switch (opcode) { + case 0x20: setName("get_local"); break; + case 0x21: setName("set_local"); break; + case 0x22: setName("tee_local"); break; + case 0x23: setName("get_global"); break; + case 0x24: setName("set_global"); break; + default: throw new ParseException(String.format( + "Invalid opcode: 0x%02X", opcode)); + } + readU32(reader, "index"); + } + + /* +memarg ::= 𝑎:u32 𝑜:u32 ⇒ {align 𝑎, offset 𝑜} +instr ::= . . . + | 0x28 𝑚:memarg ⇒ i32.load 𝑚 + | 0x29 𝑚:memarg ⇒ i64.load 𝑚 + | 0x2A 𝑚:memarg ⇒ f32.load 𝑚 + | 0x2B 𝑚:memarg ⇒ f64.load 𝑚 + | 0x2C 𝑚:memarg ⇒ i32.load8_s 𝑚 + | 0x2D 𝑚:memarg ⇒ i32.load8_u 𝑚 + | 0x2E 𝑚:memarg ⇒ i32.load16_s 𝑚 + | 0x2F 𝑚:memarg ⇒ i32.load16_u 𝑚 + | 0x30 𝑚:memarg ⇒ i64.load8_s 𝑚 + | 0x31 𝑚:memarg ⇒ i64.load8_u 𝑚 + | 0x32 𝑚:memarg ⇒ i64.load16_s 𝑚 + | 0x33 𝑚:memarg ⇒ i64.load16_u 𝑚 + | 0x34 𝑚:memarg ⇒ i64.load32_s 𝑚 + | 0x35 𝑚:memarg ⇒ i64.load32_u 𝑚 + | 0x36 𝑚:memarg ⇒ i32.store 𝑚 + | 0x37 𝑚:memarg ⇒ i64.store 𝑚 + | 0x38 𝑚:memarg ⇒ f32.store 𝑚 + | 0x39 𝑚:memarg ⇒ f64.store 𝑚 + | 0x3A 𝑚:memarg ⇒ i32.store8 𝑚 + | 0x3B 𝑚:memarg ⇒ i32.store16 𝑚 + | 0x3C 𝑚:memarg ⇒ i64.store8 𝑚 + | 0x3D 𝑚:memarg ⇒ i64.store16 𝑚 + | 0x3E 𝑚:memarg ⇒ i64.store32 𝑚 + | 0x3F 0x00 ⇒ memory.size + | 0x40 0x00 ⇒ memory.grow + */ + private void readMemoryInstructions1(WasmBinReader reader) { + switch (opcode) { + case 0x28: setName("i32.load"); break; + case 0x29: setName("i64.load"); break; + case 0x2A: setName("f32.load"); break; + case 0x2B: setName("f64.load"); break; + case 0x2C: setName("i32.load8_s"); break; + case 0x2D: setName("i32.load8_u"); break; + case 0x2E: setName("i32.load16_s"); break; + case 0x2F: setName("i32.load16_u"); break; + case 0x30: setName("i64.load8_s"); break; + case 0x31: setName("i64.load8_u"); break; + case 0x32: setName("i64.load16_s"); break; + case 0x33: setName("i64.load16_u"); break; + case 0x34: setName("i64.load32_s"); break; + case 0x35: setName("i64.load32_u"); break; + case 0x36: setName("i32.store"); break; + case 0x37: setName("i64.store"); break; + case 0x38: setName("f32.store"); break; + case 0x39: setName("f64.store"); break; + case 0x3A: setName("i32.store8"); break; + case 0x3B: setName("i32.store16"); break; + case 0x3C: setName("i64.store8"); break; + case 0x3D: setName("i64.store16"); break; + case 0x3E: setName("i64.store32"); break; + default: throw new ParseException(String.format( + "Invalid opcode: 0x%02X", opcode)); + } + + readU32(reader, "align"); + readU32(reader, "offset"); + } + + private void readMemoryInstructions2(WasmBinReader reader) { + switch (opcode) { + case 0x3F: setName("memory.size"); break; + case 0x40: setName("memory.grow"); break; + default: throw new ParseException(String.format( + "Invalid opcode: 0x%02X", opcode)); + } + + readByte(reader, null, (byte) 0x00); + } + + /* +instr ::= . . . + | 0x41 𝑛:i32 ⇒ i32.const 𝑛 + | 0x42 𝑛:i64 ⇒ i64.const 𝑛 + | 0x43 𝑧:f32 ⇒ f32.const 𝑧 + | 0x44 𝑧:f64 ⇒ f64.const 𝑧 + */ + private void readNumericInstructions1(WasmBinReader reader) { + switch (opcode) { + case 0x41: + setName("i32.const"); + read(reader, "n", S32::new); + break; + case 0x42: + setName("i64.const"); + read(reader, "n", S64::new); + break; + case 0x43: + setName("i64.const"); + reader.readBytes(4); // todo + break; + case 0x44: + setName("i64.const"); + reader.readBytes(8); // todo + break; + default: throw new ParseException(String.format( + "Invalid opcode: 0x%02X", opcode)); + } + } + + /* +instr ::= . . . + | 0x45 ⇒ i32.eqz + | 0x46 ⇒ i32.eq + | 0x47 ⇒ i32.ne + | 0x48 ⇒ i32.lt_s + | 0x49 ⇒ i32.lt_u + | 0x4A ⇒ i32.gt_s + | 0x4B ⇒ i32.gt_u + | 0x4C ⇒ i32.le_s + | 0x4D ⇒ i32.le_u + | 0x4E ⇒ i32.ge_s + | 0x4F ⇒ i32.ge_u + + | 0x50 ⇒ i64.eqz + | 0x51 ⇒ i64.eq + | 0x52 ⇒ i64.ne + | 0x53 ⇒ i64.lt_s + | 0x54 ⇒ i64.lt_u + | 0x55 ⇒ i64.gt_s + | 0x56 ⇒ i64.gt_u + | 0x57 ⇒ i64.le_s + | 0x58 ⇒ i64.le_u + | 0x59 ⇒ i64.ge_s + | 0x5A ⇒ i64.ge_u + + | 0x5B ⇒ f32.eq + | 0x5C ⇒ f32.ne + | 0x5D ⇒ f32.lt + | 0x5E ⇒ f32.gt + | 0x5F ⇒ f32.le + | 0x60 ⇒ f32.ge + + | 0x61 ⇒ f64.eq + | 0x62 ⇒ f64.ne + | 0x63 ⇒ f64.lt + | 0x64 ⇒ f64.gt + | 0x65 ⇒ f64.le + | 0x66 ⇒ f64.ge + + | 0x67 ⇒ i32.clz + | 0x68 ⇒ i32.ctz + | 0x69 ⇒ i32.popcnt + | 0x6A ⇒ i32.add + | 0x6B ⇒ i32.sub + | 0x6C ⇒ i32.mul + | 0x6D ⇒ i32.div_s + | 0x6E ⇒ i32.div_u + | 0x6F ⇒ i32.rem_s + | 0x70 ⇒ i32.rem_u + | 0x71 ⇒ i32.and + | 0x72 ⇒ i32.or + | 0x73 ⇒ i32.xor + | 0x74 ⇒ i32.shl + | 0x75 ⇒ i32.shr_s + | 0x76 ⇒ i32.shr_u + | 0x77 ⇒ i32.rotl + | 0x78 ⇒ i32.rotr + + | 0x79 ⇒ i64.clz + | 0x7A ⇒ i64.ctz + | 0x7B ⇒ i64.popcnt + | 0x7C ⇒ i64.add + | 0x7D ⇒ i64.sub + | 0x7E ⇒ i64.mul + | 0x7F ⇒ i64.div_s + | 0x80 ⇒ i64.div_u + | 0x81 ⇒ i64.rem_s + | 0x82 ⇒ i64.rem_u + | 0x83 ⇒ i64.and + | 0x84 ⇒ i64.or + | 0x85 ⇒ i64.xor + | 0x86 ⇒ i64.shl + | 0x87 ⇒ i64.shr_s + | 0x88 ⇒ i64.shr_u + | 0x89 ⇒ i64.rotl + | 0x8A ⇒ i64.rotr + + | 0x8B ⇒ f32.abs + | 0x8C ⇒ f32.neg + | 0x8D ⇒ f32.ceil + | 0x8E ⇒ f32.floor + | 0x8F ⇒ f32.trunc + | 0x90 ⇒ f32.nearest + | 0x91 ⇒ f32.sqrt + | 0x92 ⇒ f32.add + | 0x93 ⇒ f32.sub + | 0x94 ⇒ f32.mul + | 0x95 ⇒ f32.div + | 0x96 ⇒ f32.min + | 0x97 ⇒ f32.max + | 0x98 ⇒ f32.copysign + + | 0x99 ⇒ f64.abs + | 0x9A ⇒ f64.neg + | 0x9B ⇒ f64.ceil + | 0x9C ⇒ f64.floor + | 0x9D ⇒ f64.trunc + | 0x9E ⇒ f64.nearest + | 0x9F ⇒ f64.sqrt + | 0xA0 ⇒ f64.add + | 0xA1 ⇒ f64.sub + | 0xA2 ⇒ f64.mul + | 0xA3 ⇒ f64.div + | 0xA4 ⇒ f64.min + | 0xA5 ⇒ f64.max + | 0xA6 ⇒ f64.copysign + + | 0xA7 ⇒ i32.wrap/i64 + | 0xA8 ⇒ i32.trunc_s/f32 + | 0xA9 ⇒ i32.trunc_u/f32 + | 0xAA ⇒ i32.trunc_s/f64 + | 0xAB ⇒ i32.trunc_u/f64 + | 0xAC ⇒ i64.extend_s/i32 + | 0xAD ⇒ i64.extend_u/i32 + | 0xAE ⇒ i64.trunc_s/f32 + | 0xAF ⇒ i64.trunc_u/f32 + | 0xB0 ⇒ i64.trunc_s/f64 + | 0xB1 ⇒ i64.trunc_u/f64 + | 0xB2 ⇒ f32.convert_s/i32 + | 0xB3 ⇒ f32.convert_u/i32 + | 0xB4 ⇒ f32.convert_s/i64 + | 0xB5 ⇒ f32.convert_u/i64 + | 0xB6 ⇒ f32.demote/f64 + | 0xB7 ⇒ f64.convert_s/i32 + | 0xB8 ⇒ f64.convert_u/i32 + | 0xB9 ⇒ f64.convert_s/i64 + | 0xBA ⇒ f64.convert_u/i64 + | 0xBB ⇒ f64.promote/f32 + | 0xBC ⇒ i32.reinterpret/f32 + | 0xBD ⇒ i64.reinterpret/f64 + | 0xBE ⇒ f32.reinterpret/i32 + | 0xBF ⇒ f64.reinterpret/i64 + */ + private void readNumericInstructions2(WasmBinReader reader) { + clear(); + switch (opcode) { + case 0x45: setName("i32.eqz"); break; + case 0x46: setName("i32.eq"); break; + case 0x47: setName("i32.ne"); break; + case 0x48: setName("i32.lt_s"); break; + case 0x49: setName("i32.lt_u"); break; + case 0x4A: setName("i32.gt_s"); break; + case 0x4B: setName("i32.gt_u"); break; + case 0x4C: setName("i32.le_s"); break; + case 0x4D: setName("i32.le_u"); break; + case 0x4E: setName("i32.ge_s"); break; + case 0x4F: setName("i32.ge_u"); break; + case 0x50: setName("i64.eqz"); break; + case 0x51: setName("i64.eq"); break; + case 0x52: setName("i64.ne"); break; + case 0x53: setName("i64.lt_s"); break; + case 0x54: setName("i64.lt_u"); break; + case 0x55: setName("i64.gt_s"); break; + case 0x56: setName("i64.gt_u"); break; + case 0x57: setName("i64.le_s"); break; + case 0x58: setName("i64.le_u"); break; + case 0x59: setName("i64.ge_s"); break; + case 0x5A: setName("i64.ge_u"); break; + case 0x5B: setName("f32.eq"); break; + case 0x5C: setName("f32.ne"); break; + case 0x5D: setName("f32.lt"); break; + case 0x5E: setName("f32.gt"); break; + case 0x5F: setName("f32.le"); break; + case 0x60: setName("f32.ge"); break; + case 0x61: setName("f64.eq"); break; + case 0x62: setName("f64.ne"); break; + case 0x63: setName("f64.lt"); break; + case 0x64: setName("f64.gt"); break; + case 0x65: setName("f64.le"); break; + case 0x66: setName("f64.ge"); break; + case 0x67: setName("i32.clz"); break; + case 0x68: setName("i32.ctz"); break; + case 0x69: setName("i32.popcnt"); break; + case 0x6A: setName("i32.add"); break; + case 0x6B: setName("i32.sub"); break; + case 0x6C: setName("i32.mul"); break; + case 0x6D: setName("i32.div_s"); break; + case 0x6E: setName("i32.div_u"); break; + case 0x6F: setName("i32.rem_s"); break; + case 0x70: setName("i32.rem_u"); break; + case 0x71: setName("i32.and"); break; + case 0x72: setName("i32.or"); break; + case 0x73: setName("i32.xor"); break; + case 0x74: setName("i32.shl"); break; + case 0x75: setName("i32.shr_s"); break; + case 0x76: setName("i32.shr_u"); break; + case 0x77: setName("i32.rotl"); break; + case 0x78: setName("i32.rotr"); break; + case 0x79: setName("i64.clz"); break; + case 0x7A: setName("i64.ctz"); break; + case 0x7B: setName("i64.popcnt"); break; + case 0x7C: setName("i64.add"); break; + case 0x7D: setName("i64.sub"); break; + case 0x7E: setName("i64.mul"); break; + case 0x7F: setName("i64.div_s"); break; + case 0x80: setName("i64.div_u"); break; + case 0x81: setName("i64.rem_s"); break; + case 0x82: setName("i64.rem_u"); break; + case 0x83: setName("i64.and"); break; + case 0x84: setName("i64.or"); break; + case 0x85: setName("i64.xor"); break; + case 0x86: setName("i64.shl"); break; + case 0x87: setName("i64.shr_s"); break; + case 0x88: setName("i64.shr_u"); break; + case 0x89: setName("i64.rotl"); break; + case 0x8A: setName("i64.rotr"); break; + case 0x8B: setName("f32.abs"); break; + case 0x8C: setName("f32.neg"); break; + case 0x8D: setName("f32.ceil"); break; + case 0x8E: setName("f32.floor"); break; + case 0x8F: setName("f32.trunc"); break; + case 0x90: setName("f32.nearest"); break; + case 0x91: setName("f32.sqrt"); break; + case 0x92: setName("f32.add"); break; + case 0x93: setName("f32.sub"); break; + case 0x94: setName("f32.mul"); break; + case 0x95: setName("f32.div"); break; + case 0x96: setName("f32.min"); break; + case 0x97: setName("f32.max"); break; + case 0x98: setName("f32.copysign"); break; + case 0x99: setName("f64.abs"); break; + case 0x9A: setName("f64.neg"); break; + case 0x9B: setName("f64.ceil"); break; + case 0x9C: setName("f64.floor"); break; + case 0x9D: setName("f64.trunc"); break; + case 0x9E: setName("f64.nearest"); break; + case 0x9F: setName("f64.sqrt"); break; + case 0xA0: setName("f64.add"); break; + case 0xA1: setName("f64.sub"); break; + case 0xA2: setName("f64.mul"); break; + case 0xA3: setName("f64.div"); break; + case 0xA4: setName("f64.min"); break; + case 0xA5: setName("f64.max"); break; + case 0xA6: setName("f64.copysign"); break; + case 0xA7: setName("i32.wrap/i64"); break; + case 0xA8: setName("i32.trunc_s/f32"); break; + case 0xA9: setName("i32.trunc_u/f32"); break; + case 0xAA: setName("i32.trunc_s/f64"); break; + case 0xAB: setName("i32.trunc_u/f64"); break; + case 0xAC: setName("i64.extend_s/i32"); break; + case 0xAD: setName("i64.extend_u/i32"); break; + case 0xAE: setName("i64.trunc_s/f32"); break; + case 0xAF: setName("i64.trunc_u/f32"); break; + case 0xB0: setName("i64.trunc_s/f64"); break; + case 0xB1: setName("i64.trunc_u/f64"); break; + case 0xB2: setName("f32.convert_s/i32"); break; + case 0xB3: setName("f32.convert_u/i32"); break; + case 0xB4: setName("f32.convert_s/i64"); break; + case 0xB5: setName("f32.convert_u/i64"); break; + case 0xB6: setName("f32.demote/f64"); break; + case 0xB7: setName("f64.convert_s/i32"); break; + case 0xB8: setName("f64.convert_u/i32"); break; + case 0xB9: setName("f64.convert_s/i64"); break; + case 0xBA: setName("f64.convert_u/i64"); break; + case 0xBB: setName("f64.promote/f32"); break; + case 0xBC: setName("i32.reinterpret/f32"); break; + case 0xBD: setName("i64.reinterpret/f64"); break; + case 0xBE: setName("f32.reinterpret/i32"); break; + case 0xBF: setName("f64.reinterpret/i64"); break; + default: throw new ParseException(String.format( + "Invalid opcode: 0x%02X", opcode)); + } + } + + private void readBlock(WasmBinReader reader, boolean isIfBlock) { + read(reader, "rt", BlockType::new); + + // instrs + if (isIfBlock) { + while (reader.remaining() > 0) { + Instr instr = read(reader, null, Instr::new); + if (instr.opcode == 0x05) { // else + break; + } + } + } + while (reader.remaining() > 0) { + Instr instr = read(reader, null, Instr::new); + if (instr.opcode == 0x0B) { // end + break; + } + } + } + +} diff --git a/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/sections/Code.java b/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/sections/Code.java new file mode 100644 index 00000000..57dd6732 --- /dev/null +++ b/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/sections/Code.java @@ -0,0 +1,63 @@ +package com.github.zxh.classpy.wasm.sections; + +import com.github.zxh.classpy.wasm.WasmBinComponent; +import com.github.zxh.classpy.wasm.WasmBinReader; + +import java.util.Base64; + +public class Code extends WasmBinComponent { + +// { +// u32("size"); +// add("code", new Func()); +// } + + @Override + protected void readContent(WasmBinReader reader) { + setName("code"); + int size = readU32(reader, "size"); + + int pos = reader.getPosition(); + byte[] code = reader.readBytes(size); + Func func = new Func(); + add("func", func); + + try { + func.read(new WasmBinReader(code) { + @Override + public int getPosition() { + return pos + super.getPosition(); + } + }); + } catch (Exception e) { + System.out.println(e.getMessage()); + System.out.println(Base64.getEncoder().encodeToString(code)); + } + } + + + public static class Func extends WasmBinComponent { + + { + vector("locals", Locals::new); + expr("expr"); + } + + } + + + private static class Locals extends WasmBinComponent { + + { + u32("n"); + valType("type"); + } + + @Override + protected void postRead() { + setDesc(get("type").getDesc() + " x " + get("n").getDesc()); + } + + } + +} diff --git a/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/sections/Data.java b/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/sections/Data.java new file mode 100644 index 00000000..8dc0a04b --- /dev/null +++ b/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/sections/Data.java @@ -0,0 +1,25 @@ +package com.github.zxh.classpy.wasm.sections; + +import com.github.zxh.classpy.wasm.WasmBinComponent; +import com.github.zxh.classpy.wasm.WasmBinReader; + +public class Data extends WasmBinComponent { + + { + u32("data"); + expr("offset"); + add("init", new Init()); + setName("data"); + } + + private static class Init extends WasmBinComponent { + + @Override + protected void readContent(WasmBinReader reader) { + int length = readU32(reader, "length"); + readBytes(reader, "bytes", length); + } + + } + +} diff --git a/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/sections/Element.java b/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/sections/Element.java new file mode 100644 index 00000000..3e5f726d --- /dev/null +++ b/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/sections/Element.java @@ -0,0 +1,15 @@ +package com.github.zxh.classpy.wasm.sections; + +import com.github.zxh.classpy.wasm.WasmBinComponent; +import com.github.zxh.classpy.wasm.values.U32; + +public class Element extends WasmBinComponent { + + { + u32("table"); + expr("offset"); + vector("init", U32::new); + setName("element"); + } + +} diff --git a/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/sections/Export.java b/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/sections/Export.java new file mode 100644 index 00000000..b4b97c4a --- /dev/null +++ b/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/sections/Export.java @@ -0,0 +1,35 @@ +package com.github.zxh.classpy.wasm.sections; + +import com.github.zxh.classpy.common.ParseException; +import com.github.zxh.classpy.wasm.WasmBinComponent; +import com.github.zxh.classpy.wasm.WasmBinReader; + +public class Export extends WasmBinComponent { + + { + name("name"); + add("desc", new Desc()); + } + + @Override + protected void postRead() { + setDesc(get("name").getDesc()); + } + + private static class Desc extends WasmBinComponent { + + @Override + protected void readContent(WasmBinReader reader) { + int b = readByte(reader, null); + switch (b) { + case 0x00: readU32(reader, "func"); break; // funcidx + case 0x01: readU32(reader, "table"); break; // tableidx + case 0x02: readU32(reader, "mem"); break; // memidx + case 0x03: readU32(reader, "global"); break; // globalidx + default: throw new ParseException("Invalid export desc: " + b); + } + } + + } + +} diff --git a/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/sections/Global.java b/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/sections/Global.java new file mode 100644 index 00000000..2dac7cd0 --- /dev/null +++ b/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/sections/Global.java @@ -0,0 +1,24 @@ +package com.github.zxh.classpy.wasm.sections; + +import com.github.zxh.classpy.wasm.WasmBinComponent; +import com.github.zxh.classpy.wasm.instructions.Expr; +import com.github.zxh.classpy.wasm.types.GlobalType; +import com.github.zxh.classpy.wasm.values.Byte; + +public class Global extends WasmBinComponent { + + { + add("type", new GlobalType()); + add("init", new Expr()); + } + + @Override + protected void postRead() { + GlobalType gt = (GlobalType) get("type"); + Byte valType = (Byte) gt.getComponents().get(1); + String mut = gt.getComponents().get(0).getDesc(); + String desc = mut + (valType.getValue() == 0 ? ": const" : ": var"); + setDesc(desc); + } + +} diff --git a/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/sections/Import.java b/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/sections/Import.java new file mode 100644 index 00000000..92bbd405 --- /dev/null +++ b/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/sections/Import.java @@ -0,0 +1,41 @@ +package com.github.zxh.classpy.wasm.sections; + +import com.github.zxh.classpy.common.ParseException; +import com.github.zxh.classpy.wasm.WasmBinComponent; +import com.github.zxh.classpy.wasm.WasmBinReader; +import com.github.zxh.classpy.wasm.types.GlobalType; +import com.github.zxh.classpy.wasm.types.Limits; +import com.github.zxh.classpy.wasm.types.TableType; +import com.github.zxh.classpy.wasm.values.U32; + +public class Import extends WasmBinComponent { + + { + name("module"); + name("name"); + add("desc", new Desc()); + } + + @Override + protected void postRead() { + setDesc(get("module").getDesc() + "." + get("name").getDesc()); + } + + + private static class Desc extends WasmBinComponent { + + @Override + protected void readContent(WasmBinReader reader) { + int b = readByte(reader, null); + switch (b) { + case 0x00: read(reader, "func", U32::new); break; // typeidx + case 0x01: read(reader, "table", TableType::new); break; + case 0x02: read(reader, "mem", Limits::new); break; + case 0x03: read(reader, "global", GlobalType::new); break; + default: throw new ParseException("Invalid import desc: " + b); + } + } + + } + +} diff --git a/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/sections/Section.java b/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/sections/Section.java new file mode 100644 index 00000000..e8695239 --- /dev/null +++ b/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/sections/Section.java @@ -0,0 +1,71 @@ +package com.github.zxh.classpy.wasm.sections; + +import com.github.zxh.classpy.common.ParseException; +import com.github.zxh.classpy.wasm.WasmBinComponent; +import com.github.zxh.classpy.wasm.WasmBinReader; +import com.github.zxh.classpy.wasm.types.FuncType; +import com.github.zxh.classpy.wasm.types.Limits; +import com.github.zxh.classpy.wasm.types.TableType; +import com.github.zxh.classpy.wasm.values.Byte; +import com.github.zxh.classpy.wasm.values.U32; + +public class Section extends WasmBinComponent { + + protected void readContent(WasmBinReader reader) { + int id = readID(reader); + int size = readU32(reader, "size"); + readContents(reader, id, size); + } + + private int readID(WasmBinReader reader) { + Byte id = new Byte(); + add("id", id); + id.read(reader); + id.setDesc(Integer.toString(id.getValue())); + return id.getValue(); + } + + private void readContents(WasmBinReader reader, + int id, int size) { + if (id == 0) { + setName("custom section"); + readBytes(reader, "contents", size); + } else if (id == 1) { + setName("type section"); + readVector(reader, "types", FuncType::new); + } else if (id == 2) { + setName("import section"); + readVector(reader, "imports", Import::new); + } else if (id == 3) { + setName("function section"); + readVector(reader, "functions", U32::new); + } else if (id == 4) { + setName("table section"); + readVector(reader, "tables", TableType::new); + } else if (id == 5) { + setName("memory section"); + readVector(reader, "memories", Limits::new); + } else if (id == 6) { + setName("global section"); + readVector(reader, "globals", Global::new); + } else if (id == 7) { + setName("export section"); + readVector(reader, "exports", Export::new); + } else if (id == 8) { + setName("start section"); + reader.readU32(); + } else if (id == 9) { + setName("element section"); + readVector(reader, "elements", Element::new); + } else if (id == 10) { + setName("code section"); + readVector(reader, "codes", Code::new); + } else if (id == 11) { + setName("data section"); + readVector(reader, "datas", Data::new); + } else { + throw new ParseException("Invalid section id: " + id); + } + } + +} diff --git a/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/types/BlockType.java b/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/types/BlockType.java new file mode 100644 index 00000000..346ae1c1 --- /dev/null +++ b/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/types/BlockType.java @@ -0,0 +1,22 @@ +package com.github.zxh.classpy.wasm.types; + +import com.github.zxh.classpy.common.ParseException; +import com.github.zxh.classpy.wasm.WasmBinComponent; +import com.github.zxh.classpy.wasm.WasmBinReader; + +public class BlockType extends WasmBinComponent { + + protected void readContent(WasmBinReader reader) { + byte valType = reader.readByte(); + switch (valType) { + case 0x40: setDesc(""); break; + case 0x7F: setDesc("i32"); break; + case 0x7E: setDesc("i64"); break; + case 0x7D: setDesc("f32"); break; + case 0x7C: setDesc("f64"); break; + default: throw new ParseException( + String.format("Invalid block type: 0x%02X", valType)); + } + } + +} diff --git a/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/types/FuncType.java b/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/types/FuncType.java new file mode 100644 index 00000000..ef47d1e6 --- /dev/null +++ b/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/types/FuncType.java @@ -0,0 +1,29 @@ +package com.github.zxh.classpy.wasm.types; + +import com.github.zxh.classpy.common.FileComponent; +import com.github.zxh.classpy.wasm.WasmBinComponent; + +import java.util.stream.Collectors; + +public class FuncType extends WasmBinComponent { + + { + _byte(null, (byte) 0x60); + vector("parameters", ValType::new); + vector("results", ValType::new); + } + + @Override + protected void postRead() { + String params = get("parameters").getComponents().stream() + .skip(1) + .map(FileComponent::getDesc) + .collect(Collectors.joining(",", "(", ")")); + String results = get("results").getComponents().stream() + .skip(1) + .map(FileComponent::getDesc) + .collect(Collectors.joining(",", "(", ")")); + setDesc(params + "->" + results); + } + +} diff --git a/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/types/GlobalType.java b/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/types/GlobalType.java new file mode 100644 index 00000000..310c97d3 --- /dev/null +++ b/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/types/GlobalType.java @@ -0,0 +1,12 @@ +package com.github.zxh.classpy.wasm.types; + +import com.github.zxh.classpy.wasm.WasmBinComponent; + +public class GlobalType extends WasmBinComponent { + + { + valType("valtype"); + _byte("mut", (byte) 0x00, (byte) 0x01); + } + +} diff --git a/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/types/Limits.java b/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/types/Limits.java new file mode 100644 index 00000000..3494a286 --- /dev/null +++ b/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/types/Limits.java @@ -0,0 +1,21 @@ +package com.github.zxh.classpy.wasm.types; + +import com.github.zxh.classpy.wasm.WasmBinComponent; +import com.github.zxh.classpy.wasm.WasmBinReader; + +public class Limits extends WasmBinComponent { + + @Override + protected void readContent(WasmBinReader reader) { + int flag = readByte(reader, "flag", (byte) 0, (byte) 1); + + int min = readU32(reader, "min"); + setDesc("{" + min + ", }"); + + if (flag == 1) { + int max = readU32(reader, "max"); + setDesc("{" + min + ", " + max + "}"); + } + } + +} diff --git a/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/types/TableType.java b/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/types/TableType.java new file mode 100644 index 00000000..8803b029 --- /dev/null +++ b/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/types/TableType.java @@ -0,0 +1,13 @@ +package com.github.zxh.classpy.wasm.types; + +import com.github.zxh.classpy.wasm.WasmBinComponent; + +public class TableType extends WasmBinComponent { + + { + _byte("elemtype", (byte) 0x70); + add("limits", new Limits()); + setName("table"); + } + +} diff --git a/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/types/ValType.java b/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/types/ValType.java new file mode 100644 index 00000000..9b0d762e --- /dev/null +++ b/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/types/ValType.java @@ -0,0 +1,21 @@ +package com.github.zxh.classpy.wasm.types; + +import com.github.zxh.classpy.common.ParseException; +import com.github.zxh.classpy.wasm.WasmBinComponent; +import com.github.zxh.classpy.wasm.WasmBinReader; + +public class ValType extends WasmBinComponent { + + protected void readContent(WasmBinReader reader) { + byte valType = reader.readByte(); + switch (valType) { + case 0x7F: setDesc("i32"); break; + case 0x7E: setDesc("i64"); break; + case 0x7D: setDesc("f32"); break; + case 0x7C: setDesc("f64"); break; + default: throw new ParseException( + String.format("Invalid value type: 0x%02X", valType)); + } + } + +} diff --git a/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/values/Byte.java b/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/values/Byte.java new file mode 100644 index 00000000..f531cabe --- /dev/null +++ b/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/values/Byte.java @@ -0,0 +1,46 @@ +package com.github.zxh.classpy.wasm.values; + +import com.github.zxh.classpy.common.ParseException; +import com.github.zxh.classpy.wasm.WasmBinComponent; +import com.github.zxh.classpy.wasm.WasmBinReader; + +public class Byte extends WasmBinComponent { + + private final byte[] expectedValues; + private byte value; + + public Byte() { + this.expectedValues = null; + } + + public Byte(byte... expectedValues) { + this.expectedValues = expectedValues; + } + + public int getValue() { + return value & 0xFF; + } + + @Override + protected void readContent(WasmBinReader reader) { + value = reader.readByte(); + setDesc(String.format("0x%02X", value)); + checkValue(); + } + + private void checkValue() { + if (expectedValues == null || expectedValues.length == 0) { + return; + } + + for (byte expectedValue : expectedValues) { + if (expectedValue == value) { + return; + } + } + + throw new ParseException(String.format( + "Unexpected byte: 0x%02X", value)); + } + +} diff --git a/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/values/Bytes.java b/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/values/Bytes.java new file mode 100644 index 00000000..5b36cbb7 --- /dev/null +++ b/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/values/Bytes.java @@ -0,0 +1,23 @@ +package com.github.zxh.classpy.wasm.values; + +import com.github.zxh.classpy.wasm.WasmBinComponent; +import com.github.zxh.classpy.wasm.WasmBinReader; + +public class Bytes extends WasmBinComponent { + + private final int n; + private byte[] bytes; + + public Bytes(int n) { + this.n = n; + } + + public byte[] getBytes() { + return bytes; + } + + protected void readContent(WasmBinReader reader) { + bytes = reader.readBytes(n); + } + +} diff --git a/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/values/Name.java b/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/values/Name.java new file mode 100644 index 00000000..14d1f615 --- /dev/null +++ b/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/values/Name.java @@ -0,0 +1,17 @@ +package com.github.zxh.classpy.wasm.values; + +import com.github.zxh.classpy.wasm.WasmBinComponent; +import com.github.zxh.classpy.wasm.WasmBinReader; + +import java.nio.charset.StandardCharsets; + +public class Name extends WasmBinComponent { + + protected void readContent(WasmBinReader reader) { + int length = readU32(reader, "length"); + byte[] bytes = readBytes(reader, "bytes", length); + String name = new String(bytes, StandardCharsets.UTF_8); + setDesc(name); + } + +} diff --git a/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/values/S32.java b/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/values/S32.java new file mode 100644 index 00000000..8d300c20 --- /dev/null +++ b/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/values/S32.java @@ -0,0 +1,23 @@ +package com.github.zxh.classpy.wasm.values; + +import com.github.zxh.classpy.wasm.WasmBinComponent; +import com.github.zxh.classpy.wasm.WasmBinReader; + +public class S32 extends WasmBinComponent { + + private long value; + + public long getValue() { + return value; + } + + public int getIntValue() { + return (int) value; + } + + protected void readContent(WasmBinReader reader) { + value = reader.readS32(); + setDesc(Long.toString(value)); + } + +} diff --git a/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/values/S64.java b/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/values/S64.java new file mode 100644 index 00000000..2a82e5b7 --- /dev/null +++ b/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/values/S64.java @@ -0,0 +1,23 @@ +package com.github.zxh.classpy.wasm.values; + +import com.github.zxh.classpy.wasm.WasmBinComponent; +import com.github.zxh.classpy.wasm.WasmBinReader; + +public class S64 extends WasmBinComponent { + + private long value; + + public long getValue() { + return value; + } + + public int getIntValue() { + return (int) value; + } + + protected void readContent(WasmBinReader reader) { + value = reader.readS64(); + setDesc(Long.toString(value)); + } + +} diff --git a/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/values/U32.java b/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/values/U32.java new file mode 100644 index 00000000..885a407a --- /dev/null +++ b/classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/values/U32.java @@ -0,0 +1,23 @@ +package com.github.zxh.classpy.wasm.values; + +import com.github.zxh.classpy.wasm.WasmBinComponent; +import com.github.zxh.classpy.wasm.WasmBinReader; + +public class U32 extends WasmBinComponent { + + private long value; + + public long getValue() { + return value; + } + + public int getIntValue() { + return (int) value; + } + + protected void readContent(WasmBinReader reader) { + value = reader.readU32(); + setDesc(Long.toString(value)); + } + +} diff --git a/settings.gradle b/settings.gradle index 62eeb5e3..52a03dd3 100644 --- a/settings.gradle +++ b/settings.gradle @@ -3,4 +3,5 @@ rootProject.name = 'classpy' include 'classpy-common' include 'classpy-classfile' include 'classpy-binarychunk' +include 'classpy-wasm' include 'classpy-gui' \ No newline at end of file