├── .gitignore ├── LICENSE.txt ├── README.md ├── build.gradle ├── classpy-binarychunk ├── build.gradle └── src │ ├── main │ └── java │ │ ├── com │ │ └── github │ │ │ └── zxh │ │ │ └── classpy │ │ │ ├── lua │ │ │ └── BinaryChunkParser.java │ │ │ ├── lua53 │ │ │ ├── binarychunk │ │ │ │ ├── BinaryChunkFile.java │ │ │ │ ├── BinaryChunkParser.java │ │ │ │ ├── BinaryChunkPart.java │ │ │ │ ├── BinaryChunkReader.java │ │ │ │ ├── datatype │ │ │ │ │ ├── Bytes.java │ │ │ │ │ ├── CInt.java │ │ │ │ │ ├── CSizet.java │ │ │ │ │ ├── LuByte.java │ │ │ │ │ ├── LuaInt.java │ │ │ │ │ ├── LuaNum.java │ │ │ │ │ ├── LuaStr.java │ │ │ │ │ └── Table.java │ │ │ │ └── part │ │ │ │ │ ├── Constant.java │ │ │ │ │ ├── Debug.java │ │ │ │ │ ├── Function.java │ │ │ │ │ ├── Header.java │ │ │ │ │ └── Instruction.java │ │ │ └── vm │ │ │ │ ├── LuaType.java │ │ │ │ ├── OpArgMask.java │ │ │ │ ├── OpCode.java │ │ │ │ └── OpMode.java │ │ │ └── lua54 │ │ │ ├── binarychunk │ │ │ ├── BinaryChunkFile.java │ │ │ ├── BinaryChunkParser.java │ │ │ ├── BinaryChunkPart.java │ │ │ ├── BinaryChunkReader.java │ │ │ ├── datatype │ │ │ │ ├── Bytes.java │ │ │ │ ├── LuByte.java │ │ │ │ ├── LuaInt.java │ │ │ │ ├── LuaNum.java │ │ │ │ ├── LuaStr.java │ │ │ │ ├── VarInt.java │ │ │ │ └── Vec.java │ │ │ └── part │ │ │ │ ├── Constant.java │ │ │ │ ├── Debug.java │ │ │ │ ├── Function.java │ │ │ │ ├── Header.java │ │ │ │ └── Instruction.java │ │ │ └── vm │ │ │ ├── LuaType.java │ │ │ ├── OpCode.java │ │ │ └── OpMode.java │ │ └── module-info.java │ └── test │ ├── lua │ ├── basic_types.lua │ ├── blank.lua │ ├── constants.lua │ ├── functions.lua │ ├── hello_world.lua │ └── operators.lua │ └── resources │ ├── luac53out │ ├── basic_types.luac │ ├── blank.luac │ ├── functions.luac │ ├── hello_world.luac │ ├── hello_world_no_debug.luac │ └── operators.luac │ └── luac54out │ └── constants.luac ├── classpy-bitcoin ├── build.gradle └── src │ └── main │ └── java │ ├── com │ └── github │ │ └── zxh │ │ └── classpy │ │ └── bitcoin │ │ ├── Block.java │ │ ├── BlockParser.java │ │ ├── BlockPart.java │ │ ├── BlockReader.java │ │ ├── Script.java │ │ ├── Transaction.java │ │ ├── TxParser.java │ │ └── types │ │ ├── Bytes.java │ │ ├── Hash.java │ │ ├── Table.java │ │ ├── UInt32.java │ │ ├── UInt64.java │ │ └── VarInt.java │ └── module-info.java ├── classpy-classfile ├── build.gradle └── src │ ├── main │ └── java │ │ ├── com │ │ └── github │ │ │ └── zxh │ │ │ └── classpy │ │ │ └── classfile │ │ │ ├── ClassFile.java │ │ │ ├── ClassFileParser.java │ │ │ ├── ClassFilePart.java │ │ │ ├── ClassFileReader.java │ │ │ ├── FieldInfo.java │ │ │ ├── MethodInfo.java │ │ │ ├── attribute │ │ │ ├── AnnotationDefaultAttribute.java │ │ │ ├── AttributeFactory.java │ │ │ ├── AttributeInfo.java │ │ │ ├── BootstrapMethodsAttribute.java │ │ │ ├── CodeAttribute.java │ │ │ ├── ConstantValueAttribute.java │ │ │ ├── DeprecatedAttribute.java │ │ │ ├── EnclosingMethodAttribute.java │ │ │ ├── ExceptionsAttribute.java │ │ │ ├── InnerClassesAttribute.java │ │ │ ├── LineNumberTableAttribute.java │ │ │ ├── LocalVariableTableAttribute.java │ │ │ ├── LocalVariableTypeTableAttribute.java │ │ │ ├── MethodParametersAttribute.java │ │ │ ├── ModuleAttribute.java │ │ │ ├── ModuleMainClassAttribute.java │ │ │ ├── ModulePackagesAttribute.java │ │ │ ├── RuntimeVisibleAnnotationsAttribute.java │ │ │ ├── RuntimeVisibleParameterAnnotationsAttribute.java │ │ │ ├── RuntimeVisibleTypeAnnotationsAttribute.java │ │ │ ├── SignatureAttribute.java │ │ │ ├── SourceDebugExtensionAttribute.java │ │ │ ├── SourceFileAttribute.java │ │ │ ├── StackMapTableAttribute.java │ │ │ ├── SyntheticAttribute.java │ │ │ └── UndefinedAttribute.java │ │ │ ├── bytecode │ │ │ ├── Bipush.java │ │ │ ├── Branch.java │ │ │ ├── Iinc.java │ │ │ ├── Instruction.java │ │ │ ├── InstructionCp1.java │ │ │ ├── InstructionCp2.java │ │ │ ├── InstructionFactory.java │ │ │ ├── InstructionU1.java │ │ │ ├── InvokeDynamic.java │ │ │ ├── InvokeInterface.java │ │ │ ├── LookupSwitch.java │ │ │ ├── Multianewarray.java │ │ │ ├── NewArray.java │ │ │ ├── Sipush.java │ │ │ ├── TableSwitch.java │ │ │ └── Wide.java │ │ │ ├── constant │ │ │ ├── ConstantClassInfo.java │ │ │ ├── ConstantDoubleInfo.java │ │ │ ├── ConstantFactory.java │ │ │ ├── ConstantFieldrefInfo.java │ │ │ ├── ConstantFloatInfo.java │ │ │ ├── ConstantInfo.java │ │ │ ├── ConstantIntegerInfo.java │ │ │ ├── ConstantInterfaceMethodrefInfo.java │ │ │ ├── ConstantInvokeDynamicInfo.java │ │ │ ├── ConstantLongInfo.java │ │ │ ├── ConstantMethodHandleInfo.java │ │ │ ├── ConstantMethodTypeInfo.java │ │ │ ├── ConstantMethodrefInfo.java │ │ │ ├── ConstantModuleInfo.java │ │ │ ├── ConstantNameAndTypeInfo.java │ │ │ ├── ConstantPackageInfo.java │ │ │ ├── ConstantPool.java │ │ │ ├── ConstantStringInfo.java │ │ │ └── ConstantUtf8Info.java │ │ │ ├── datatype │ │ │ ├── Bytes.java │ │ │ ├── Table.java │ │ │ ├── U1.java │ │ │ ├── U1CpIndex.java │ │ │ ├── U1Hex.java │ │ │ ├── U2.java │ │ │ ├── U2AccessFlags.java │ │ │ ├── U2CpIndex.java │ │ │ ├── U4.java │ │ │ ├── U4Hex.java │ │ │ └── UInt.java │ │ │ └── jvm │ │ │ ├── AccessFlagType.java │ │ │ ├── AccessFlags.java │ │ │ ├── Mutf8Decoder.java │ │ │ ├── Opcode.java │ │ │ └── RefKind.java │ │ └── module-info.java │ └── test │ └── java │ └── com │ └── github │ └── zxh │ └── classpy │ └── classfile │ ├── ClassComponentTest.java │ ├── ClassFileTest.java │ ├── Mutf8DecoderTest.java │ └── testclasses │ ├── AnnotatedClass.java │ ├── ByteCode.java │ ├── CodeAttr.java │ ├── ConstantPool.java │ ├── GenericClass.java │ ├── HelloWorld.java │ ├── MyInterface.java │ ├── SimpleAttr.java │ ├── SimpleClass.java │ ├── TypeAnnotatedClass.java │ └── annotations │ ├── MyClassAnnotation.java │ ├── MyRuntimeAnnotation.java │ └── MyTypeAnnotation.java ├── classpy-common ├── build.gradle └── src │ ├── main │ └── java │ │ ├── com │ │ └── github │ │ │ └── zxh │ │ │ └── classpy │ │ │ ├── common │ │ │ ├── BytesReader.java │ │ │ ├── FileParser.java │ │ │ ├── FilePart.java │ │ │ ├── FixedInt.java │ │ │ ├── ParseException.java │ │ │ └── ReadableFilePart.java │ │ │ └── helper │ │ │ ├── StringHelper.java │ │ │ └── UrlHelper.java │ │ └── module-info.java │ └── test │ └── java │ └── com │ └── github │ └── zxh │ └── classpy │ ├── BytesReaderTest.java │ └── StringHelperTest.java ├── classpy-gui ├── build.gradle └── src │ └── main │ ├── java │ ├── com │ │ └── github │ │ │ └── zxh │ │ │ └── classpy │ │ │ └── gui │ │ │ ├── AboutDialog.java │ │ │ ├── ClasspyApp.java │ │ │ ├── Main.java │ │ │ ├── MyFileChooser.java │ │ │ ├── MyMenuBar.java │ │ │ ├── events │ │ │ ├── CloseAllTabs.java │ │ │ ├── EventBus.java │ │ │ ├── OpenAboutDialog.java │ │ │ ├── OpenFile.java │ │ │ ├── OpenNewWindow.java │ │ │ └── UpdateRecentFiles.java │ │ │ ├── fs │ │ │ ├── BaseTreeNode.java │ │ │ ├── DirTreeItem.java │ │ │ ├── DirTreeNode.java │ │ │ ├── DirTreeView.java │ │ │ ├── ZipTreeItem.java │ │ │ ├── ZipTreeLoader.java │ │ │ ├── ZipTreeNode.java │ │ │ └── ZipTreeView.java │ │ │ ├── parsed │ │ │ ├── BytesBar.java │ │ │ ├── HexPane.java │ │ │ ├── HexText.java │ │ │ ├── ParsedTreeItem.java │ │ │ └── ParsedViewerPane.java │ │ │ └── support │ │ │ ├── FileType.java │ │ │ ├── FileTypeInferer.java │ │ │ ├── ImageHelper.java │ │ │ ├── OpenFileResult.java │ │ │ ├── OpenFileTask.java │ │ │ ├── RecentFile.java │ │ │ └── RecentFiles.java │ └── module-info.java │ └── resources │ ├── bitcoin.png │ ├── classpy.css │ ├── clock.png │ ├── file.png │ ├── folder.png │ ├── jar.png │ ├── java.png │ ├── jmod.png │ ├── lua.png │ ├── spy128.png │ ├── spy16.png │ ├── spy32.png │ └── wasm.png ├── classpy-wasm ├── build.gradle └── src │ ├── main │ └── java │ │ ├── com │ │ └── github │ │ │ └── zxh │ │ │ └── classpy │ │ │ └── wasm │ │ │ ├── Vector.java │ │ │ ├── WasmBinFile.java │ │ │ ├── WasmBinParser.java │ │ │ ├── WasmBinPart.java │ │ │ ├── WasmBinReader.java │ │ │ ├── instructions │ │ │ ├── Expr.java │ │ │ └── Instr.java │ │ │ ├── sections │ │ │ ├── Code.java │ │ │ ├── Data.java │ │ │ ├── Element.java │ │ │ ├── Export.java │ │ │ ├── Global.java │ │ │ ├── Import.java │ │ │ └── Section.java │ │ │ ├── types │ │ │ ├── BlockType.java │ │ │ ├── FuncType.java │ │ │ ├── GlobalType.java │ │ │ ├── Limits.java │ │ │ ├── TableType.java │ │ │ └── ValType.java │ │ │ └── values │ │ │ ├── Byte.java │ │ │ ├── Bytes.java │ │ │ ├── Index.java │ │ │ ├── Name.java │ │ │ ├── S32.java │ │ │ ├── S64.java │ │ │ └── U32.java │ │ └── module-info.java │ └── test │ └── java │ └── com │ └── github │ └── zxh │ └── classpy │ └── wasm │ ├── FuncTest.java │ └── LEB128Test.java ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── screenshot.png ├── screenshot2.png ├── screenshot3.png ├── screenshot4.png └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle/ 2 | .nb-gradle/ 3 | .idea/ 4 | build/ 5 | out/ 6 | *.sublime-workspace 7 | *.iml 8 | .DS_Store 9 | target/ -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 Xiuhong Zhang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Classpy 2 | 3 | Classpy is a GUI tool for investigating Java class file, Lua binary chunk, Wasm binary code, and other binary file formats. 4 | 5 | 6 | 7 | ## Inspiration 8 | 9 | This tool is mainly inspired by [javap](http://docs.oracle.com/javase/8/docs/technotes/tools/windows/javap.html) and [JavaClassViewer](http://www.codeproject.com/Articles/35915/Java-Class-Viewer). I reinvent the wheel for the following two reasons: 10 | 11 | 1. Learn Java class file format and bytecode through parsing it 12 | 2. Try JavaFX 8 13 | 14 | 15 | 16 | ## Features 17 | 18 | * Understands class files described by [JVMS9](https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html) 19 | * Supports [Lua](https://www.lua.org/) 5.3 binary chunk format 20 | * Supports [Wasm](https://webassembly.org/) binary format 21 | * Supports [Bitcoin](https://en.wikipedia.org/wiki/Bitcoin) raw block and transaction format 22 | * Displays parsed binary file as tree and hex text 23 | * The corresponding hex text is highlighted when you select a tree node 24 | 25 | 26 | 27 | ## Quick Start 28 | 29 | Just for macos with brew 30 | 31 | - Install 32 | ```shell 33 | brew tap guxingke/repo && brew install classpy 34 | ``` 35 | - Try 36 | - open default gui window 37 | ```shell 38 | classpy 39 | ``` 40 | - open gui window with args 41 | ```shell 42 | echo "public class HelloWorld { public static void main(String[] args) { System.out.println(\"Hello World\"); } }" > HelloWorld.java 43 | $JAVA_HOME/bin/javac HelloWorld.java 44 | 45 | classpy HelloWorld.class 46 | ``` 47 | 48 | 49 | 50 | ## Requirements 51 | 52 | Java 15 (checkout branch `java8` if you stuck on Java 8) 53 | 54 | 55 | 56 | ## Build 57 | 58 | ```shell 59 | cd path/to/classpy 60 | ./gradlew fatJar 61 | # java -jar path/to/classpy/classpy-gui/build/libs/classpy-fat-jar-0.10.0.jar 62 | ``` 63 | 64 | 65 | 66 | ## Run 67 | 68 | ```shell 69 | cd path/to/classpy 70 | ./gradlew run 71 | ``` 72 | 73 | 74 | 75 | ## Screenshots 76 | 77 | ![Screenshot1](https://raw.githubusercontent.com/zxh0/classpy/master/screenshot.png) 78 | ![Screenshot2](https://raw.githubusercontent.com/zxh0/classpy/master/screenshot2.png) 79 | ![Screenshot3](https://raw.githubusercontent.com/zxh0/classpy/master/screenshot3.png) 80 | ![Screenshot4](https://raw.githubusercontent.com/zxh0/classpy/master/screenshot4.png) 81 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | subprojects { 2 | version = '0.10.0' 3 | group = 'classpy' 4 | 5 | repositories { 6 | jcenter() 7 | } 8 | 9 | plugins.withType(JavaPlugin).configureEach { 10 | java { 11 | modularity.inferModulePath = true 12 | } 13 | } 14 | 15 | tasks.withType(JavaCompile) { 16 | options.encoding = 'UTF-8' 17 | // options.compilerArgs += '--enable-preview' 18 | } 19 | tasks.withType(JavaExec) { 20 | jvmArgs += '--enable-preview' 21 | // jvmArgs += '-XX:+ShowCodeDetailsInExceptionMessages' 22 | } 23 | 24 | tasks.withType(Test).configureEach { 25 | jvmArgs += "--enable-preview" 26 | useJUnitPlatform() 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /classpy-binarychunk/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java-library' 3 | } 4 | 5 | dependencies { 6 | implementation project(':classpy-common') 7 | } -------------------------------------------------------------------------------- /classpy-binarychunk/src/main/java/com/github/zxh/classpy/lua/BinaryChunkParser.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.lua; 2 | 3 | import com.github.zxh.classpy.common.FileParser; 4 | import com.github.zxh.classpy.common.FilePart; 5 | 6 | public class BinaryChunkParser implements FileParser { 7 | 8 | private final com.github.zxh.classpy.lua53.binarychunk.BinaryChunkParser lua53BCParser = new com.github.zxh.classpy.lua53.binarychunk.BinaryChunkParser(); 9 | private final com.github.zxh.classpy.lua54.binarychunk.BinaryChunkParser lua54BCParser = new com.github.zxh.classpy.lua54.binarychunk.BinaryChunkParser(); 10 | 11 | @Override 12 | public FilePart parse(byte[] data) { 13 | if (data.length > 6 && data[4] == 0x54) { 14 | return lua54BCParser.parse(data); 15 | } else { 16 | return lua53BCParser.parse(data); 17 | } 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /classpy-binarychunk/src/main/java/com/github/zxh/classpy/lua53/binarychunk/BinaryChunkFile.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.lua53.binarychunk; 2 | 3 | import com.github.zxh.classpy.lua53.binarychunk.part.Function; 4 | import com.github.zxh.classpy.lua53.binarychunk.part.Header; 5 | import com.github.zxh.classpy.lua53.binarychunk.datatype.LuByte; 6 | 7 | /** 8 | * Lua 5.3 binary chunk file - /lua/src/ldump.c#luaU_dump() 9 | */ 10 | public class BinaryChunkFile extends BinaryChunkPart { 11 | 12 | { 13 | add("header", new Header()); 14 | add("size_upvalues", new LuByte()); 15 | add("main", new Function()); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /classpy-binarychunk/src/main/java/com/github/zxh/classpy/lua53/binarychunk/BinaryChunkParser.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.lua53.binarychunk; 2 | 3 | import com.github.zxh.classpy.common.FilePart; 4 | import com.github.zxh.classpy.common.FileParser; 5 | 6 | public class BinaryChunkParser implements FileParser { 7 | 8 | @Override 9 | public FilePart parse(byte[] data) { 10 | BinaryChunkReader reader = new BinaryChunkReader(data); 11 | BinaryChunkFile bc = new BinaryChunkFile(); 12 | bc.read(reader); 13 | postRead(bc); 14 | return bc; 15 | } 16 | 17 | private static void postRead(BinaryChunkPart bc) { 18 | for (FilePart c : bc.getParts()) { 19 | postRead((BinaryChunkPart) c); 20 | } 21 | bc.postRead(); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /classpy-binarychunk/src/main/java/com/github/zxh/classpy/lua53/binarychunk/BinaryChunkPart.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.lua53.binarychunk; 2 | 3 | import com.github.zxh.classpy.common.ReadableFilePart; 4 | import com.github.zxh.classpy.lua53.binarychunk.datatype.*; 5 | 6 | import java.util.function.Supplier; 7 | 8 | /** 9 | * Component of lua binary chunk file. 10 | */ 11 | public class BinaryChunkPart extends ReadableFilePart { 12 | 13 | protected void postRead() { 14 | 15 | } 16 | 17 | public void lu_byte(String name) { 18 | super.add(name, new LuByte()); 19 | } 20 | 21 | public void lua_int(String name) { 22 | super.add(name, new LuaInt()); 23 | } 24 | 25 | public void cint(String name) { 26 | super.add(name, new CInt()); 27 | } 28 | 29 | public void lua_num(String name) { 30 | super.add(name, new LuaNum()); 31 | } 32 | 33 | public void str(String name) { 34 | super.add(name, new LuaStr()); 35 | } 36 | 37 | public void bytes(String name, int n) { 38 | super.add(name, new Bytes(n)); 39 | } 40 | 41 | public void table(String name, Supplier partSupplier) { 42 | super.add(name, new Table(partSupplier)); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /classpy-binarychunk/src/main/java/com/github/zxh/classpy/lua53/binarychunk/BinaryChunkReader.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.lua53.binarychunk; 2 | 3 | import com.github.zxh.classpy.common.BytesReader; 4 | 5 | import java.nio.ByteOrder; 6 | 7 | public class BinaryChunkReader extends BytesReader { 8 | 9 | private int sizetSize = 4; 10 | private int cIntSize = 4; 11 | private int luaIntSize = 8; 12 | private int luaNumSize = 8; 13 | 14 | public BinaryChunkReader(byte[] data) { 15 | super(data, ByteOrder.LITTLE_ENDIAN); 16 | } 17 | 18 | // setters 19 | public void setSizetSize(int sizetSize) {this.sizetSize = sizetSize;} 20 | public void setCIntSize(int cIntSize) {this.cIntSize = cIntSize;} 21 | public void setLuaIntSize(int luaIntSize) {this.luaIntSize = luaIntSize;} 22 | public void setLuaNumSize(int luaNumSize) {this.luaNumSize = luaNumSize;} 23 | 24 | public long readSizet() { 25 | return sizetSize == 8 ? super.readFixedI64() : super.readFixedU32(); 26 | } 27 | 28 | public long readCInt() { 29 | return cIntSize == 8 ? super.readFixedI64() : super.readFixedI32(); 30 | } 31 | 32 | public long readLuaInt() { 33 | return luaIntSize == 8 ? super.readFixedI64() : super.readFixedI32(); 34 | } 35 | 36 | public double readLuaNum() { 37 | return luaNumSize == 8 ? super.readF64(): super.readF32(); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /classpy-binarychunk/src/main/java/com/github/zxh/classpy/lua53/binarychunk/datatype/Bytes.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.lua53.binarychunk.datatype; 2 | 3 | import com.github.zxh.classpy.lua53.binarychunk.BinaryChunkPart; 4 | import com.github.zxh.classpy.lua53.binarychunk.BinaryChunkReader; 5 | 6 | /** 7 | * byte array. 8 | */ 9 | public class Bytes extends BinaryChunkPart { 10 | 11 | private final int n; 12 | private byte[] bytes; 13 | 14 | public Bytes(int n) { 15 | this.n = n; 16 | } 17 | 18 | public byte[] getBytes() { 19 | return bytes; 20 | } 21 | 22 | @Override 23 | protected void readContent(BinaryChunkReader reader) { 24 | bytes = reader.readBytes(n); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /classpy-binarychunk/src/main/java/com/github/zxh/classpy/lua53/binarychunk/datatype/CInt.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.lua53.binarychunk.datatype; 2 | 3 | import com.github.zxh.classpy.lua53.binarychunk.BinaryChunkPart; 4 | import com.github.zxh.classpy.lua53.binarychunk.BinaryChunkReader; 5 | 6 | /** 7 | * c int - /lua/src/ldump.c#DumpInt() 8 | */ 9 | public class CInt extends BinaryChunkPart { 10 | 11 | private long value; 12 | 13 | public long getValue() { 14 | return value; 15 | } 16 | 17 | @Override 18 | protected void readContent(BinaryChunkReader reader) { 19 | value = reader.readCInt(); 20 | setDesc(Long.toString(value)); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /classpy-binarychunk/src/main/java/com/github/zxh/classpy/lua53/binarychunk/datatype/CSizet.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.lua53.binarychunk.datatype; 2 | 3 | import com.github.zxh.classpy.lua53.binarychunk.BinaryChunkPart; 4 | import com.github.zxh.classpy.lua53.binarychunk.BinaryChunkReader; 5 | 6 | /** 7 | * c size_t. 8 | */ 9 | public class CSizet extends BinaryChunkPart { 10 | 11 | private long value; 12 | 13 | public long getValue() { 14 | return value; 15 | } 16 | 17 | @Override 18 | protected void readContent(BinaryChunkReader reader) { 19 | value = reader.readSizet(); 20 | setDesc(Long.toString(value)); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /classpy-binarychunk/src/main/java/com/github/zxh/classpy/lua53/binarychunk/datatype/LuByte.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.lua53.binarychunk.datatype; 2 | 3 | import com.github.zxh.classpy.lua53.binarychunk.BinaryChunkPart; 4 | import com.github.zxh.classpy.lua53.binarychunk.BinaryChunkReader; 5 | 6 | /** 7 | * lu_byte - /lua/src/ldump.c#DumpByte() 8 | */ 9 | public class LuByte extends BinaryChunkPart { 10 | 11 | private int value; 12 | 13 | public int getValue() { 14 | return value; 15 | } 16 | 17 | @Override 18 | protected void readContent(BinaryChunkReader reader) { 19 | value = reader.readFixedU8(); 20 | super.setDesc(Integer.toString(value)); 21 | 22 | // TODO 23 | if (super.getName() != null) { 24 | switch (super.getName()) { 25 | case "sizeof(int)" -> reader.setCIntSize(value); 26 | case "sizeof(size_t)" -> reader.setSizetSize(value); 27 | case "sizeof(lua_Integer)" -> reader.setLuaIntSize(value); 28 | case "sizeof(lua_Number)" -> reader.setLuaNumSize(value); 29 | } 30 | } 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /classpy-binarychunk/src/main/java/com/github/zxh/classpy/lua53/binarychunk/datatype/LuaInt.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.lua53.binarychunk.datatype; 2 | 3 | import com.github.zxh.classpy.lua53.binarychunk.BinaryChunkPart; 4 | import com.github.zxh.classpy.lua53.binarychunk.BinaryChunkReader; 5 | 6 | /** 7 | * lua_Integer - /lua/src/ldump.c#DumpInteger() 8 | */ 9 | public class LuaInt extends BinaryChunkPart { 10 | 11 | private long value; 12 | 13 | public long getValue() { 14 | return value; 15 | } 16 | 17 | @Override 18 | protected void readContent(BinaryChunkReader reader) { 19 | value = reader.readLuaInt(); 20 | setDesc(Long.toString(value)); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /classpy-binarychunk/src/main/java/com/github/zxh/classpy/lua53/binarychunk/datatype/LuaNum.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.lua53.binarychunk.datatype; 2 | 3 | import com.github.zxh.classpy.lua53.binarychunk.BinaryChunkPart; 4 | import com.github.zxh.classpy.lua53.binarychunk.BinaryChunkReader; 5 | 6 | /** 7 | * lua_Number - /lua/src/ldump.c#DumpNumber() 8 | */ 9 | public class LuaNum extends BinaryChunkPart { 10 | 11 | private double value; 12 | 13 | @Override 14 | protected void readContent(BinaryChunkReader reader) { 15 | value = reader.readLuaNum(); 16 | setDesc(Double.toString(value)); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /classpy-binarychunk/src/main/java/com/github/zxh/classpy/lua53/binarychunk/datatype/LuaStr.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.lua53.binarychunk.datatype; 2 | 3 | import com.github.zxh.classpy.lua53.binarychunk.BinaryChunkPart; 4 | import com.github.zxh.classpy.lua53.binarychunk.BinaryChunkReader; 5 | 6 | /** 7 | * string in binary chunk - /lua/src/ldump.c#DumpString() 8 | */ 9 | public class LuaStr extends BinaryChunkPart { 10 | 11 | @Override 12 | protected void readContent(BinaryChunkReader reader) { 13 | LuByte size = new LuByte(); 14 | size.read(reader); 15 | 16 | if (size.getValue() == 0) { 17 | super.setDesc("NULL"); 18 | } else if (size.getValue() < 0xFF) { 19 | super.add("size", size); 20 | readStr(reader, size.getValue() - 1); 21 | } else { // size == 0xFF 22 | CSizet xsize = new CSizet(); 23 | xsize.read(reader); 24 | super.add("size", xsize); 25 | readStr(reader, (int) xsize.getValue() - 1); 26 | } 27 | } 28 | 29 | private void readStr(BinaryChunkReader reader, int bytesCount) { 30 | Bytes bytes = new Bytes(bytesCount); 31 | bytes.read(reader); 32 | super.add("bytes", bytes); 33 | 34 | String str = new String(bytes.getBytes()); // todo 35 | super.setDesc(str); 36 | bytes.setDesc(str); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /classpy-binarychunk/src/main/java/com/github/zxh/classpy/lua53/binarychunk/datatype/Table.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.lua53.binarychunk.datatype; 2 | 3 | import com.github.zxh.classpy.common.FilePart; 4 | import com.github.zxh.classpy.helper.StringHelper; 5 | import com.github.zxh.classpy.lua53.binarychunk.BinaryChunkPart; 6 | import com.github.zxh.classpy.lua53.binarychunk.BinaryChunkReader; 7 | 8 | import java.util.List; 9 | import java.util.function.Supplier; 10 | 11 | /** 12 | * Table in binary chunk. 13 | */ 14 | public class Table extends BinaryChunkPart { 15 | 16 | private final Supplier partSupplier; 17 | 18 | public Table(Supplier partSupplier) { 19 | this.partSupplier = partSupplier; 20 | } 21 | 22 | @Override 23 | protected void readContent(BinaryChunkReader reader) { 24 | CInt size = new CInt(); 25 | size.read(reader); 26 | super.add("size", size); 27 | 28 | for (int i = 0; i < size.getValue(); i++) { 29 | BinaryChunkPart c = partSupplier.get(); 30 | super.add(null, c); 31 | c.read(reader); 32 | } 33 | } 34 | 35 | @Override 36 | protected void postRead() { 37 | List kids = super.getParts(); 38 | int maxIdx = kids.size() - 1; 39 | for (int i = 1; i < kids.size(); i++) { 40 | FilePart kid = kids.get(i); 41 | if (kid.getName() == null) { 42 | kid.setName(StringHelper.formatIndex(maxIdx, i - 1)); 43 | } else { 44 | kid.setName(StringHelper.formatIndex(maxIdx, i - 1) 45 | + " (" + kid.getName() + ")"); 46 | } 47 | } 48 | super.setDesc("(" + maxIdx + ")"); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /classpy-binarychunk/src/main/java/com/github/zxh/classpy/lua53/binarychunk/part/Debug.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.lua53.binarychunk.part; 2 | 3 | import java.util.List; 4 | import com.github.zxh.classpy.common.FilePart; 5 | import com.github.zxh.classpy.lua53.binarychunk.BinaryChunkPart; 6 | import com.github.zxh.classpy.lua53.binarychunk.datatype.CInt; 7 | import com.github.zxh.classpy.lua53.binarychunk.datatype.LuaStr; 8 | 9 | /** 10 | * debug info - /lua/src/ldump.c#DumpDebug() 11 | */ 12 | public class Debug extends BinaryChunkPart { 13 | 14 | { 15 | table("line_info", CInt::new); 16 | table("loc_vars", LocVar::new); 17 | table("upvalues", LuaStr::new); 18 | } 19 | 20 | public long getLine(int pc) { 21 | List locVars = super.get("line_info").getParts(); 22 | if (pc + 1 >= locVars.size()) { 23 | return -1; 24 | } else { 25 | return ((CInt) locVars.get(pc + 1)).getValue(); 26 | } 27 | } 28 | 29 | public String getLocVarName(int idx) { 30 | List locVars = super.get("loc_vars").getParts(); 31 | if (idx + 1 >= locVars.size()) { 32 | return ""; 33 | } else { 34 | return ((LocVar) locVars.get(idx + 1)).getVarName(); 35 | } 36 | } 37 | 38 | public String getUpvalName(int idx) { 39 | List upvals = super.get("upvalues").getParts(); 40 | if (idx + 1 >= upvals.size()) { 41 | return ""; 42 | } else { 43 | return upvals.get(idx + 1).getDesc(); 44 | } 45 | } 46 | 47 | public static class LocVar extends BinaryChunkPart { 48 | 49 | { 50 | str ("var_name"); 51 | cint("start_pc"); 52 | cint("end_pc" ); 53 | } 54 | 55 | @Override 56 | protected void postRead() { 57 | setName(get("var_name").getDesc()); 58 | setDesc(get("start_pc").getDesc() 59 | + " ~ " + get("end_pc").getDesc()); 60 | } 61 | 62 | private String getVarName() { 63 | return super.get("var_name").getDesc(); 64 | } 65 | 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /classpy-binarychunk/src/main/java/com/github/zxh/classpy/lua53/binarychunk/part/Header.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.lua53.binarychunk.part; 2 | 3 | import java.util.Arrays; 4 | import com.github.zxh.classpy.common.ParseException; 5 | import com.github.zxh.classpy.lua53.binarychunk.BinaryChunkPart; 6 | import com.github.zxh.classpy.lua53.binarychunk.datatype.Bytes; 7 | import com.github.zxh.classpy.lua53.binarychunk.datatype.LuByte; 8 | import com.github.zxh.classpy.lua53.binarychunk.datatype.LuaInt; 9 | 10 | /** 11 | * Header. 12 | */ 13 | public class Header extends BinaryChunkPart { 14 | 15 | private final byte[] _luaSig = {0x1b, 'L', 'u', 'a'}; 16 | private final byte[] _luacData = {0x19, (byte) 0x93, '\r', '\n', 0x1a, '\n'}; 17 | 18 | { 19 | bytes ("LUA_SIGNATURE", 4); 20 | lu_byte("LUA_VERSION" ); 21 | lu_byte("LUAC_FORMAT" ); 22 | bytes ("LUAC_DATA", 6); 23 | lu_byte("sizeof(int)" ); 24 | lu_byte("sizeof(size_t)" ); 25 | lu_byte("sizeof(Instruction)"); 26 | lu_byte("sizeof(lua_Integer)"); 27 | lu_byte("sizeof(lua_Number)" ); 28 | lua_int("LUAC_INT" ); 29 | lua_num("LUAC_NUM" ); 30 | } 31 | 32 | @Override 33 | protected void postRead() { 34 | checkSignature(); 35 | checkLuacData(); 36 | LuByte luaVersion = (LuByte) super.get("LUA_VERSION"); 37 | luaVersion.setDesc("0x" + Integer.toHexString(luaVersion.getValue())); 38 | LuaInt luacInt = (LuaInt) super.get("LUAC_INT"); 39 | luacInt.setDesc("0x" + Long.toHexString(luacInt.getValue())); 40 | } 41 | 42 | private void checkSignature() { 43 | Bytes sig = (Bytes) super.get("LUA_SIGNATURE"); 44 | if (!Arrays.equals(sig.getBytes(), _luaSig)) { 45 | throw new ParseException("not a precompiled chunk!"); 46 | } else { 47 | sig.setDesc("\"\\x1bLua\""); 48 | } 49 | } 50 | 51 | private void checkLuacData() { 52 | Bytes luacData = (Bytes) super.get("LUAC_DATA"); 53 | if (!Arrays.equals(luacData.getBytes(), _luacData)) { 54 | throw new ParseException("corrupted!"); 55 | } else { 56 | luacData.setDesc("\"\\x19\\x93\\r\\n\\x1a\\n\""); 57 | } 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /classpy-binarychunk/src/main/java/com/github/zxh/classpy/lua53/vm/LuaType.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.lua53.vm; 2 | 3 | import com.github.zxh.classpy.common.ParseException; 4 | 5 | /** 6 | * Lua types. 7 | * - /lua/src/lua.h 8 | * - /lua/src/lobject.h 9 | */ 10 | public enum LuaType { 11 | 12 | /* basic types */ 13 | LUA_TNONE (-1), 14 | LUA_TNIL (0), 15 | LUA_TBOOLEAN (1), 16 | LUA_TLIGHTUSERDATA(2), 17 | LUA_TNUMBER (3), 18 | LUA_TSTRING (4), 19 | LUA_TTABLE (5), 20 | LUA_TFUNCTION (6), 21 | LUA_TUSERDATA (7), 22 | LUA_TTHREAD (8), 23 | 24 | /* Variant tags for numbers */ 25 | LUA_TNUMFLT(LUA_TNUMBER.type | (0 << 4)), /* float numbers */ 26 | LUA_TNUMINT(LUA_TNUMBER.type | (1 << 4)), /* integer numbers */ 27 | 28 | /* Variant tags for strings */ 29 | LUA_TSHRSTR(LUA_TSTRING.type | (0 << 4)), /* short strings */ 30 | LUA_TLNGSTR(LUA_TSTRING.type | (1 << 4)), /* long strings */ 31 | 32 | ; 33 | 34 | public final int type; 35 | 36 | LuaType(int type) { 37 | this.type = type; 38 | } 39 | 40 | public static LuaType valueOf(int type) { 41 | for (LuaType lt : values()) { 42 | if (lt.type == type) { 43 | return lt; 44 | } 45 | } 46 | throw new ParseException("Unknown lua type: " + type); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /classpy-binarychunk/src/main/java/com/github/zxh/classpy/lua53/vm/OpArgMask.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.lua53.vm; 2 | 3 | /* 4 | * masks for instruction properties. The format is: 5 | * bits 0-1: op mode 6 | * bits 2-3: C arg mode 7 | * bits 4-5: B arg mode 8 | * bit 6: instruction set register A 9 | * bit 7: operator is a test (next instruction must be a jump) 10 | */ 11 | public enum OpArgMask { 12 | OpArgN, /* argument is not used */ 13 | OpArgU, /* argument is used */ 14 | OpArgR, /* argument is a register or a jump offset */ 15 | OpArgK, /* argument is a constant or register/constant */ 16 | } 17 | -------------------------------------------------------------------------------- /classpy-binarychunk/src/main/java/com/github/zxh/classpy/lua53/vm/OpMode.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.lua53.vm; 2 | 3 | /* basic instruction format */ 4 | public enum OpMode { 5 | iABC, 6 | iABx, 7 | iAsBx, 8 | iAx, 9 | } 10 | -------------------------------------------------------------------------------- /classpy-binarychunk/src/main/java/com/github/zxh/classpy/lua54/binarychunk/BinaryChunkFile.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.lua54.binarychunk; 2 | 3 | import com.github.zxh.classpy.lua54.binarychunk.datatype.LuByte; 4 | import com.github.zxh.classpy.lua54.binarychunk.part.Function; 5 | import com.github.zxh.classpy.lua54.binarychunk.part.Header; 6 | 7 | /** 8 | * Lua 5.4 binary chunk file. 9 | */ 10 | public class BinaryChunkFile extends BinaryChunkPart { 11 | 12 | // lua5.4.1/lundump.c#luaU_undump() 13 | { 14 | add("header", new Header()); 15 | add("nupvals", new LuByte()); 16 | add("main", new Function()); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /classpy-binarychunk/src/main/java/com/github/zxh/classpy/lua54/binarychunk/BinaryChunkParser.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.lua54.binarychunk; 2 | 3 | import com.github.zxh.classpy.common.FileParser; 4 | import com.github.zxh.classpy.common.FilePart; 5 | 6 | public class BinaryChunkParser implements FileParser { 7 | 8 | @Override 9 | public FilePart parse(byte[] data) { 10 | BinaryChunkReader reader = new BinaryChunkReader(data); 11 | BinaryChunkFile bc = new BinaryChunkFile(); 12 | try { 13 | bc.read(reader); 14 | postRead(bc); 15 | } catch (Exception e) { 16 | e.printStackTrace(); 17 | } 18 | return bc; 19 | } 20 | 21 | private static void postRead(BinaryChunkPart bc) { 22 | for (FilePart c : bc.getParts()) { 23 | postRead((BinaryChunkPart) c); 24 | } 25 | bc.postRead(); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /classpy-binarychunk/src/main/java/com/github/zxh/classpy/lua54/binarychunk/BinaryChunkPart.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.lua54.binarychunk; 2 | 3 | import com.github.zxh.classpy.common.ReadableFilePart; 4 | import com.github.zxh.classpy.lua54.binarychunk.datatype.*; 5 | 6 | import java.util.function.Supplier; 7 | 8 | /** 9 | * Component of lua binary chunk file. 10 | */ 11 | public class BinaryChunkPart extends ReadableFilePart { 12 | 13 | protected void postRead() { 14 | 15 | } 16 | 17 | public void lu_byte(String name) { 18 | super.add(name, new LuByte()); 19 | } 20 | 21 | public void lua_int(String name) { 22 | super.add(name, new LuaInt()); 23 | } 24 | 25 | public void varInt(String name) { 26 | super.add(name, new VarInt()); 27 | } 28 | 29 | public void lua_num(String name) { 30 | super.add(name, new LuaNum()); 31 | } 32 | 33 | public void str(String name) { 34 | super.add(name, new LuaStr()); 35 | } 36 | 37 | public void bytes(String name, int n) { 38 | super.add(name, new Bytes(n)); 39 | } 40 | 41 | public void vector(String name, Supplier partSupplier) { 42 | super.add(name, new Vec(partSupplier)); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /classpy-binarychunk/src/main/java/com/github/zxh/classpy/lua54/binarychunk/BinaryChunkReader.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.lua54.binarychunk; 2 | 3 | import com.github.zxh.classpy.common.BytesReader; 4 | import com.github.zxh.classpy.common.ParseException; 5 | 6 | import java.nio.ByteOrder; 7 | 8 | public class BinaryChunkReader extends BytesReader { 9 | 10 | private int instrSize = 4; 11 | private int luaIntSize = 8; 12 | private int luaNumSize = 8; 13 | 14 | public BinaryChunkReader(byte[] data) { 15 | super(data, ByteOrder.LITTLE_ENDIAN); 16 | } 17 | 18 | // setters 19 | public void setInstrSize(int sizetSize) {this.instrSize = sizetSize;} 20 | public void setLuaIntSize(int luaIntSize) {this.luaIntSize = luaIntSize;} 21 | public void setLuaNumSize(int luaNumSize) {this.luaNumSize = luaNumSize;} 22 | 23 | public long readLuaInt() { 24 | return luaIntSize == 8 ? super.readFixedI64() : super.readFixedI32(); 25 | } 26 | 27 | public double readLuaNum() { 28 | return luaNumSize == 8 ? super.readF64(): super.readF32(); 29 | } 30 | 31 | // lua5.4.1/lundump.c#loadInt() 32 | public int readVarInt() { 33 | return (int) readUnsigned(Integer.MAX_VALUE); 34 | } 35 | 36 | // lua5.4.1/lundump.c#loadUnsigned() 37 | private long readUnsigned (long limit) { 38 | long x = 0; 39 | byte b; 40 | limit >>= 7; 41 | do { 42 | b = readByte(); 43 | if (x >= limit) 44 | throw new ParseException("integer overflow"); 45 | x = (x << 7) | (b & 0x7f); 46 | } while ((b & 0x80) == 0); 47 | return x; 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /classpy-binarychunk/src/main/java/com/github/zxh/classpy/lua54/binarychunk/datatype/Bytes.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.lua54.binarychunk.datatype; 2 | 3 | import com.github.zxh.classpy.lua54.binarychunk.BinaryChunkPart; 4 | import com.github.zxh.classpy.lua54.binarychunk.BinaryChunkReader; 5 | 6 | /** 7 | * byte array. 8 | */ 9 | public class Bytes extends BinaryChunkPart { 10 | 11 | private final int n; 12 | private byte[] bytes; 13 | 14 | public Bytes(int n) { 15 | this.n = n; 16 | } 17 | 18 | public byte[] getBytes() { 19 | return bytes; 20 | } 21 | 22 | @Override 23 | protected void readContent(BinaryChunkReader reader) { 24 | bytes = reader.readBytes(n); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /classpy-binarychunk/src/main/java/com/github/zxh/classpy/lua54/binarychunk/datatype/LuByte.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.lua54.binarychunk.datatype; 2 | 3 | import com.github.zxh.classpy.lua54.binarychunk.BinaryChunkPart; 4 | import com.github.zxh.classpy.lua54.binarychunk.BinaryChunkReader; 5 | 6 | /** 7 | * lu_byte 8 | */ 9 | public class LuByte extends BinaryChunkPart { 10 | 11 | private int value; 12 | 13 | public int getValue() { 14 | return value; 15 | } 16 | 17 | @Override 18 | protected void readContent(BinaryChunkReader reader) { 19 | value = reader.readFixedU8(); 20 | super.setDesc(Integer.toString(value)); 21 | 22 | // TODO 23 | if (super.getName() != null) { 24 | switch (super.getName()) { 25 | case "sizeof(Instruction)" -> reader.setInstrSize(value); 26 | case "sizeof(lua_Integer)" -> reader.setLuaIntSize(value); 27 | case "sizeof(lua_Number)" -> reader.setLuaNumSize(value); 28 | } 29 | } 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /classpy-binarychunk/src/main/java/com/github/zxh/classpy/lua54/binarychunk/datatype/LuaInt.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.lua54.binarychunk.datatype; 2 | 3 | import com.github.zxh.classpy.lua54.binarychunk.BinaryChunkPart; 4 | import com.github.zxh.classpy.lua54.binarychunk.BinaryChunkReader; 5 | 6 | /** 7 | * lua_Integer 8 | */ 9 | public class LuaInt extends BinaryChunkPart { 10 | 11 | private long value; 12 | 13 | public long getValue() { 14 | return value; 15 | } 16 | 17 | @Override 18 | protected void readContent(BinaryChunkReader reader) { 19 | value = reader.readLuaInt(); 20 | setDesc(Long.toString(value)); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /classpy-binarychunk/src/main/java/com/github/zxh/classpy/lua54/binarychunk/datatype/LuaNum.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.lua54.binarychunk.datatype; 2 | 3 | import com.github.zxh.classpy.lua54.binarychunk.BinaryChunkPart; 4 | import com.github.zxh.classpy.lua54.binarychunk.BinaryChunkReader; 5 | 6 | /** 7 | * lua_Number 8 | */ 9 | public class LuaNum extends BinaryChunkPart { 10 | 11 | private double value; 12 | 13 | @Override 14 | protected void readContent(BinaryChunkReader reader) { 15 | value = reader.readLuaNum(); 16 | setDesc(Double.toString(value)); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /classpy-binarychunk/src/main/java/com/github/zxh/classpy/lua54/binarychunk/datatype/LuaStr.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.lua54.binarychunk.datatype; 2 | 3 | import com.github.zxh.classpy.lua54.binarychunk.BinaryChunkPart; 4 | import com.github.zxh.classpy.lua54.binarychunk.BinaryChunkReader; 5 | 6 | /** 7 | * string in binary chunk. 8 | */ 9 | public class LuaStr extends BinaryChunkPart { 10 | 11 | @Override 12 | protected void readContent(BinaryChunkReader reader) { 13 | VarInt size = new VarInt(); 14 | size.read(reader); 15 | 16 | if (size.getValue() == 0) { 17 | super.setDesc("NULL"); 18 | } else if (size.getValue() < 0xFF) { 19 | super.add("size", size); 20 | readStr(reader, size.getValue() - 1); 21 | } 22 | } 23 | 24 | private void readStr(BinaryChunkReader reader, int bytesCount) { 25 | Bytes bytes = new Bytes(bytesCount); 26 | bytes.read(reader); 27 | super.add("bytes", bytes); 28 | 29 | String str = new String(bytes.getBytes()); // todo 30 | super.setDesc(str); 31 | bytes.setDesc(str); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /classpy-binarychunk/src/main/java/com/github/zxh/classpy/lua54/binarychunk/datatype/VarInt.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.lua54.binarychunk.datatype; 2 | 3 | import com.github.zxh.classpy.lua54.binarychunk.BinaryChunkPart; 4 | import com.github.zxh.classpy.lua54.binarychunk.BinaryChunkReader; 5 | 6 | public class VarInt extends BinaryChunkPart { 7 | 8 | private int value; 9 | 10 | public int getValue() { 11 | return value; 12 | } 13 | 14 | @Override 15 | protected void readContent(BinaryChunkReader reader) { 16 | value = reader.readVarInt(); 17 | setDesc(Long.toString(value)); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /classpy-binarychunk/src/main/java/com/github/zxh/classpy/lua54/binarychunk/datatype/Vec.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.lua54.binarychunk.datatype; 2 | 3 | import com.github.zxh.classpy.common.FilePart; 4 | import com.github.zxh.classpy.helper.StringHelper; 5 | import com.github.zxh.classpy.lua54.binarychunk.BinaryChunkPart; 6 | import com.github.zxh.classpy.lua54.binarychunk.BinaryChunkReader; 7 | 8 | import java.util.List; 9 | import java.util.function.Supplier; 10 | 11 | /** 12 | * Table in binary chunk. 13 | */ 14 | public class Vec extends BinaryChunkPart { 15 | 16 | private final Supplier partSupplier; 17 | 18 | public Vec(Supplier partSupplier) { 19 | this.partSupplier = partSupplier; 20 | } 21 | 22 | @Override 23 | protected void readContent(BinaryChunkReader reader) { 24 | VarInt size = new VarInt(); 25 | size.read(reader); 26 | super.add("size", size); 27 | 28 | for (int i = 0; i < size.getValue(); i++) { 29 | BinaryChunkPart c = partSupplier.get(); 30 | super.add(null, c); 31 | c.read(reader); 32 | } 33 | } 34 | 35 | @Override 36 | protected void postRead() { 37 | List kids = super.getParts(); 38 | int maxIdx = kids.size() - 1; 39 | for (int i = 1; i < kids.size(); i++) { 40 | FilePart kid = kids.get(i); 41 | if (kid.getName() == null) { 42 | kid.setName(StringHelper.formatIndex(maxIdx, i - 1)); 43 | } else { 44 | kid.setName(StringHelper.formatIndex(maxIdx, i - 1) 45 | + " (" + kid.getName() + ")"); 46 | } 47 | } 48 | super.setDesc("(" + maxIdx + ")"); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /classpy-binarychunk/src/main/java/com/github/zxh/classpy/lua54/binarychunk/part/Debug.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.lua54.binarychunk.part; 2 | 3 | import com.github.zxh.classpy.common.FilePart; 4 | import com.github.zxh.classpy.lua54.binarychunk.BinaryChunkPart; 5 | import com.github.zxh.classpy.lua54.binarychunk.datatype.LuByte; 6 | import com.github.zxh.classpy.lua54.binarychunk.datatype.LuaStr; 7 | 8 | import java.util.List; 9 | 10 | // lua5.4.1/lundump.c#loadDebug() 11 | public class Debug extends BinaryChunkPart { 12 | 13 | { 14 | vector("line_info", LuByte::new); 15 | vector("abs_line_info", AbsLineInfo::new); 16 | vector("loc_vars", LocVar::new); 17 | vector("upvalues", LuaStr::new); 18 | } 19 | 20 | // TODO 21 | public long getLine(int pc) { 22 | return -1; 23 | } 24 | 25 | public String getLocVarName(int idx) { 26 | List locVars = super.get("loc_vars").getParts(); 27 | if (idx + 1 >= locVars.size()) { 28 | return ""; 29 | } else { 30 | return ((LocVar) locVars.get(idx + 1)).getVarName(); 31 | } 32 | } 33 | 34 | public String getUpvalName(int idx) { 35 | List upvals = super.get("upvalues").getParts(); 36 | if (idx + 1 >= upvals.size()) { 37 | return ""; 38 | } else { 39 | return upvals.get(idx + 1).getDesc(); 40 | } 41 | } 42 | 43 | public static class AbsLineInfo extends BinaryChunkPart { 44 | 45 | { 46 | varInt("pc"); 47 | varInt("line"); 48 | } 49 | 50 | } 51 | 52 | public static class LocVar extends BinaryChunkPart { 53 | 54 | { 55 | str ("var_name"); 56 | varInt("start_pc"); 57 | varInt("end_pc" ); 58 | } 59 | 60 | @Override 61 | protected void postRead() { 62 | setName(get("var_name").getDesc()); 63 | setDesc(get("start_pc").getDesc() 64 | + " ~ " + get("end_pc").getDesc()); 65 | } 66 | 67 | private String getVarName() { 68 | return super.get("var_name").getDesc(); 69 | } 70 | 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /classpy-binarychunk/src/main/java/com/github/zxh/classpy/lua54/binarychunk/part/Header.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.lua54.binarychunk.part; 2 | 3 | import com.github.zxh.classpy.common.ParseException; 4 | import com.github.zxh.classpy.lua54.binarychunk.BinaryChunkPart; 5 | import com.github.zxh.classpy.lua54.binarychunk.datatype.Bytes; 6 | import com.github.zxh.classpy.lua54.binarychunk.datatype.LuByte; 7 | import com.github.zxh.classpy.lua54.binarychunk.datatype.LuaInt; 8 | 9 | import java.util.Arrays; 10 | 11 | // lua5.4.1/lundump.c#checkHeader() 12 | public class Header extends BinaryChunkPart { 13 | 14 | private final byte[] _luaSig = {0x1b, 'L', 'u', 'a'}; 15 | private final byte[] _luacData = {0x19, (byte) 0x93, '\r', '\n', 0x1a, '\n'}; 16 | 17 | { 18 | bytes ("LUA_SIGNATURE", 4); 19 | lu_byte("LUAC_VERSION" ); 20 | lu_byte("LUAC_FORMAT" ); 21 | bytes ("LUAC_DATA", 6); 22 | lu_byte("sizeof(Instruction)"); 23 | lu_byte("sizeof(lua_Integer)"); 24 | lu_byte("sizeof(lua_Number)" ); 25 | lua_int("LUAC_INT" ); 26 | lua_num("LUAC_NUM" ); 27 | } 28 | 29 | @Override 30 | protected void postRead() { 31 | checkSignature(); 32 | checkLuacData(); 33 | LuByte luaVersion = (LuByte) super.get("LUAC_VERSION"); 34 | luaVersion.setDesc("0x" + Integer.toHexString(luaVersion.getValue())); 35 | LuaInt luacInt = (LuaInt) super.get("LUAC_INT"); 36 | luacInt.setDesc("0x" + Long.toHexString(luacInt.getValue())); 37 | } 38 | 39 | private void checkSignature() { 40 | Bytes sig = (Bytes) super.get("LUA_SIGNATURE"); 41 | if (!Arrays.equals(sig.getBytes(), _luaSig)) { 42 | throw new ParseException("not a precompiled chunk!"); 43 | } else { 44 | sig.setDesc("\"\\x1bLua\""); 45 | } 46 | } 47 | 48 | private void checkLuacData() { 49 | Bytes luacData = (Bytes) super.get("LUAC_DATA"); 50 | if (!Arrays.equals(luacData.getBytes(), _luacData)) { 51 | throw new ParseException("corrupted!"); 52 | } else { 53 | luacData.setDesc("\"\\x19\\x93\\r\\n\\x1a\\n\""); 54 | } 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /classpy-binarychunk/src/main/java/com/github/zxh/classpy/lua54/vm/LuaType.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.lua54.vm; 2 | 3 | import com.github.zxh.classpy.common.ParseException; 4 | 5 | /** 6 | * Lua types. 7 | * - /lua/src/lua.h 8 | * - /lua/src/lobject.h 9 | */ 10 | public enum LuaType { 11 | 12 | /* basic types */ 13 | LUA_TNONE (-1), 14 | LUA_TNIL (0), 15 | LUA_TBOOLEAN (1), 16 | LUA_TLIGHTUSERDATA(2), 17 | LUA_TNUMBER (3), 18 | LUA_TSTRING (4), 19 | LUA_TTABLE (5), 20 | LUA_TFUNCTION (6), 21 | LUA_TUSERDATA (7), 22 | LUA_TTHREAD (8), 23 | 24 | // Variant tags 25 | LUA_VNIL(0), 26 | LUA_VFALSE (LUA_TBOOLEAN.type | (0 << 4)), // false 27 | LUA_VTRUE (LUA_TBOOLEAN.type | (1 << 4)), // true 28 | LUA_VNUMINT(LUA_TNUMBER.type | (0 << 4)), // integer numbers 29 | LUA_VNUMFLT(LUA_TNUMBER.type | (1 << 4)), // float numbers 30 | LUA_VSHRSTR(LUA_TSTRING.type | (0 << 4)), // short strings 31 | LUA_VLNGSTR(LUA_TSTRING.type | (1 << 4)), // long strings 32 | 33 | ; 34 | 35 | public final int type; 36 | 37 | LuaType(int type) { 38 | this.type = type; 39 | } 40 | 41 | public static LuaType valueOf(int type) { 42 | for (LuaType lt : values()) { 43 | if (lt.type == type) { 44 | return lt; 45 | } 46 | } 47 | throw new ParseException("Unknown lua type: " + type); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /classpy-binarychunk/src/main/java/com/github/zxh/classpy/lua54/vm/OpMode.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.lua54.vm; 2 | 3 | /* basic instruction format */ 4 | public enum OpMode { 5 | iABC, 6 | iABx, 7 | iAsBx, 8 | iAx, 9 | isJ, 10 | } 11 | -------------------------------------------------------------------------------- /classpy-binarychunk/src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | module classpy.binarychunk { 2 | exports com.github.zxh.classpy.lua; 3 | requires classpy.common; 4 | } 5 | -------------------------------------------------------------------------------- /classpy-binarychunk/src/test/lua/basic_types.lua: -------------------------------------------------------------------------------- 1 | x = nil -- nil 2 | b1 = true -- boolean 3 | b2 = false -- boolean 4 | pi = 3.14 -- number/integer 5 | one = 1 -- number/floating-point 6 | s = "hello" -- string 7 | -- function 8 | -- userdata 9 | -- thread 10 | -- table -------------------------------------------------------------------------------- /classpy-binarychunk/src/test/lua/blank.lua: -------------------------------------------------------------------------------- 1 | -- really blank -------------------------------------------------------------------------------- /classpy-binarychunk/src/test/lua/constants.lua: -------------------------------------------------------------------------------- 1 | print(nil) 2 | print(true) 3 | print(false) 4 | print(0xABCD) 5 | print(456.78) 6 | print("foo") 7 | -------------------------------------------------------------------------------- /classpy-binarychunk/src/test/lua/functions.lua: -------------------------------------------------------------------------------- 1 | function f1() 2 | function f2() 3 | function f3() 4 | -- body 5 | end 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /classpy-binarychunk/src/test/lua/hello_world.lua: -------------------------------------------------------------------------------- 1 | print("Hello, World!") -------------------------------------------------------------------------------- /classpy-binarychunk/src/test/lua/operators.lua: -------------------------------------------------------------------------------- 1 | function arithmetic(a, b) 2 | print(a + b) 3 | print(a - b) 4 | print(a * b) 5 | print(a / b) 6 | print(a // b) 7 | print(a % b) 8 | print(a ^ b) 9 | print(-b) 10 | end 11 | 12 | function bitwise(a, b) 13 | print(a & b) 14 | print(a | b) 15 | print(a ~ b) 16 | print(a >> 4) 17 | print(a << 4) 18 | print(~a) 19 | end 20 | 21 | function relational(a, b) 22 | print(a == b) 23 | print(a ~= b) 24 | print(a < b) 25 | print(a > b) 26 | print(a <= b) 27 | print(a >= b) 28 | end 29 | 30 | function logical(a, b) 31 | print(a or b) 32 | print(a and b) 33 | print(not a) 34 | end 35 | 36 | function concatenation(a, b) 37 | print(a .. b) 38 | end 39 | 40 | function length(a) 41 | print(#a) 42 | end 43 | 44 | arithmetic(11, 2) 45 | bitwise(0x0F, 0xF0) 46 | relational(1, 2) 47 | logical(true, false) 48 | concatenation('foo', 'bar') 49 | length('foo') 50 | -------------------------------------------------------------------------------- /classpy-binarychunk/src/test/resources/luac53out/basic_types.luac: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zxh0/classpy/c907b08082ac9b6b1e8eaa2fbf9f35320a7d241a/classpy-binarychunk/src/test/resources/luac53out/basic_types.luac -------------------------------------------------------------------------------- /classpy-binarychunk/src/test/resources/luac53out/blank.luac: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zxh0/classpy/c907b08082ac9b6b1e8eaa2fbf9f35320a7d241a/classpy-binarychunk/src/test/resources/luac53out/blank.luac -------------------------------------------------------------------------------- /classpy-binarychunk/src/test/resources/luac53out/functions.luac: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zxh0/classpy/c907b08082ac9b6b1e8eaa2fbf9f35320a7d241a/classpy-binarychunk/src/test/resources/luac53out/functions.luac -------------------------------------------------------------------------------- /classpy-binarychunk/src/test/resources/luac53out/hello_world.luac: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zxh0/classpy/c907b08082ac9b6b1e8eaa2fbf9f35320a7d241a/classpy-binarychunk/src/test/resources/luac53out/hello_world.luac -------------------------------------------------------------------------------- /classpy-binarychunk/src/test/resources/luac53out/hello_world_no_debug.luac: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zxh0/classpy/c907b08082ac9b6b1e8eaa2fbf9f35320a7d241a/classpy-binarychunk/src/test/resources/luac53out/hello_world_no_debug.luac -------------------------------------------------------------------------------- /classpy-binarychunk/src/test/resources/luac53out/operators.luac: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zxh0/classpy/c907b08082ac9b6b1e8eaa2fbf9f35320a7d241a/classpy-binarychunk/src/test/resources/luac53out/operators.luac -------------------------------------------------------------------------------- /classpy-binarychunk/src/test/resources/luac54out/constants.luac: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zxh0/classpy/c907b08082ac9b6b1e8eaa2fbf9f35320a7d241a/classpy-binarychunk/src/test/resources/luac54out/constants.luac -------------------------------------------------------------------------------- /classpy-bitcoin/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java-library' 3 | } 4 | 5 | dependencies { 6 | implementation project(':classpy-common') 7 | } 8 | -------------------------------------------------------------------------------- /classpy-bitcoin/src/main/java/com/github/zxh/classpy/bitcoin/Block.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.bitcoin; 2 | 3 | public class Block extends BlockPart { 4 | 5 | { 6 | uint32("Version"); 7 | hash ("HashPrevBlock"); 8 | hash ("HashMerkleRoot"); 9 | uint32("Time"); 10 | uint32("Bits"); 11 | uint32("Nonce"); 12 | table ("Transactions", Transaction::new); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /classpy-bitcoin/src/main/java/com/github/zxh/classpy/bitcoin/BlockParser.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.bitcoin; 2 | 3 | import com.github.zxh.classpy.common.FileParser; 4 | 5 | public class BlockParser implements FileParser { 6 | 7 | @Override 8 | public Block parse(byte[] data) { 9 | Block block = new Block(); 10 | try { 11 | block.read(new BlockReader(data)); 12 | } catch (Exception e) { 13 | e.printStackTrace(System.err); 14 | } 15 | return block; 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /classpy-bitcoin/src/main/java/com/github/zxh/classpy/bitcoin/BlockPart.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.bitcoin; 2 | 3 | import com.github.zxh.classpy.bitcoin.types.*; 4 | import com.github.zxh.classpy.common.ReadableFilePart; 5 | 6 | import java.util.function.Supplier; 7 | 8 | public class BlockPart extends ReadableFilePart { 9 | 10 | protected T read(BlockReader reader, 11 | String name, T c) { 12 | add(name, c); 13 | c.read(reader); 14 | return c; 15 | } 16 | 17 | protected long readVarInt(BlockReader reader, String name) { 18 | VarInt varInt = new VarInt(); 19 | add(name, varInt); 20 | varInt.read(reader); 21 | return varInt.getValue(); 22 | } 23 | 24 | protected void uint32(String name) { 25 | add(name, new UInt32()); 26 | } 27 | 28 | protected void uint64(String name) { 29 | add(name, new UInt64()); 30 | } 31 | 32 | protected void hash(String name) { 33 | add(name, new Hash()); 34 | } 35 | 36 | protected void bytes(String name, int n) { 37 | add(name, new Bytes(n)); 38 | } 39 | 40 | protected void script(String name) { 41 | add(name, new Script()); 42 | } 43 | 44 | protected void table(String name, 45 | Supplier supplier) { 46 | add(name, new Table(supplier)); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /classpy-bitcoin/src/main/java/com/github/zxh/classpy/bitcoin/BlockReader.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.bitcoin; 2 | 3 | import com.github.zxh.classpy.common.BytesReader; 4 | 5 | import java.nio.ByteOrder; 6 | 7 | // https://en.wikipedia.org/wiki/LEB128 8 | public class BlockReader extends BytesReader { 9 | 10 | public BlockReader(byte[] data) { 11 | super(data, ByteOrder.LITTLE_ENDIAN); // ? 12 | } 13 | 14 | public long readVarInt() { 15 | int b = readByte() & 0xFF; 16 | if (b < 0xFD) { 17 | return b; 18 | } if (b == 0xFD) { 19 | return readFixedU16(); 20 | } if (b == 0xFE) { 21 | return readFixedU32(); 22 | } else { 23 | return readFixedI64(); 24 | } 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /classpy-bitcoin/src/main/java/com/github/zxh/classpy/bitcoin/Transaction.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.bitcoin; 2 | 3 | import com.github.zxh.classpy.bitcoin.types.Table; 4 | import com.github.zxh.classpy.bitcoin.types.UInt32; 5 | 6 | // https://en.bitcoin.it/wiki/Transaction 7 | public class Transaction extends BlockPart { 8 | 9 | @Override 10 | protected void readContent(BlockReader reader) { 11 | read(reader, "Version", new UInt32()); 12 | Table txins = new Table(TxIn::new); 13 | txins.read(reader); 14 | if (txins.getParts().size() == 1 && reader.readByte() != 0) { 15 | read(reader, "Txins", new Table(TxIn::new)); 16 | read(reader, "Txouts", new Table(TxOut::new)); 17 | int nTxins = get("Txins").getParts().size() - 1; 18 | for (int i = 0; i < nTxins; i++) { 19 | read(reader, "Witnesses", new WitnessData()); 20 | } 21 | } else { 22 | add("Txins", txins); 23 | read(reader, "Txouts", new Table(TxOut::new)); 24 | } 25 | read(reader, "LockTime", new UInt32()); 26 | } 27 | 28 | 29 | private static class WitnessData extends BlockPart { 30 | 31 | @Override 32 | protected void readContent(BlockReader reader) { 33 | long n = reader.readVarInt(); 34 | for (int i = 0; i < n; i++) { 35 | long m = reader.readVarInt(); 36 | reader.readBytes((int) m); 37 | } 38 | } 39 | 40 | } 41 | 42 | private static class TxIn extends BlockPart { 43 | 44 | { 45 | hash ("PreviousTransactionHash"); 46 | uint32("PreviousTxoutIndex"); 47 | script("TxinScript"); 48 | bytes ("SequenceNo", 4); 49 | } 50 | 51 | } 52 | 53 | private static class TxOut extends BlockPart { 54 | 55 | { 56 | uint64("value"); 57 | script("TxoutScript"); 58 | } 59 | 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /classpy-bitcoin/src/main/java/com/github/zxh/classpy/bitcoin/TxParser.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.bitcoin; 2 | 3 | import com.github.zxh.classpy.common.FileParser; 4 | 5 | public class TxParser implements FileParser { 6 | 7 | @Override 8 | public Transaction parse(byte[] data) { 9 | Transaction tx = new Transaction(); 10 | try { 11 | tx.read(new BlockReader(data)); 12 | } catch (Exception e) { 13 | e.printStackTrace(System.err); 14 | } 15 | return tx; 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /classpy-bitcoin/src/main/java/com/github/zxh/classpy/bitcoin/types/Bytes.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.bitcoin.types; 2 | 3 | import com.github.zxh.classpy.bitcoin.BlockPart; 4 | import com.github.zxh.classpy.bitcoin.BlockReader; 5 | 6 | public class Bytes extends BlockPart { 7 | 8 | private final int n; 9 | private byte[] bytes; 10 | 11 | public Bytes(int n) { 12 | this.n = n; 13 | } 14 | 15 | public byte[] getBytes() { 16 | return bytes; 17 | } 18 | 19 | @Override 20 | protected void readContent(BlockReader reader) { 21 | bytes = reader.readBytes(n); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /classpy-bitcoin/src/main/java/com/github/zxh/classpy/bitcoin/types/Hash.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.bitcoin.types; 2 | 3 | import com.github.zxh.classpy.bitcoin.BlockPart; 4 | import com.github.zxh.classpy.bitcoin.BlockReader; 5 | 6 | public class Hash extends BlockPart { 7 | 8 | @Override 9 | protected void readContent(BlockReader reader) { 10 | byte[] bytes = reader.readBytes(32); 11 | 12 | StringBuilder sb = new StringBuilder(); 13 | for (int i = 31; i >= 0; i--) { 14 | sb.append(Integer.toHexString(bytes[i] & 0xFF)); 15 | } 16 | setDesc(sb.toString()); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /classpy-bitcoin/src/main/java/com/github/zxh/classpy/bitcoin/types/Table.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.bitcoin.types; 2 | 3 | import com.github.zxh.classpy.bitcoin.BlockPart; 4 | import com.github.zxh.classpy.bitcoin.BlockReader; 5 | 6 | import java.util.function.Supplier; 7 | 8 | public class Table extends BlockPart { 9 | 10 | private final Supplier supplier; 11 | private final String partName; 12 | 13 | public Table(Supplier supplier) { 14 | this.supplier = supplier; 15 | partName = supplier.get().getClass().getSimpleName(); 16 | } 17 | 18 | @Override 19 | protected void readContent(BlockReader reader) { 20 | long count = readVarInt(reader, "Count"); 21 | for (long i = 0; i < count; i++) { 22 | BlockPart element = supplier.get(); 23 | add(partName + "#" + i, element); 24 | element.read(reader); 25 | } 26 | setDesc(Long.toString(count)); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /classpy-bitcoin/src/main/java/com/github/zxh/classpy/bitcoin/types/UInt32.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.bitcoin.types; 2 | 3 | import com.github.zxh.classpy.bitcoin.BlockPart; 4 | import com.github.zxh.classpy.bitcoin.BlockReader; 5 | 6 | public class UInt32 extends BlockPart { 7 | 8 | @Override 9 | protected void readContent(BlockReader reader) { 10 | long value = reader.readFixedU32(); 11 | setDesc(Long.toString(value)); 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /classpy-bitcoin/src/main/java/com/github/zxh/classpy/bitcoin/types/UInt64.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.bitcoin.types; 2 | 3 | import com.github.zxh.classpy.bitcoin.BlockPart; 4 | import com.github.zxh.classpy.bitcoin.BlockReader; 5 | 6 | public class UInt64 extends BlockPart { 7 | 8 | @Override 9 | protected void readContent(BlockReader reader) { 10 | long value = reader.readFixedI64(); 11 | setDesc(Long.toString(value)); 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /classpy-bitcoin/src/main/java/com/github/zxh/classpy/bitcoin/types/VarInt.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.bitcoin.types; 2 | 3 | import com.github.zxh.classpy.bitcoin.BlockPart; 4 | import com.github.zxh.classpy.bitcoin.BlockReader; 5 | 6 | // https://en.bitcoin.it/wiki/Protocol_documentation#Variable_length_integer 7 | public class VarInt extends BlockPart { 8 | 9 | private long value; 10 | 11 | public long getValue() { 12 | return value; 13 | } 14 | 15 | @Override 16 | protected void readContent(BlockReader reader) { 17 | value = reader.readVarInt(); 18 | setDesc(Long.toString(value)); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /classpy-bitcoin/src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | module classpy.bitcoin { 2 | exports com.github.zxh.classpy.bitcoin; 3 | requires classpy.common; 4 | } 5 | -------------------------------------------------------------------------------- /classpy-classfile/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java-library' 3 | } 4 | 5 | dependencies { 6 | implementation project(':classpy-common') 7 | 8 | testImplementation 'org.junit.jupiter:junit-jupiter-api:5.6.0' 9 | testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine' 10 | testCompileOnly 'junit:junit:4.13' 11 | testRuntimeOnly 'org.junit.vintage:junit-vintage-engine' 12 | } 13 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/ClassFile.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile; 2 | 3 | import com.github.zxh.classpy.classfile.attribute.AttributeInfo; 4 | import com.github.zxh.classpy.classfile.constant.ConstantPool; 5 | import com.github.zxh.classpy.classfile.datatype.U2; 6 | import com.github.zxh.classpy.classfile.datatype.U2CpIndex; 7 | import com.github.zxh.classpy.classfile.jvm.AccessFlagType; 8 | 9 | /* 10 | ClassFile { 11 | u4 magic; 12 | u2 minor_version; 13 | u2 major_version; 14 | u2 constant_pool_count; 15 | cp_info constant_pool[constant_pool_count-1]; 16 | u2 access_flags; 17 | u2 this_class; 18 | u2 super_class; 19 | u2 interfaces_count; 20 | u2 interfaces[interfaces_count]; 21 | u2 fields_count; 22 | field_info fields[fields_count]; 23 | u2 methods_count; 24 | method_info methods[methods_count]; 25 | u2 attributes_count; 26 | attribute_info attributes[attributes_count]; 27 | } 28 | */ 29 | public class ClassFile extends ClassFilePart { 30 | 31 | { 32 | U2 cpCount = new U2(); 33 | 34 | u4hex("magic"); 35 | u2 ("minor_version"); 36 | u2 ("major_version"); 37 | add ("constant_pool_count", cpCount); 38 | add ("constant_pool", new ConstantPool(cpCount)); 39 | u2af ("access_flags", AccessFlagType.AF_CLASS); 40 | u2cp ("this_class"); 41 | u2cp ("super_class"); 42 | u2 ("interfaces_count"); 43 | table("interfaces", U2CpIndex.class); 44 | u2 ("fields_count"); 45 | table("fields", FieldInfo.class); 46 | u2 ("methods_count"); 47 | table("methods", MethodInfo.class); 48 | u2 ("attributes_count"); 49 | table("attributes", AttributeInfo.class); 50 | } 51 | 52 | ConstantPool getConstantPool() { 53 | return (ConstantPool) super.get("constant_pool"); 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/ClassFileParser.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile; 2 | 3 | import com.github.zxh.classpy.classfile.constant.ConstantPool; 4 | import com.github.zxh.classpy.common.FilePart; 5 | import com.github.zxh.classpy.common.FileParser; 6 | 7 | public class ClassFileParser implements FileParser { 8 | 9 | public ClassFile parse(byte[] data) { 10 | ClassFile cf = new ClassFile(); 11 | cf.read(new ClassFileReader(data)); 12 | postRead(cf, cf.getConstantPool()); 13 | return cf; 14 | } 15 | 16 | private static void postRead(ClassFilePart fc, ConstantPool cp) { 17 | for (FilePart c : fc.getParts()) { 18 | postRead((ClassFilePart) c, cp); 19 | } 20 | fc.postRead(cp); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/ClassFilePart.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile; 2 | 3 | import com.github.zxh.classpy.classfile.constant.ConstantPool; 4 | import com.github.zxh.classpy.classfile.datatype.*; 5 | import com.github.zxh.classpy.common.ReadableFilePart; 6 | 7 | /** 8 | * Base class for all class file parts. 9 | */ 10 | public abstract class ClassFilePart extends ReadableFilePart { 11 | 12 | protected void postRead(ConstantPool cp) { 13 | 14 | } 15 | 16 | protected int getUInt(String name) { 17 | return ((UInt) get(name)).getValue(); 18 | } 19 | 20 | protected final void u1(String name) { 21 | this.add(name, new U1()); 22 | } 23 | 24 | protected final void u1cp(String name) { 25 | this.add(name, new U1CpIndex()); 26 | } 27 | 28 | protected final void u2(String name) { 29 | this.add(name, new U2()); 30 | } 31 | 32 | protected final void u2cp(String name) { 33 | this.add(name, new U2CpIndex()); 34 | } 35 | 36 | protected final void u2af(String name, int afType) { 37 | this.add(name, new U2AccessFlags(afType)); 38 | } 39 | 40 | protected final void u4(String name) { 41 | this.add(name, new U4()); 42 | } 43 | 44 | protected final void u4hex(String name) { 45 | this.add(name, new U4Hex()); 46 | } 47 | 48 | protected final void table(String name, 49 | Class entryClass) { 50 | UInt length = (UInt) getParts().get(getParts().size() - 1); 51 | Table table = new Table(length, entryClass); 52 | this.add(name, table); 53 | } 54 | 55 | protected final void bytes(String name) { 56 | UInt count = (UInt) getParts().get(getParts().size() - 1); 57 | Bytes bytes = new Bytes(count); 58 | this.add(name, bytes); 59 | } 60 | 61 | protected final void add(ClassFilePart subPart) { 62 | this.add(null, subPart); 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/ClassFileReader.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile; 2 | 3 | import com.github.zxh.classpy.classfile.constant.ConstantPool; 4 | import com.github.zxh.classpy.common.BytesReader; 5 | 6 | import java.nio.ByteOrder; 7 | 8 | public class ClassFileReader extends BytesReader { 9 | 10 | private ConstantPool constantPool; 11 | 12 | public ClassFileReader(byte[] data) { 13 | super(data, ByteOrder.BIG_ENDIAN); 14 | } 15 | 16 | public ConstantPool getConstantPool() { 17 | return constantPool; 18 | } 19 | 20 | public void setConstantPool(ConstantPool constantPool) { 21 | this.constantPool = constantPool; 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/FieldInfo.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile; 2 | 3 | import com.github.zxh.classpy.classfile.attribute.AttributeInfo; 4 | import com.github.zxh.classpy.classfile.constant.ConstantPool; 5 | import com.github.zxh.classpy.classfile.jvm.AccessFlagType; 6 | 7 | /* 8 | field_info { 9 | u2 access_flags; 10 | u2 name_index; 11 | u2 descriptor_index; 12 | u2 attributes_count; 13 | attribute_info attributes[attributes_count]; 14 | } 15 | */ 16 | public class FieldInfo extends ClassFilePart { 17 | 18 | { 19 | u2af ("access_flags", AccessFlagType.AF_FIELD); 20 | u2cp ("name_index"); 21 | u2cp ("descriptor_index"); 22 | u2 ("attributes_count"); 23 | table("attributes", AttributeInfo.class); 24 | } 25 | 26 | @Override 27 | protected void postRead(ConstantPool cp) { 28 | int nameIndex = super.getUInt("name_index"); 29 | if (nameIndex > 0) { 30 | // TODO: fix loading java.lang.String from rt.jar 31 | setDesc(cp.getUtf8String(nameIndex)); 32 | } 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/MethodInfo.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile; 2 | 3 | import com.github.zxh.classpy.classfile.attribute.AttributeInfo; 4 | import com.github.zxh.classpy.classfile.constant.ConstantPool; 5 | import com.github.zxh.classpy.classfile.jvm.AccessFlagType; 6 | 7 | /* 8 | method_info { 9 | u2 access_flags; 10 | u2 name_index; 11 | u2 descriptor_index; 12 | u2 attributes_count; 13 | attribute_info attributes[attributes_count]; 14 | } 15 | */ 16 | public class MethodInfo extends ClassFilePart { 17 | 18 | { 19 | u2af ("access_flags", AccessFlagType.AF_METHOD); 20 | u2cp ("name_index"); 21 | u2cp ("descriptor_index"); 22 | u2 ("attributes_count"); 23 | table("attributes", AttributeInfo.class); 24 | } 25 | 26 | @Override 27 | protected void postRead(ConstantPool cp) { 28 | int nameIndex = super.getUInt("name_index"); 29 | int descIndex = super.getUInt("descriptor_index"); 30 | if (nameIndex > 0) { 31 | // TODO: fix loading java.lang.String from rt.jar 32 | setDesc(cp.getUtf8String(nameIndex) + cp.getUtf8String(descIndex)); 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/attribute/AnnotationDefaultAttribute.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.attribute; 2 | 3 | import com.github.zxh.classpy.classfile.attribute.RuntimeVisibleAnnotationsAttribute.ElementValue; 4 | 5 | /* 6 | AnnotationDefault_attribute { 7 | u2 attribute_name_index; 8 | u4 attribute_length; 9 | element_value default_value; 10 | } 11 | */ 12 | public class AnnotationDefaultAttribute extends AttributeInfo { 13 | 14 | { 15 | add("default_value", new ElementValue()); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/attribute/AttributeInfo.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.attribute; 2 | 3 | import com.github.zxh.classpy.classfile.ClassFilePart; 4 | 5 | /* 6 | attribute_info { 7 | u2 attribute_name_index; 8 | u4 attribute_length; 9 | u1 info[attribute_length]; 10 | } 11 | */ 12 | public abstract class AttributeInfo extends ClassFilePart { 13 | 14 | { 15 | u2("attribute_name_index"); 16 | u4("attribute_length"); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/attribute/BootstrapMethodsAttribute.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.attribute; 2 | 3 | import com.github.zxh.classpy.classfile.ClassFilePart; 4 | import com.github.zxh.classpy.classfile.datatype.U2CpIndex; 5 | 6 | /* 7 | BootstrapMethods_attribute { 8 | u2 attribute_name_index; 9 | u4 attribute_length; 10 | u2 num_bootstrap_methods; 11 | { u2 bootstrap_method_ref; 12 | u2 num_bootstrap_arguments; 13 | u2 bootstrap_arguments[num_bootstrap_arguments]; 14 | } bootstrap_methods[num_bootstrap_methods]; 15 | } 16 | */ 17 | public class BootstrapMethodsAttribute extends AttributeInfo { 18 | 19 | { 20 | u2 ("num_bootstrap_methods"); 21 | table("bootstrap_methods", BootstrapMethodInfo.class); 22 | } 23 | 24 | 25 | public static class BootstrapMethodInfo extends ClassFilePart { 26 | 27 | { 28 | u2cp ("bootstrap_method_ref"); 29 | u2 ("num_bootstrap_arguments"); 30 | table("bootstrap_arguments", U2CpIndex.class); 31 | } 32 | 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/attribute/ConstantValueAttribute.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.attribute; 2 | 3 | /* 4 | ConstantValue_attribute { 5 | u2 attribute_name_index; 6 | u4 attribute_length; 7 | u2 constantvalue_index; 8 | } 9 | */ 10 | public class ConstantValueAttribute extends AttributeInfo { 11 | 12 | { 13 | u2cp("constant_value_index"); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/attribute/DeprecatedAttribute.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.attribute; 2 | 3 | /* 4 | Deprecated_attribute { 5 | u2 attribute_name_index; 6 | u4 attribute_length; 7 | } 8 | */ 9 | public class DeprecatedAttribute extends AttributeInfo { 10 | 11 | } 12 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/attribute/EnclosingMethodAttribute.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.attribute; 2 | 3 | /* 4 | EnclosingMethod_attribute { 5 | u2 attribute_name_index; 6 | u4 attribute_length; 7 | u2 class_index; 8 | u2 method_index; 9 | } 10 | */ 11 | public class EnclosingMethodAttribute extends AttributeInfo { 12 | 13 | { 14 | u2cp("class_index"); 15 | u2cp("method_index"); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/attribute/ExceptionsAttribute.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.attribute; 2 | 3 | import com.github.zxh.classpy.classfile.datatype.U2CpIndex; 4 | 5 | /* 6 | Exceptions_attribute { 7 | u2 attribute_name_index; 8 | u4 attribute_length; 9 | u2 number_of_exceptions; 10 | u2 exception_index_table[number_of_exceptions]; 11 | } 12 | */ 13 | public class ExceptionsAttribute extends AttributeInfo { 14 | 15 | { 16 | u2 ("number_of_exceptions"); 17 | table("exception_index_table", U2CpIndex.class); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/attribute/InnerClassesAttribute.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.attribute; 2 | 3 | import com.github.zxh.classpy.classfile.ClassFilePart; 4 | import com.github.zxh.classpy.classfile.jvm.AccessFlagType; 5 | 6 | /* 7 | InnerClasses_attribute { 8 | u2 attribute_name_index; 9 | u4 attribute_length; 10 | u2 number_of_classes; 11 | { u2 inner_class_info_index; 12 | u2 outer_class_info_index; 13 | u2 inner_name_index; 14 | u2 inner_class_access_flags; 15 | } classes[number_of_classes]; 16 | } 17 | */ 18 | public class InnerClassesAttribute extends AttributeInfo { 19 | 20 | { 21 | u2 ("number_of_classes"); 22 | table("classes", InnerClassInfo.class); 23 | } 24 | 25 | 26 | public static class InnerClassInfo extends ClassFilePart { 27 | 28 | { 29 | u2cp("inner_class_info_index"); 30 | u2cp("outer_class_info_index"); 31 | u2cp("inner_name_index"); 32 | u2af("inner_class_access_flags", AccessFlagType.AF_NESTED_CLASS); 33 | } 34 | 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/attribute/LineNumberTableAttribute.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.attribute; 2 | 3 | import com.github.zxh.classpy.classfile.ClassFilePart; 4 | import com.github.zxh.classpy.classfile.constant.ConstantPool; 5 | 6 | /* 7 | LineNumberTable_attribute { 8 | u2 attribute_name_index; 9 | u4 attribute_length; 10 | u2 line_number_table_length; 11 | { u2 start_pc; 12 | u2 line_number; 13 | } line_number_table[line_number_table_length]; 14 | } 15 | */ 16 | public class LineNumberTableAttribute extends AttributeInfo { 17 | 18 | { 19 | u2 ("line_number_table_length"); 20 | table("line_number_table", LineNumberTableEntry.class); 21 | } 22 | 23 | 24 | public static class LineNumberTableEntry extends ClassFilePart { 25 | 26 | { 27 | u2("start_pc"); 28 | u2("line_number"); 29 | } 30 | 31 | @Override 32 | protected void postRead(ConstantPool cp) { 33 | int lineNumber = super.getUInt("line_number"); 34 | int startPc = super.getUInt("start_pc"); 35 | setName("line " + lineNumber); 36 | setDesc(Integer.toString(startPc)); 37 | } 38 | 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/attribute/LocalVariableTableAttribute.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.attribute; 2 | 3 | import com.github.zxh.classpy.classfile.ClassFilePart; 4 | import com.github.zxh.classpy.classfile.constant.ConstantPool; 5 | 6 | /* 7 | LocalVariableTable_attribute { 8 | u2 attribute_name_index; 9 | u4 attribute_length; 10 | u2 local_variable_table_length; 11 | { u2 start_pc; 12 | u2 length; 13 | u2 name_index; 14 | u2 descriptor_index; 15 | u2 index; 16 | } local_variable_table[local_variable_table_length]; 17 | } 18 | */ 19 | public class LocalVariableTableAttribute extends AttributeInfo { 20 | 21 | { 22 | u2 ("local_variable_table_length"); 23 | table("local_variable_table", LocalVariableTableEntry.class); 24 | } 25 | 26 | 27 | public static class LocalVariableTableEntry extends ClassFilePart { 28 | 29 | { 30 | u2 ("start_pc"); 31 | u2 ("length"); 32 | u2cp("name_index"); 33 | u2cp("descriptor_index"); 34 | u2 ("index"); 35 | } 36 | 37 | @Override 38 | protected void postRead(ConstantPool cp) { 39 | int startPc = super.getUInt("start_pc"); 40 | int length = super.getUInt("length"); 41 | int nameIndex = super.getUInt("name_index"); 42 | 43 | int endPc = startPc + length - 1; 44 | String varName = cp.getConstantDesc(nameIndex); 45 | setDesc(String.format("%s(%d~%d)", varName, startPc, endPc)); 46 | } 47 | 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/attribute/LocalVariableTypeTableAttribute.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.attribute; 2 | 3 | import com.github.zxh.classpy.classfile.ClassFilePart; 4 | import com.github.zxh.classpy.classfile.constant.ConstantPool; 5 | 6 | /* 7 | LocalVariableTypeTable_attribute { 8 | u2 attribute_name_index; 9 | u4 attribute_length; 10 | u2 local_variable_type_table_length; 11 | { u2 start_pc; 12 | u2 length; 13 | u2 name_index; 14 | u2 signature_index; 15 | u2 index; 16 | } local_variable_type_table[local_variable_type_table_length]; 17 | } 18 | */ 19 | public class LocalVariableTypeTableAttribute extends AttributeInfo { 20 | 21 | { 22 | u2 ("local_variable_type_table_length"); 23 | table("local_variable_type_table", LocalVariableTypeTableEntry.class); 24 | } 25 | 26 | 27 | public static class LocalVariableTypeTableEntry extends ClassFilePart { 28 | 29 | { 30 | u2 ("start_pc"); 31 | u2 ("length"); 32 | u2cp("name_index"); 33 | u2cp("signature_index"); 34 | u2 ("index"); 35 | } 36 | 37 | @Override 38 | protected void postRead(ConstantPool cp) { 39 | int nameIndex = super.getUInt("name_index"); 40 | setDesc(cp.getUtf8String(nameIndex)); 41 | } 42 | 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/attribute/MethodParametersAttribute.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.attribute; 2 | 3 | import com.github.zxh.classpy.classfile.ClassFilePart; 4 | 5 | /* 6 | MethodParameters_attribute { 7 | u2 attribute_name_index; 8 | u4 attribute_length; 9 | u1 parameters_count; 10 | { u2 name_index; 11 | u2 access_flags; 12 | } parameters[parameters_count]; 13 | } 14 | */ 15 | public class MethodParametersAttribute extends AttributeInfo { 16 | 17 | { 18 | u1 ("parameters_count"); 19 | table("parameters", ParameterInfo.class); 20 | } 21 | 22 | 23 | public static class ParameterInfo extends ClassFilePart { 24 | 25 | { 26 | u2("name_index"); 27 | u2("access_flags"); 28 | } 29 | 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/attribute/ModuleMainClassAttribute.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.attribute; 2 | 3 | /* 4 | ModuleMainClass_attribute { 5 | u2 attribute_name_index; 6 | u4 attribute_length; 7 | u2 main_class_index; 8 | } 9 | */ 10 | public class ModuleMainClassAttribute extends AttributeInfo { 11 | 12 | { 13 | u2cp("main_class_index"); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/attribute/ModulePackagesAttribute.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.attribute; 2 | 3 | import com.github.zxh.classpy.classfile.datatype.U2CpIndex; 4 | 5 | /* 6 | ModulePackages_attribute { 7 | u2 attribute_name_index; 8 | u4 attribute_length; 9 | u2 package_count; 10 | u2 package_index[package_count]; 11 | } 12 | */ 13 | public class ModulePackagesAttribute extends AttributeInfo { 14 | 15 | { 16 | u2("package_count"); 17 | table("package_index", U2CpIndex.class); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/attribute/RuntimeVisibleParameterAnnotationsAttribute.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.attribute; 2 | 3 | import com.github.zxh.classpy.classfile.ClassFilePart; 4 | import com.github.zxh.classpy.classfile.attribute.RuntimeVisibleAnnotationsAttribute.AnnotationInfo; 5 | 6 | /* 7 | RuntimeVisibleParameterAnnotations_attribute { 8 | u2 attribute_name_index; 9 | u4 attribute_length; 10 | u1 num_parameters; 11 | { u2 num_annotations; 12 | annotation annotations[num_annotations]; 13 | } parameter_annotations[num_parameters]; 14 | } 15 | */ 16 | public class RuntimeVisibleParameterAnnotationsAttribute extends AttributeInfo { 17 | 18 | { 19 | u1 ("num_parameters"); 20 | table("parameter_annotations", ParameterAnnotationInfo.class); 21 | } 22 | 23 | 24 | public static class ParameterAnnotationInfo extends ClassFilePart { 25 | 26 | { 27 | u2 ("num_annotations"); 28 | table("annotations", AnnotationInfo.class); 29 | } 30 | 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/attribute/SignatureAttribute.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.attribute; 2 | 3 | /* 4 | Signature_attribute { 5 | u2 attribute_name_index; 6 | u4 attribute_length; 7 | u2 signature_index; 8 | } 9 | */ 10 | public class SignatureAttribute extends AttributeInfo { 11 | 12 | { 13 | u2cp("signature_index"); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/attribute/SourceDebugExtensionAttribute.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.attribute; 2 | 3 | /* 4 | SourceDebugExtension_attribute { 5 | u2 attribute_name_index; 6 | u4 attribute_length; 7 | u1 debug_extension[attribute_length]; 8 | } 9 | */ 10 | public class SourceDebugExtensionAttribute extends AttributeInfo { 11 | 12 | { 13 | bytes("debug_extension"); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/attribute/SourceFileAttribute.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.attribute; 2 | 3 | /* 4 | SourceFile_attribute { 5 | u2 attribute_name_index; 6 | u4 attribute_length; 7 | u2 sourcefile_index; 8 | } 9 | */ 10 | public class SourceFileAttribute extends AttributeInfo { 11 | 12 | { 13 | u2cp("source_file_index"); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/attribute/StackMapTableAttribute.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.attribute; 2 | 3 | import com.github.zxh.classpy.classfile.ClassFileReader; 4 | 5 | /* 6 | StackMapTable_attribute { 7 | u2 attribute_name_index; 8 | u4 attribute_length; 9 | u2 number_of_entries; 10 | stack_map_frame entries[number_of_entries]; 11 | } 12 | */ 13 | public class StackMapTableAttribute extends AttributeInfo { 14 | 15 | { 16 | u2("number_of_entries"); 17 | } 18 | 19 | @Override 20 | protected void readContent(ClassFileReader reader) { 21 | super.readContent(reader); 22 | // todo 23 | reader.skipBytes(super.getUInt("attribute_length") - 2); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/attribute/SyntheticAttribute.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.attribute; 2 | 3 | /* 4 | Synthetic_attribute { 5 | u2 attribute_name_index; 6 | u4 attribute_length; 7 | } 8 | */ 9 | public class SyntheticAttribute extends AttributeInfo { 10 | 11 | } 12 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/attribute/UndefinedAttribute.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.attribute; 2 | 3 | /* 4 | attribute_info { 5 | u2 attribute_name_index; 6 | u4 attribute_length; 7 | u1 info[attribute_length]; 8 | } 9 | */ 10 | public class UndefinedAttribute extends AttributeInfo { 11 | 12 | { 13 | bytes("info"); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/bytecode/Bipush.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.bytecode; 2 | 3 | import com.github.zxh.classpy.classfile.ClassFileReader; 4 | import com.github.zxh.classpy.classfile.jvm.Opcode; 5 | 6 | public class Bipush extends Instruction { 7 | 8 | public Bipush(Opcode opcode, int pc) { 9 | super(opcode, pc); 10 | } 11 | 12 | @Override 13 | protected void readOperands(ClassFileReader reader) { 14 | byte operand = reader.readByte(); 15 | setDesc(getDesc() + " " + operand); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/bytecode/Branch.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.bytecode; 2 | 3 | import com.github.zxh.classpy.classfile.ClassFileReader; 4 | import com.github.zxh.classpy.classfile.jvm.Opcode; 5 | 6 | public class Branch extends Instruction { 7 | 8 | public Branch(Opcode opcode, int pc) { 9 | super(opcode, pc); 10 | } 11 | 12 | @Override 13 | protected void readOperands(ClassFileReader reader) { 14 | short offset = reader.readFixedI16(); 15 | int jmpTo = pc + offset; 16 | setDesc(getDesc() + " " + jmpTo); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/bytecode/Iinc.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.bytecode; 2 | 3 | import com.github.zxh.classpy.classfile.ClassFileReader; 4 | import com.github.zxh.classpy.classfile.jvm.Opcode; 5 | 6 | public class Iinc extends Instruction { 7 | 8 | public Iinc(Opcode opcode, int pc) { 9 | super(opcode, pc); 10 | } 11 | 12 | @Override 13 | protected void readOperands(ClassFileReader reader) { 14 | int index = reader.readFixedU8(); 15 | int _const = reader.readByte(); 16 | setDesc(getDesc() + " " + index + ", " + _const); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/bytecode/Instruction.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.bytecode; 2 | 3 | import com.github.zxh.classpy.classfile.ClassFilePart; 4 | import com.github.zxh.classpy.classfile.ClassFileReader; 5 | import com.github.zxh.classpy.classfile.jvm.Opcode; 6 | 7 | /** 8 | * Base class for all instructions. 9 | */ 10 | public class Instruction extends ClassFilePart { 11 | 12 | protected final Opcode opcode; 13 | protected final int pc; 14 | 15 | public Instruction(Opcode opcode, int pc) { 16 | this.opcode = opcode; 17 | this.pc = pc; 18 | setDesc(opcode.name()); 19 | } 20 | 21 | public int getPc() { 22 | return pc; 23 | } 24 | 25 | @Override 26 | protected final void readContent(ClassFileReader reader) { 27 | if (!super.getParts().isEmpty()) { 28 | super.readContent(reader); 29 | } else { 30 | reader.readFixedU8(); // opcode 31 | readOperands(reader); 32 | } 33 | } 34 | 35 | protected void readOperands(ClassFileReader reader) { 36 | if (opcode.operandCount > 0) { 37 | reader.skipBytes(opcode.operandCount); 38 | } 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/bytecode/InstructionCp1.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.bytecode; 2 | 3 | import com.github.zxh.classpy.classfile.constant.ConstantPool; 4 | import com.github.zxh.classpy.classfile.jvm.Opcode; 5 | 6 | /** 7 | * The instruction whose operand is U1CpIndex. 8 | */ 9 | public class InstructionCp1 extends Instruction { 10 | 11 | { 12 | u1 ("opcode"); 13 | u1cp("operand"); 14 | } 15 | 16 | public InstructionCp1(Opcode opcode, int pc) { 17 | super(opcode, pc); 18 | } 19 | 20 | @Override 21 | protected void postRead(ConstantPool cp) { 22 | setDesc(getDesc() + " " + super.get("operand").getDesc()); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/bytecode/InstructionCp2.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.bytecode; 2 | 3 | import com.github.zxh.classpy.classfile.constant.ConstantPool; 4 | import com.github.zxh.classpy.classfile.jvm.Opcode; 5 | 6 | /** 7 | * The instruction whose operand is U2CpIndex. 8 | */ 9 | public class InstructionCp2 extends Instruction { 10 | 11 | { 12 | u1 ("opcode"); 13 | u2cp("operand"); 14 | } 15 | 16 | public InstructionCp2(Opcode opcode, int pc) { 17 | super(opcode, pc); 18 | } 19 | 20 | protected void postRead(ConstantPool cp) { 21 | setDesc(getDesc() + " " + super.get("operand").getDesc()); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/bytecode/InstructionFactory.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.bytecode; 2 | 3 | import com.github.zxh.classpy.classfile.jvm.Opcode; 4 | 5 | public class InstructionFactory { 6 | 7 | /** 8 | * Create instruction by opcode. 9 | */ 10 | public static Instruction create(Opcode opcode, int pc) { 11 | // TODO 12 | return switch (opcode) { 13 | case ldc_w, ldc2_w, 14 | getstatic, putstatic, getfield, putfield, 15 | invokevirtual, invokespecial, invokestatic, 16 | _new, anewarray, checkcast, _instanceof -> new InstructionCp2(opcode, pc); 17 | case iload, lload, fload, dload, aload, 18 | istore, lstore, fstore, dstore, astore -> new InstructionU1(opcode, pc); 19 | case ifeq, ifne, iflt, ifge, ifgt, ifle, 20 | if_icmpeq, if_icmpne, if_icmplt, if_icmpge, if_icmpgt, if_icmple, 21 | _goto, ifnull, ifnonnull -> new Branch(opcode, pc); 22 | case bipush -> new Bipush(opcode, pc); 23 | case sipush -> new Sipush(opcode, pc); 24 | case ldc -> new InstructionCp1(opcode, pc); 25 | case iinc -> new Iinc(opcode, pc); 26 | case tableswitch -> new TableSwitch(opcode, pc); 27 | case lookupswitch -> new LookupSwitch(opcode, pc); 28 | case invokeinterface -> new InvokeInterface(opcode, pc); 29 | case invokedynamic -> new InvokeDynamic(opcode, pc); 30 | case newarray -> new NewArray(opcode, pc); 31 | case multianewarray -> new Multianewarray(opcode, pc); 32 | case wide -> new Wide(opcode, pc); 33 | default -> new Instruction(opcode, pc); 34 | }; 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/bytecode/InstructionU1.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.bytecode; 2 | 3 | import com.github.zxh.classpy.classfile.constant.ConstantPool; 4 | import com.github.zxh.classpy.classfile.jvm.Opcode; 5 | 6 | /** 7 | * The instruction whose operand is U1. 8 | */ 9 | public class InstructionU1 extends Instruction { 10 | 11 | { 12 | u1("opcode"); 13 | u1("operand"); 14 | } 15 | 16 | public InstructionU1(Opcode opcode, int pc) { 17 | super(opcode, pc); 18 | } 19 | 20 | @Override 21 | protected void postRead(ConstantPool cp) { 22 | setDesc(getDesc() + " " + super.get("operand").getDesc()); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/bytecode/InvokeDynamic.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.bytecode; 2 | 3 | import com.github.zxh.classpy.classfile.constant.ConstantPool; 4 | import com.github.zxh.classpy.classfile.jvm.Opcode; 5 | 6 | public class InvokeDynamic extends Instruction { 7 | 8 | { 9 | u1 ("opcode"); 10 | u2cp("index"); 11 | u2 ("zero"); 12 | } 13 | 14 | public InvokeDynamic(Opcode opcode, int pc) { 15 | super(opcode, pc); 16 | } 17 | 18 | @Override 19 | protected void postRead(ConstantPool cp) { 20 | setDesc(getDesc() + " " + super.get("index").getDesc()); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/bytecode/InvokeInterface.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.bytecode; 2 | 3 | import com.github.zxh.classpy.classfile.constant.ConstantPool; 4 | import com.github.zxh.classpy.classfile.jvm.Opcode; 5 | 6 | public class InvokeInterface extends Instruction { 7 | 8 | { 9 | u1 ("opcode"); 10 | u2cp("index"); 11 | u1 ("count"); 12 | u1 ("zero"); 13 | } 14 | 15 | public InvokeInterface(Opcode opcode, int pc) { 16 | super(opcode, pc); 17 | } 18 | 19 | @Override 20 | protected void postRead(ConstantPool cp) { 21 | setDesc(getDesc() + " " 22 | + super.get("index").getDesc() + ", " 23 | + super.getUInt("count")); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/bytecode/Multianewarray.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.bytecode; 2 | 3 | import com.github.zxh.classpy.classfile.ClassFileReader; 4 | import com.github.zxh.classpy.classfile.jvm.Opcode; 5 | 6 | public class Multianewarray extends Instruction { 7 | 8 | { 9 | u1 ("opcode"); 10 | u2cp("index"); 11 | u1 ("dimensions"); 12 | } 13 | 14 | public Multianewarray(Opcode opcode, int pc) { 15 | super(opcode, pc); 16 | } 17 | 18 | @Override 19 | protected void readOperands(ClassFileReader reader) { 20 | setDesc(getDesc() + " " 21 | + super.get("index").getDesc() + ", " 22 | + super.getUInt("dimensions")); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/bytecode/NewArray.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.bytecode; 2 | 3 | import com.github.zxh.classpy.common.ParseException; 4 | import com.github.zxh.classpy.classfile.constant.ConstantPool; 5 | import com.github.zxh.classpy.classfile.jvm.Opcode; 6 | 7 | public class NewArray extends Instruction { 8 | 9 | { 10 | u1("opcode"); 11 | u1("atype"); 12 | } 13 | 14 | public NewArray(Opcode opcode, int pc) { 15 | super(opcode, pc); 16 | } 17 | 18 | @Override 19 | protected void postRead(ConstantPool cp) { 20 | int atype = super.getUInt("atype"); 21 | setDesc(getDesc() + " " + getArrayType(atype)); 22 | } 23 | 24 | private static String getArrayType(int atype) { 25 | return switch (atype) { 26 | case 4 -> "boolean"; 27 | case 5 -> "char"; 28 | case 6 -> "float"; 29 | case 7 -> "double"; 30 | case 8 -> "byte"; 31 | case 9 -> "short"; 32 | case 10 -> "int"; 33 | case 11 -> "long"; 34 | default -> throw new ParseException("Invalid atype: " + atype); 35 | }; 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/bytecode/Sipush.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.bytecode; 2 | 3 | import com.github.zxh.classpy.classfile.ClassFileReader; 4 | import com.github.zxh.classpy.classfile.jvm.Opcode; 5 | 6 | public class Sipush extends Instruction { 7 | 8 | public Sipush(Opcode opcode, int pc) { 9 | super(opcode, pc); 10 | } 11 | 12 | @Override 13 | protected void readOperands(ClassFileReader reader) { 14 | short operand = reader.readFixedI16(); 15 | setDesc(getDesc() + " " + operand); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/bytecode/TableSwitch.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.bytecode; 2 | 3 | import com.github.zxh.classpy.classfile.ClassFilePart; 4 | import com.github.zxh.classpy.classfile.ClassFileReader; 5 | import com.github.zxh.classpy.classfile.jvm.Opcode; 6 | 7 | /* 8 | tableswitch 9 | <0-3 byte pad> 10 | defaultbyte1 11 | defaultbyte2 12 | defaultbyte3 13 | defaultbyte4 14 | lowbyte1 15 | lowbyte2 16 | lowbyte3 17 | lowbyte4 18 | highbyte1 19 | highbyte2 20 | highbyte3 21 | highbyte4 22 | jump offsets... 23 | */ 24 | public class TableSwitch extends Instruction { 25 | 26 | //private final List jumpOffsets = new ArrayList<>(); 27 | 28 | public TableSwitch(Opcode opcode, int pc) { 29 | super(opcode, pc); 30 | } 31 | 32 | @Override 33 | protected void readOperands(ClassFileReader reader) { 34 | skipPadding(reader); 35 | 36 | JumpOffset defaultOffset = readJumpOffset(reader, "default"); 37 | 38 | int low = reader.readFixedI32(); 39 | int high = reader.readFixedI32(); 40 | 41 | // high - low + 1 signed 32-bit offsets 42 | for (int i = low; i <= high; i++) { 43 | JumpOffset offset = readJumpOffset(reader, String.valueOf(i)); 44 | add(offset); 45 | } 46 | 47 | add(defaultOffset); 48 | } 49 | 50 | private void skipPadding(ClassFileReader reader) { 51 | for (int i = 1; (pc + i) %4 != 0; i++) { 52 | reader.readByte(); 53 | } 54 | } 55 | 56 | private JumpOffset readJumpOffset(ClassFileReader reader, String name) { 57 | JumpOffset offset = new JumpOffset(); 58 | offset.read(reader); 59 | offset.setName(name); 60 | offset.setDesc(Integer.toString(pc + offset.offset)); 61 | return offset; 62 | } 63 | 64 | 65 | public static class JumpOffset extends ClassFilePart { 66 | 67 | private int offset; 68 | 69 | @Override 70 | protected void readContent(ClassFileReader reader) { 71 | offset = reader.readFixedI32(); 72 | } 73 | 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/bytecode/Wide.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.bytecode; 2 | 3 | import com.github.zxh.classpy.classfile.ClassFileReader; 4 | import com.github.zxh.classpy.classfile.jvm.Opcode; 5 | 6 | public class Wide extends Instruction { 7 | 8 | public Wide(Opcode opcode, int pc) { 9 | super(opcode, pc); 10 | } 11 | 12 | @Override 13 | protected void readOperands(ClassFileReader reader) { 14 | int wideOpcode = reader.readFixedU8(); 15 | if (wideOpcode == Opcode.iinc.opcode) { 16 | reader.skipBytes(4); 17 | } else { 18 | reader.skipBytes(2); 19 | } 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/constant/ConstantClassInfo.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.constant; 2 | 3 | /* 4 | CONSTANT_Class_info { 5 | u1 tag; 6 | u2 name_index; 7 | } 8 | */ 9 | public class ConstantClassInfo extends ConstantInfo { 10 | 11 | { 12 | u2("name_index"); 13 | } 14 | 15 | public int getNameIndex() { 16 | return super.getUInt("name_index"); 17 | } 18 | 19 | @Override 20 | protected String loadDesc(ConstantPool cp) { 21 | return cp.getUtf8String(getNameIndex()); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/constant/ConstantDoubleInfo.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.constant; 2 | 3 | /* 4 | CONSTANT_Double_info { 5 | u1 tag; 6 | u4 high_bytes; 7 | u4 low_bytes; 8 | } 9 | */ 10 | public class ConstantDoubleInfo extends ConstantInfo { 11 | 12 | { 13 | u4hex("high_bytes"); 14 | u4hex("low_bytes"); 15 | } 16 | 17 | @Override 18 | protected String loadDesc(ConstantPool cp) { 19 | long high = super.getUInt("high_bytes"); 20 | long low = super.getUInt("low_bytes") & 0xffffffffL; 21 | double d = Double.longBitsToDouble((high << 32) + low); 22 | return String.valueOf(d); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/constant/ConstantFactory.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.constant; 2 | 3 | import com.github.zxh.classpy.common.ParseException; 4 | 5 | public class ConstantFactory { 6 | 7 | /** 8 | * Create concrete ConstantXxxInfo by tag. 9 | */ 10 | public static ConstantInfo create(byte tag) { 11 | return switch (tag) { 12 | case 1 -> new ConstantUtf8Info(); 13 | case 3 -> new ConstantIntegerInfo(); 14 | case 4 -> new ConstantFloatInfo(); 15 | case 5 -> new ConstantLongInfo(); 16 | case 6 -> new ConstantDoubleInfo(); 17 | case 7 -> new ConstantClassInfo(); 18 | case 8 -> new ConstantStringInfo(); 19 | case 9 -> new ConstantFieldrefInfo(); 20 | case 10 -> new ConstantMethodrefInfo(); 21 | case 11 -> new ConstantInterfaceMethodrefInfo(); 22 | case 12 -> new ConstantNameAndTypeInfo(); 23 | case 15 -> new ConstantMethodHandleInfo(); 24 | case 16 -> new ConstantMethodTypeInfo(); 25 | case 18 -> new ConstantInvokeDynamicInfo(); 26 | case 19 -> new ConstantModuleInfo(); 27 | case 20 -> new ConstantPackageInfo(); 28 | default -> throw new ParseException("Invalid Constant Type: " + tag); 29 | }; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/constant/ConstantFieldrefInfo.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.constant; 2 | 3 | /* 4 | CONSTANT_Fieldref_info { 5 | u1 tag; 6 | u2 class_index; 7 | u2 name_and_type_index; 8 | } 9 | */ 10 | public class ConstantFieldrefInfo extends ConstantInfo { 11 | 12 | { 13 | u2("class_index"); 14 | u2("name_and_type_index"); 15 | } 16 | 17 | @Override 18 | protected String loadDesc(ConstantPool cp) { 19 | int classIndex = super.getUInt("class_index"); 20 | int nameAndTypeIndex = super.getUInt("name_and_type_index"); 21 | 22 | ConstantClassInfo classInfo = cp.getClassInfo(classIndex); 23 | String className = cp.getUtf8String(classInfo.getNameIndex()); 24 | ConstantNameAndTypeInfo nameAndTypeInfo = cp.getNameAndTypeInfo(nameAndTypeIndex); 25 | String fieldName = cp.getUtf8String(nameAndTypeInfo.getNameIndex()); 26 | return className + "." + fieldName; 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/constant/ConstantFloatInfo.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.constant; 2 | 3 | /* 4 | CONSTANT_Float_info { 5 | u1 tag; 6 | u4 bytes; 7 | } 8 | */ 9 | public class ConstantFloatInfo extends ConstantInfo { 10 | 11 | { 12 | u4("bytes"); 13 | } 14 | 15 | @Override 16 | protected String loadDesc(ConstantPool cp) { 17 | float f = Float.intBitsToFloat(super.getUInt("bytes")); 18 | return Float.toString(f); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/constant/ConstantInfo.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.constant; 2 | 3 | import com.github.zxh.classpy.classfile.ClassFilePart; 4 | 5 | /* 6 | cp_info { 7 | u1 tag; 8 | u1 info[]; 9 | } 10 | */ 11 | public abstract class ConstantInfo extends ClassFilePart { 12 | 13 | { 14 | u1("tag"); 15 | } 16 | 17 | protected abstract String loadDesc(ConstantPool cp); 18 | 19 | } 20 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/constant/ConstantIntegerInfo.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.constant; 2 | 3 | /* 4 | CONSTANT_Integer_info { 5 | u1 tag; 6 | u4 bytes; 7 | } 8 | */ 9 | public class ConstantIntegerInfo extends ConstantInfo { 10 | 11 | { 12 | u4("bytes"); 13 | } 14 | 15 | @Override 16 | protected String loadDesc(ConstantPool cp) { 17 | int i = super.getUInt("bytes"); 18 | return String.valueOf(i); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/constant/ConstantInterfaceMethodrefInfo.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.constant; 2 | 3 | /* 4 | CONSTANT_InterfaceMethodref_info { 5 | u1 tag; 6 | u2 class_index; 7 | u2 name_and_type_index; 8 | } 9 | */ 10 | public class ConstantInterfaceMethodrefInfo extends ConstantMethodrefInfo { 11 | 12 | } 13 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/constant/ConstantInvokeDynamicInfo.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.constant; 2 | 3 | /* 4 | CONSTANT_InvokeDynamic_info { 5 | u1 tag; 6 | u2 bootstrap_method_attr_index; 7 | u2 name_and_type_index; 8 | } 9 | */ 10 | public class ConstantInvokeDynamicInfo extends ConstantInfo { 11 | 12 | { 13 | u2("bootstrap_method_attr_index"); 14 | u2("name_and_type_index"); 15 | } 16 | 17 | @Override 18 | protected String loadDesc(ConstantPool cp) { 19 | int nameAndTypeIndex = super.getUInt("name_and_type_index"); 20 | return cp.getNameAndTypeInfo(nameAndTypeIndex).loadDesc(cp); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/constant/ConstantLongInfo.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.constant; 2 | 3 | /* 4 | CONSTANT_Long_info { 5 | u1 tag; 6 | u4 high_bytes; 7 | u4 low_bytes; 8 | } 9 | */ 10 | public class ConstantLongInfo extends ConstantInfo { 11 | 12 | { 13 | u4hex("high_bytes"); 14 | u4hex("low_bytes"); 15 | } 16 | 17 | @Override 18 | protected String loadDesc(ConstantPool cp) { 19 | long high = super.getUInt("high_bytes"); 20 | long low = super.getUInt("low_bytes") & 0xffffffffL; 21 | long l = (high << 32) + low; 22 | return String.valueOf(l); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/constant/ConstantMethodHandleInfo.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.constant; 2 | 3 | import com.github.zxh.classpy.classfile.jvm.RefKind; 4 | 5 | /* 6 | CONSTANT_MethodHandle_info { 7 | u1 tag; 8 | u1 reference_kind; 9 | u2 reference_index; 10 | } 11 | */ 12 | public class ConstantMethodHandleInfo extends ConstantInfo { 13 | 14 | { 15 | u1("reference_kind"); 16 | u2("reference_index"); 17 | } 18 | 19 | @Override 20 | protected String loadDesc(ConstantPool cp) { 21 | int referenceKind = super.getUInt("reference_kind"); 22 | int referenceIndex = super.getUInt("reference_index"); 23 | 24 | RefKind refKind = RefKind.valueOf(referenceKind); 25 | switch (refKind) { 26 | case REF_getField: 27 | case REF_getStatic: 28 | case REF_putField: 29 | case REF_putStatic: 30 | return refKind + "->" + cp.getFieldrefInfo(referenceIndex).loadDesc(cp); 31 | case REF_invokeVirtual: 32 | case REF_newInvokeSpecial: 33 | return refKind + "->" + cp.getMethodrefInfo(referenceIndex).loadDesc(cp); 34 | case REF_invokeStatic: 35 | case REF_invokeSpecial: 36 | try { 37 | return refKind + "->" + cp.getMethodrefInfo(referenceIndex).loadDesc(cp); 38 | } catch (Exception e) { 39 | return refKind + "->" + cp.getInterfaceMethodrefInfo(referenceIndex).loadDesc(cp); 40 | } 41 | case REF_invokeInterface: 42 | return refKind + "->" + cp.getInterfaceMethodrefInfo(referenceIndex).loadDesc(cp); 43 | default:return null; 44 | } 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/constant/ConstantMethodTypeInfo.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.constant; 2 | 3 | /* 4 | CONSTANT_MethodType_info { 5 | u1 tag; 6 | u2 descriptor_index; 7 | } 8 | */ 9 | public class ConstantMethodTypeInfo extends ConstantInfo { 10 | 11 | { 12 | u2("descriptor_index"); 13 | } 14 | 15 | @Override 16 | protected String loadDesc(ConstantPool cp) { 17 | int descriptorIndex = super.getUInt("descriptor_index"); 18 | return cp.getUtf8String(descriptorIndex); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/constant/ConstantMethodrefInfo.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.constant; 2 | 3 | /* 4 | CONSTANT_Methodref_info { 5 | u1 tag; 6 | u2 class_index; 7 | u2 name_and_type_index; 8 | } 9 | */ 10 | public class ConstantMethodrefInfo extends ConstantFieldrefInfo { 11 | 12 | } 13 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/constant/ConstantModuleInfo.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.constant; 2 | 3 | /* 4 | CONSTANT_Module_info { 5 | u1 tag; 6 | u2 name_index; 7 | } 8 | */ 9 | public class ConstantModuleInfo extends ConstantInfo { 10 | 11 | { 12 | u2("name_index"); 13 | } 14 | 15 | public int getNameIndex() { 16 | return super.getUInt("name_index"); 17 | } 18 | 19 | @Override 20 | protected String loadDesc(ConstantPool cp) { 21 | return cp.getUtf8String(getNameIndex()); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/constant/ConstantNameAndTypeInfo.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.constant; 2 | 3 | /* 4 | CONSTANT_NameAndType_info { 5 | u1 tag; 6 | u2 name_index; 7 | u2 descriptor_index; 8 | } 9 | */ 10 | public class ConstantNameAndTypeInfo extends ConstantInfo { 11 | 12 | { 13 | u2("name_index"); 14 | u2("descriptor_index"); 15 | } 16 | 17 | public int getNameIndex() { 18 | return super.getUInt("name_index"); 19 | } 20 | 21 | @Override 22 | protected String loadDesc(ConstantPool cp) { 23 | String name = cp.getUtf8String(super.getUInt("name_index")); 24 | String type = cp.getUtf8String(super.getUInt("descriptor_index")); 25 | return name + "&" + type; 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/constant/ConstantPackageInfo.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.constant; 2 | 3 | /* 4 | CONSTANT_Package_info { 5 | u1 tag; 6 | u2 name_index; 7 | } 8 | */ 9 | public class ConstantPackageInfo extends ConstantInfo { 10 | 11 | { 12 | u2("name_index"); 13 | } 14 | 15 | public int getNameIndex() { 16 | return super.getUInt("name_index"); 17 | } 18 | 19 | @Override 20 | protected String loadDesc(ConstantPool cp) { 21 | return cp.getUtf8String(getNameIndex()); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/constant/ConstantStringInfo.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.constant; 2 | 3 | /* 4 | CONSTANT_String_info { 5 | u1 tag; 6 | u2 string_index; 7 | } 8 | */ 9 | public class ConstantStringInfo extends ConstantInfo { 10 | 11 | { 12 | u2("string_index"); 13 | } 14 | 15 | @Override 16 | protected String loadDesc(ConstantPool cp) { 17 | int stringIndex = super.getUInt("string_index"); 18 | return cp.getUtf8Info(stringIndex).loadDesc(cp); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/constant/ConstantUtf8Info.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.constant; 2 | 3 | import com.github.zxh.classpy.classfile.ClassFilePart; 4 | import com.github.zxh.classpy.common.ParseException; 5 | import com.github.zxh.classpy.classfile.datatype.U2; 6 | import com.github.zxh.classpy.classfile.ClassFileReader; 7 | import com.github.zxh.classpy.classfile.jvm.Mutf8Decoder; 8 | import com.github.zxh.classpy.helper.StringHelper; 9 | 10 | import java.io.IOException; 11 | 12 | /* 13 | CONSTANT_Utf8_info { 14 | u1 tag; 15 | u2 length; 16 | u1 bytes[length]; 17 | } 18 | */ 19 | public class ConstantUtf8Info extends ConstantInfo { 20 | 21 | { 22 | U2 length = new U2(); 23 | 24 | add("length", length); 25 | add("bytes", new Mutf8(length)); 26 | } 27 | 28 | public String getString() { 29 | return ((Mutf8) super.get("bytes")).str; 30 | } 31 | 32 | @Override 33 | protected String loadDesc(ConstantPool cp) { 34 | Mutf8 bytes = (Mutf8) super.get("bytes"); 35 | return StringHelper.cutAndAppendEllipsis(bytes.getDesc(), 100); 36 | } 37 | 38 | 39 | // UTF8 String in constant pool. 40 | private static class Mutf8 extends ClassFilePart { 41 | 42 | private final U2 length; 43 | private String str; 44 | 45 | public Mutf8(U2 length) { 46 | this.length = length; 47 | } 48 | 49 | @Override 50 | protected void readContent(ClassFileReader reader) { 51 | byte[] bytes = reader.readBytes(length.getValue()); 52 | try { 53 | str = Mutf8Decoder.decodeMutf8(bytes); 54 | } catch (IOException e) { 55 | throw new ParseException(e); 56 | } 57 | 58 | setDesc(str); 59 | } 60 | 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/datatype/Bytes.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.datatype; 2 | 3 | import com.github.zxh.classpy.classfile.ClassFilePart; 4 | import com.github.zxh.classpy.classfile.ClassFileReader; 5 | 6 | /** 7 | * Unparsed bytes. 8 | */ 9 | public class Bytes extends ClassFilePart { 10 | 11 | private final UInt count; 12 | 13 | public Bytes(UInt count) { 14 | this.count = count; 15 | } 16 | 17 | @Override 18 | protected void readContent(ClassFileReader reader) { 19 | reader.skipBytes(count.getValue()); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/datatype/U1.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.datatype; 2 | 3 | /** 4 | * Unsigned one-byte quantity. 5 | */ 6 | public class U1 extends UInt { 7 | 8 | public U1() { 9 | super(READ_U1, TO_STRING); 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/datatype/U1CpIndex.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.datatype; 2 | 3 | /** 4 | * Same as U1, but used as index of ConstantPool. 5 | */ 6 | public class U1CpIndex extends UInt { 7 | 8 | public U1CpIndex() { 9 | super(READ_U1, TO_CONST); 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/datatype/U1Hex.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.datatype; 2 | 3 | public class U1Hex extends UInt { 4 | 5 | public U1Hex() { 6 | super(READ_U1, TO_HEX); 7 | } 8 | 9 | } 10 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/datatype/U2.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.datatype; 2 | 3 | /** 4 | * Unsigned two-byte quantity. 5 | */ 6 | public class U2 extends UInt { 7 | 8 | public U2() { 9 | super(READ_U2, TO_STRING); 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/datatype/U2AccessFlags.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.datatype; 2 | 3 | import com.github.zxh.classpy.classfile.jvm.AccessFlags; 4 | 5 | import java.util.stream.Collectors; 6 | import java.util.stream.Stream; 7 | 8 | public class U2AccessFlags extends UInt { 9 | 10 | public U2AccessFlags(int afType) { 11 | super(READ_U2, (val, cp) -> describe(afType, val)); 12 | } 13 | 14 | private static String describe(int flagsType, int flagsVal) { 15 | return Stream.of(AccessFlags.values()) 16 | .filter(flag -> (flag.type & flagsType) != 0) 17 | .filter(flag -> (flag.flag & flagsVal) != 0) 18 | .map(Object::toString) 19 | .collect(Collectors.joining(", ")); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/datatype/U2CpIndex.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.datatype; 2 | 3 | /** 4 | * Same as U2, but used as index of ConstantPool. 5 | */ 6 | public class U2CpIndex extends UInt { 7 | 8 | public U2CpIndex() { 9 | super(READ_U2, TO_CONST); 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/datatype/U4.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.datatype; 2 | 3 | /** 4 | * Unsigned four-byte quantity. 5 | */ 6 | public class U4 extends UInt { 7 | 8 | public U4() { 9 | super(READ_U4, TO_STRING); 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/datatype/U4Hex.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.datatype; 2 | 3 | public class U4Hex extends UInt { 4 | 5 | public U4Hex() { 6 | super(READ_U4, TO_HEX); 7 | } 8 | 9 | } 10 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/datatype/UInt.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.datatype; 2 | 3 | import com.github.zxh.classpy.classfile.ClassFilePart; 4 | import com.github.zxh.classpy.classfile.constant.ConstantPool; 5 | import com.github.zxh.classpy.classfile.ClassFileReader; 6 | 7 | import java.util.function.BiFunction; 8 | import java.util.function.Function; 9 | 10 | public abstract class UInt extends ClassFilePart { 11 | 12 | protected static final Function READ_U1 = ClassFileReader::readFixedU8; 13 | protected static final Function READ_U2 = ClassFileReader::readFixedU16; 14 | protected static final Function READ_U4 = ClassFileReader::readFixedI32; 15 | 16 | protected static final BiFunction TO_STRING = 17 | (val, cp) -> val.toString(); 18 | protected static final BiFunction TO_HEX = 19 | (val, cp) -> "0x" + Integer.toHexString(val).toUpperCase(); 20 | protected static final BiFunction TO_CONST = 21 | (val, cp) -> val > 0 22 | ? "#" + val + "->" + cp.getConstantDesc(val) 23 | : "#" + val; 24 | 25 | 26 | private final Function intReader; 27 | private final BiFunction intDescriber; 28 | private int value; 29 | 30 | public UInt(Function intReader, 31 | BiFunction intDescriber) { 32 | this.intReader = intReader; 33 | this.intDescriber = intDescriber; 34 | } 35 | 36 | public final int getValue() { 37 | return value; 38 | } 39 | 40 | @Override 41 | protected final void readContent(ClassFileReader reader) { 42 | value = intReader.apply(reader); 43 | } 44 | 45 | @Override 46 | protected final void postRead(ConstantPool cp) { 47 | setDesc(intDescriber.apply(value, cp)); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/jvm/AccessFlagType.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.jvm; 2 | 3 | public class AccessFlagType { 4 | 5 | public static final int AF_CLASS = 0b00001; 6 | public static final int AF_FIELD = 0b00010; 7 | public static final int AF_METHOD = 0b00100; 8 | public static final int AF_NESTED_CLASS = 0b01000; 9 | public static final int AF_MODULE_ATTR = 0b10000; 10 | public static final int AF_ALL = 0b11111; 11 | 12 | } 13 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/jvm/AccessFlags.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.jvm; 2 | 3 | import static com.github.zxh.classpy.classfile.jvm.AccessFlagType.*; 4 | 5 | /** 6 | * Access and property flags of class, field, method and nested class. 7 | */ 8 | public enum AccessFlags { 9 | 10 | ACC_PUBLIC (0x0001, AF_ALL ), 11 | ACC_PRIVATE (0x0002, AF_FIELD | AF_METHOD | AF_NESTED_CLASS), 12 | ACC_PROTECTED (0x0004, AF_FIELD | AF_METHOD | AF_NESTED_CLASS), 13 | ACC_STATIC (0x0008, AF_FIELD | AF_METHOD | AF_NESTED_CLASS), 14 | ACC_FINAL (0x0010, AF_ALL ), 15 | ACC_SUPER (0x0020, AF_CLASS ), 16 | ACC_TRANSITIVE (0x0020, AF_MODULE_ATTR ), 17 | ACC_SYNCHRONIZED(0x0020, AF_METHOD ), 18 | ACC_VOLATILE (0x0040, AF_FIELD ), 19 | ACC_BRIDGE (0x0040, AF_METHOD ), 20 | ACC_STATIC_PHASE(0x0040, AF_MODULE_ATTR ), 21 | ACC_TRANSIENT (0x0080, AF_FIELD ), 22 | ACC_VARARGS (0x0080, AF_METHOD ), 23 | ACC_NATIVE (0x0100, AF_METHOD ), 24 | ACC_INTERFACE (0x0200, AF_CLASS | AF_NESTED_CLASS ), 25 | ACC_ABSTRACT (0x0400, AF_CLASS | AF_METHOD | AF_NESTED_CLASS), 26 | ACC_STRICT (0x0800, AF_METHOD ), 27 | ACC_SYNTHETIC (0x1000, AF_ALL ), 28 | ACC_ANNOTATION (0x2000, AF_CLASS | AF_NESTED_CLASS ), 29 | ACC_ENUM (0x4000, AF_CLASS | AF_FIELD | AF_NESTED_CLASS ), 30 | ACC_MODULE (0x8000, AF_CLASS ), 31 | ACC_MANDATED (0x8000, AF_MODULE_ATTR ), 32 | ; 33 | 34 | public final int flag; 35 | public final int type; 36 | 37 | AccessFlags(int flag, int type) { 38 | this.flag = flag; 39 | this.type = type; 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/jvm/Mutf8Decoder.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.jvm; 2 | 3 | import java.io.ByteArrayInputStream; 4 | import java.io.ByteArrayOutputStream; 5 | import java.io.DataInputStream; 6 | import java.io.DataOutputStream; 7 | import java.io.IOException; 8 | 9 | public class Mutf8Decoder { 10 | 11 | /** 12 | * Decode modified UTF-8 string from byte[]. 13 | * TODO: optimize 14 | */ 15 | public static String decodeMutf8(byte[] bytes) throws IOException { 16 | ByteArrayOutputStream baos = new ByteArrayOutputStream(bytes.length + 2); 17 | DataOutputStream dos = new DataOutputStream(baos); 18 | dos.writeShort(bytes.length); 19 | dos.write(bytes); 20 | 21 | ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); 22 | DataInputStream dis = new DataInputStream(bais); 23 | return dis.readUTF(); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/com/github/zxh/classpy/classfile/jvm/RefKind.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.jvm; 2 | 3 | // http://docs.oracle.com/javase/specs/jvms/se8/html/jvms-5.html#jvms-5.4.3.5 4 | public enum RefKind { 5 | 6 | REF_getField (1), 7 | REF_getStatic (2), 8 | REF_putField (3), 9 | REF_putStatic (4), 10 | REF_invokeVirtual (5), 11 | REF_invokeStatic (6), 12 | REF_invokeSpecial (7), 13 | REF_newInvokeSpecial(8), 14 | REF_invokeInterface (9), 15 | ; 16 | 17 | public final int kind; 18 | 19 | RefKind(int kind) { 20 | this.kind = kind; 21 | } 22 | 23 | public static RefKind valueOf(int kind) { 24 | for (RefKind value : values()) { 25 | if (value.kind == kind) { 26 | return value; 27 | } 28 | } 29 | 30 | throw new IllegalArgumentException("Invalid RefKind: " + kind); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /classpy-classfile/src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | module classpy.classfile { 2 | exports com.github.zxh.classpy.classfile; 3 | requires classpy.common; 4 | } 5 | -------------------------------------------------------------------------------- /classpy-classfile/src/test/java/com/github/zxh/classpy/classfile/ClassComponentTest.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile; 2 | 3 | import org.junit.Test; 4 | 5 | /** 6 | * 7 | * @author zxh 8 | */ 9 | public class ClassComponentTest { 10 | 11 | @Test 12 | public void _toString() { 13 | // name == null && desc == null 14 | // ClassFilePart fc = new ClassFilePart(); 15 | // assertEquals(ClassFilePart.class.getSimpleName(), fc.toString()); 16 | // 17 | // // desc == null 18 | // fc.setName("nama"); 19 | // assertEquals("nama", fc.toString()); 20 | // 21 | // // name == null 22 | // fc.setName(null); 23 | // fc.setDesc("dasc"); 24 | // assertEquals("dasc", fc.toString()); 25 | // 26 | // fc.setName("nama"); 27 | // fc.setDesc("dasc"); 28 | // assertEquals("nama: dasc", fc.toString()); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /classpy-classfile/src/test/java/com/github/zxh/classpy/classfile/Mutf8DecoderTest.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.io.DataOutput; 5 | import java.io.DataOutputStream; 6 | import java.io.IOException; 7 | import java.util.Arrays; 8 | import org.junit.Test; 9 | import static com.github.zxh.classpy.classfile.jvm.Mutf8Decoder.decodeMutf8; 10 | import static org.junit.Assert.assertEquals; 11 | 12 | /** 13 | * 14 | * @author zxh 15 | */ 16 | public class Mutf8DecoderTest { 17 | 18 | @Test 19 | public void ascii() throws IOException { 20 | String str = "abcdefg"; 21 | byte[] bytes = encodeMutf8(str); 22 | assertEquals(7, bytes.length); 23 | assertEquals(str, decodeMutf8(bytes)); 24 | } 25 | 26 | @Test 27 | public void u0000() throws IOException { 28 | String str = "\u0000"; 29 | byte[] bytes = encodeMutf8(str); 30 | assertEquals(2, bytes.length); 31 | assertEquals(str, decodeMutf8(bytes)); 32 | } 33 | 34 | @Test 35 | public void bmp() throws IOException { 36 | String str = "汉字"; 37 | byte[] bytes = encodeMutf8(str); 38 | assertEquals(6, bytes.length); 39 | assertEquals(str, decodeMutf8(bytes)); 40 | } 41 | 42 | @Test 43 | public void surrogatePair() throws IOException { 44 | String str = "\ud801\udc00"; 45 | byte[] bytes = encodeMutf8(str); 46 | assertEquals(6, bytes.length); 47 | assertEquals(str, decodeMutf8(bytes)); 48 | } 49 | 50 | private static byte[] encodeMutf8(String str) throws IOException { 51 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 52 | DataOutput out = new DataOutputStream(baos); 53 | out.writeUTF(str); 54 | byte[] bytes = baos.toByteArray(); 55 | // remove length 56 | return Arrays.copyOfRange(bytes, 2, bytes.length); 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /classpy-classfile/src/test/java/com/github/zxh/classpy/classfile/testclasses/CodeAttr.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.testclasses; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Comparator; 5 | import java.util.List; 6 | 7 | public class CodeAttr { 8 | 9 | public void simpleMethod(int x, Object y) { 10 | int a = 1; 11 | int b = 2; 12 | try { 13 | int c = a / b; 14 | } catch (Exception e) { 15 | // 16 | } 17 | } 18 | 19 | public void localVariableTypeTableAttribute() { 20 | List ss = new ArrayList<>(); 21 | Comparator c = null; 22 | int a = 1; 23 | int b = 1; 24 | int d = a + b; 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /classpy-classfile/src/test/java/com/github/zxh/classpy/classfile/testclasses/ConstantPool.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.testclasses; 2 | 3 | public class ConstantPool { 4 | 5 | public static final String TEST_CHINESE = "你好,中国。"; 6 | public static final String TEST_LONG_STRING = "qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq"; 7 | public static final String TEST_NEW_LINE = "hello\nworld!"; 8 | public static final String TEST_MUTF8 = "\u0000\ud801\udc00"; 9 | public static final int CONST_INT1 = 65535; 10 | public static final int CONST_INT2 = -8; 11 | public static final float CONST_FLOAT = 3.14f; 12 | public static final long CONST_LONG1 = Long.MAX_VALUE; 13 | public static final long CONST_LONG2 = -1; 14 | public static final double CONST_DOUBLE = -2.718d; 15 | 16 | private Runnable r; 17 | 18 | public void run() { 19 | r = () -> {}; 20 | r.run(); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /classpy-classfile/src/test/java/com/github/zxh/classpy/classfile/testclasses/GenericClass.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.testclasses; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | // SignatureAttribute 7 | public class GenericClass { 8 | 9 | // SignatureAttribute 10 | private final List listOfT = new ArrayList<>(); 11 | 12 | // SignatureAttribute 13 | public static > void m1(List list) { 14 | //list.sort((a, b) -> 1); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /classpy-classfile/src/test/java/com/github/zxh/classpy/classfile/testclasses/HelloWorld.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.testclasses; 2 | 3 | public class HelloWorld { 4 | 5 | public static void main(String[] args) { 6 | System.out.println("Hello, World!"); 7 | } 8 | 9 | } 10 | -------------------------------------------------------------------------------- /classpy-classfile/src/test/java/com/github/zxh/classpy/classfile/testclasses/MyInterface.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.testclasses; 2 | 3 | @FunctionalInterface 4 | public interface MyInterface { 5 | 6 | void foo(Object x, String y, int z); 7 | 8 | default void bar() { 9 | 10 | } 11 | 12 | static int sm(int x) { 13 | return x; 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /classpy-classfile/src/test/java/com/github/zxh/classpy/classfile/testclasses/SimpleAttr.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.testclasses; 2 | 3 | import java.io.IOException; 4 | 5 | // SourceFileAttribute 6 | // InnerClassesAttribute 7 | // BootstrapMethodsAttribute 8 | public class SimpleAttr { 9 | 10 | @Deprecated 11 | public void deprecatedAttribute() { 12 | 13 | } 14 | 15 | public void exceptionsAttribute() throws IOException, RuntimeException { 16 | 17 | } 18 | 19 | public void invokeDynamic() { 20 | Runnable r = () -> {}; 21 | } 22 | 23 | public void enclosingMethodAttribute() { 24 | new Runnable() { 25 | 26 | @Override 27 | public void run() { 28 | // 29 | } 30 | 31 | }.run(); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /classpy-classfile/src/test/java/com/github/zxh/classpy/classfile/testclasses/SimpleClass.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.testclasses; 2 | 3 | public class SimpleClass implements Runnable, Comparable { 4 | 5 | public int x; 6 | private float y; 7 | 8 | @Override 9 | public void run() { 10 | 11 | } 12 | 13 | @Override 14 | public int compareTo(SimpleClass o) { 15 | return 0; 16 | } 17 | 18 | public int foo() { 19 | return x + 1; 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /classpy-classfile/src/test/java/com/github/zxh/classpy/classfile/testclasses/annotations/MyClassAnnotation.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.testclasses.annotations; 2 | 3 | import java.lang.annotation.ElementType; 4 | import static java.lang.annotation.ElementType.CONSTRUCTOR; 5 | import static java.lang.annotation.ElementType.FIELD; 6 | import static java.lang.annotation.ElementType.LOCAL_VARIABLE; 7 | import static java.lang.annotation.ElementType.METHOD; 8 | import static java.lang.annotation.ElementType.PARAMETER; 9 | import static java.lang.annotation.ElementType.TYPE; 10 | import java.lang.annotation.Retention; 11 | import java.lang.annotation.RetentionPolicy; 12 | import java.lang.annotation.Target; 13 | 14 | @Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE}) 15 | @Retention(RetentionPolicy.CLASS) 16 | public @interface MyClassAnnotation { 17 | 18 | int intValue() default 1; 19 | String strValue() default "str"; 20 | ElementType enumValue() default ElementType.TYPE; 21 | Class classValue() default Object.class; 22 | Target annotationValue() default @Target({TYPE}); 23 | String[] arrayValue() default {"A", "B", "C"}; 24 | 25 | } 26 | -------------------------------------------------------------------------------- /classpy-classfile/src/test/java/com/github/zxh/classpy/classfile/testclasses/annotations/MyRuntimeAnnotation.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.testclasses.annotations; 2 | 3 | import java.lang.annotation.ElementType; 4 | import static java.lang.annotation.ElementType.CONSTRUCTOR; 5 | import static java.lang.annotation.ElementType.FIELD; 6 | import static java.lang.annotation.ElementType.LOCAL_VARIABLE; 7 | import static java.lang.annotation.ElementType.METHOD; 8 | import static java.lang.annotation.ElementType.PARAMETER; 9 | import static java.lang.annotation.ElementType.TYPE; 10 | import java.lang.annotation.Retention; 11 | import java.lang.annotation.RetentionPolicy; 12 | import java.lang.annotation.Target; 13 | 14 | @Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE}) 15 | @Retention(RetentionPolicy.RUNTIME) 16 | public @interface MyRuntimeAnnotation { 17 | 18 | int intValue() default 1; 19 | String strValue() default "str"; 20 | ElementType enumValue() default ElementType.TYPE; 21 | Class classValue() default Object.class; 22 | Target annotationValue() default @Target({TYPE}); 23 | String[] arrayValue() default {"A", "B", "C"}; 24 | 25 | } 26 | -------------------------------------------------------------------------------- /classpy-classfile/src/test/java/com/github/zxh/classpy/classfile/testclasses/annotations/MyTypeAnnotation.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.classfile.testclasses.annotations; 2 | 3 | import java.lang.annotation.ElementType; 4 | import static java.lang.annotation.ElementType.TYPE_USE; 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.RetentionPolicy; 7 | import java.lang.annotation.Target; 8 | 9 | @Target({TYPE_USE}) 10 | @Retention(RetentionPolicy.CLASS) 11 | public @interface MyTypeAnnotation { 12 | 13 | int intValue() default 1; 14 | String strValue() default "str"; 15 | ElementType enumValue() default ElementType.TYPE; 16 | Class classValue() default Object.class; 17 | Target annotationValue() default @Target({TYPE_USE}); 18 | String[] arrayValue() default {"A", "B", "C"}; 19 | 20 | } 21 | -------------------------------------------------------------------------------- /classpy-common/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java-library' 3 | } 4 | 5 | dependencies { 6 | testImplementation 'org.junit.jupiter:junit-jupiter-api:5.6.0' 7 | testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine' 8 | testCompileOnly 'junit:junit:4.13' 9 | testRuntimeOnly 'org.junit.vintage:junit-vintage-engine' 10 | } 11 | -------------------------------------------------------------------------------- /classpy-common/src/main/java/com/github/zxh/classpy/common/BytesReader.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.common; 2 | 3 | import java.nio.ByteBuffer; 4 | import java.nio.ByteOrder; 5 | 6 | public class BytesReader { 7 | 8 | private final ByteBuffer buf; 9 | 10 | public BytesReader(byte[] data, ByteOrder order) { 11 | this.buf = ByteBuffer.wrap(data) 12 | .asReadOnlyBuffer() 13 | .order(order); 14 | } 15 | 16 | public int remaining() { 17 | return buf.remaining(); 18 | } 19 | 20 | public int getPosition() { 21 | return buf.position(); 22 | } 23 | 24 | public byte getFixedI8(int index) { 25 | return buf.get(index); 26 | } 27 | 28 | public short getFixedI16(int index) { 29 | return buf.getShort(index); 30 | } 31 | 32 | public byte readByte() { 33 | return readFixedI8(); 34 | } 35 | 36 | // 8-bit signed int 37 | public byte readFixedI8() { 38 | return buf.get(); 39 | } 40 | 41 | // 8-bit unsigned int 42 | public int readFixedU8() { 43 | return Byte.toUnsignedInt(buf.get()); 44 | } 45 | 46 | // 16-bit signed int 47 | public short readFixedI16() { 48 | return buf.getShort(); 49 | } 50 | 51 | // 16-bit unsigned int 52 | public int readFixedU16() { 53 | return Short.toUnsignedInt(buf.getShort()); 54 | } 55 | 56 | // 32-bit signed int 57 | public int readFixedI32() { 58 | return buf.getInt(); 59 | } 60 | 61 | // 32-bit unsigned int 62 | public long readFixedU32() { 63 | return Integer.toUnsignedLong(buf.getInt()); 64 | } 65 | 66 | // 64-bit signed int 67 | public long readFixedI64() { 68 | return buf.getLong(); 69 | } 70 | 71 | // 32-bit float 72 | public float readF32() { 73 | return buf.getFloat(); 74 | } 75 | 76 | // 64-bit float 77 | public double readF64() { 78 | return buf.getDouble(); 79 | } 80 | 81 | // byte[] 82 | public byte[] readBytes(int n) { 83 | byte[] bytes = new byte[n]; 84 | buf.get(bytes); 85 | return bytes; 86 | } 87 | 88 | public void skipBytes(int n) { 89 | for (int i = 0; i < n; i++) { 90 | buf.get(); 91 | } 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /classpy-common/src/main/java/com/github/zxh/classpy/common/FileParser.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.common; 2 | 3 | public interface FileParser { 4 | 5 | FileParser NOP = data -> new FilePart() {}; 6 | 7 | FilePart parse(byte[] data); 8 | 9 | } 10 | -------------------------------------------------------------------------------- /classpy-common/src/main/java/com/github/zxh/classpy/common/FixedInt.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.common; 2 | 3 | public abstract class FixedInt extends ReadableFilePart { 4 | 5 | public enum IntType { I8, I16, I32, I64, U8, U16, U32, U64 } 6 | public enum IntDesc { Dec, Hex } 7 | 8 | private final IntType intType; 9 | private final IntDesc intDesc; 10 | private long value; 11 | 12 | public FixedInt(IntType intType, 13 | IntDesc intDesc) { 14 | this.intType = intType; 15 | this.intDesc = intDesc; 16 | } 17 | 18 | public final long getValue() { 19 | return value; 20 | } 21 | 22 | @Override 23 | protected final void readContent(R reader) { 24 | value = switch (intType) { 25 | case I8 -> reader.readFixedI8(); 26 | case U8 -> reader.readFixedU8(); 27 | case I16 -> reader.readFixedI16(); 28 | case U16 -> reader.readFixedU16(); 29 | case I32 -> reader.readFixedI32(); 30 | case U32 -> reader.readFixedU32(); 31 | case I64, U64 -> reader.readFixedI64(); 32 | }; 33 | switch (intDesc) { 34 | case Dec: setDesc(isUnsigned() 35 | ? Long.toUnsignedString(value) 36 | : Long.toString(value)); 37 | case Hex: setDesc("0x" + Long.toHexString(value).toUpperCase()); 38 | } 39 | } 40 | 41 | private boolean isUnsigned() { 42 | return switch (intType) { 43 | case U8, U16, U32, U64 -> true; 44 | default -> false; 45 | }; 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /classpy-common/src/main/java/com/github/zxh/classpy/common/ParseException.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.common; 2 | 3 | @SuppressWarnings("serial") 4 | public class ParseException extends RuntimeException { 5 | 6 | public ParseException(String message) { 7 | super(message); 8 | } 9 | 10 | public ParseException(Throwable cause) { 11 | super(cause); 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /classpy-common/src/main/java/com/github/zxh/classpy/common/ReadableFilePart.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.common; 2 | 3 | public abstract class ReadableFilePart extends FilePart { 4 | 5 | /** 6 | * Reads content, records offset and length. 7 | */ 8 | public final void read(R reader) { 9 | try { 10 | int offset = reader.getPosition(); 11 | readContent(reader); 12 | int length = reader.getPosition() - offset; 13 | super.setOffset(offset); 14 | super.setLength(length); 15 | } catch (Exception e) { 16 | System.out.println("error parsing: " + getClass()); 17 | throw e; 18 | } 19 | } 20 | 21 | /** 22 | * Reads content using reader. 23 | */ 24 | protected void readContent(R reader) { 25 | for (FilePart fp : getParts()) { 26 | @SuppressWarnings("unchecked") 27 | var rfp = (ReadableFilePart) fp; 28 | rfp.read(reader); 29 | } 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /classpy-common/src/main/java/com/github/zxh/classpy/helper/StringHelper.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.helper; 2 | 3 | public class StringHelper { 4 | 5 | /** 6 | * Convert index to String. 7 | * Examples: 8 | * maxIndex index result 9 | * 9 8 #8 10 | * 15 8 #08 11 | * 15 12 #12 12 | * 123 8 #008 13 | * 123 12 #012 14 | * 123 120 #120 15 | */ 16 | public static String formatIndex(int maxIndex, int index) { 17 | int idxWidth = String.valueOf(maxIndex).length(); 18 | String fmtStr = "#%0" + idxWidth + "d"; 19 | return String.format(fmtStr, index); 20 | } 21 | 22 | /** 23 | * Cut the string and append ellipsis. 24 | */ 25 | public static String cutAndAppendEllipsis(String str, int maxLength) { 26 | str = str.replaceAll("[\\r\\n]", ""); 27 | 28 | if (str.length() <= maxLength) { 29 | return str; 30 | } 31 | 32 | int cutPos = maxLength - 3; 33 | char firstCutChar = str.charAt(cutPos); 34 | 35 | if (Character.isLowSurrogate(firstCutChar)) { 36 | return str.substring(0, cutPos - 1) + "..."; 37 | } else { 38 | return str.substring(0, cutPos) + "..."; 39 | } 40 | } 41 | 42 | /** 43 | * Convert hex string to byte array. 44 | */ 45 | public static byte[] hex2Bytes(String str) { 46 | byte[] bytes = new byte[str.length() / 2]; 47 | for (int i = 0; i < bytes.length; i++) { 48 | bytes[i] = (byte) Integer.parseInt( 49 | str.substring(2 * i, 2 * i + 2), 16); 50 | } 51 | return bytes; 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /classpy-common/src/main/java/com/github/zxh/classpy/helper/UrlHelper.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.helper; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | import java.io.InputStreamReader; 7 | import java.net.URL; 8 | 9 | public class UrlHelper { 10 | 11 | public static String readOneLine(String url) throws IOException { 12 | try (BufferedReader reader = new BufferedReader(new InputStreamReader(new URL(url).openStream()))) { 13 | return reader.readLine(); 14 | } 15 | } 16 | 17 | public static byte[] readData(String url) throws IOException { 18 | if (url.startsWith("jmod:")) { 19 | url = url.replace("jmod:", "jar:"); 20 | } 21 | try (InputStream is = new URL(url).openStream()) { 22 | byte[] data = new byte[is.available()]; 23 | int len = 0; 24 | while (len < data.length) { 25 | len += is.read(data, len, data.length - len); 26 | } 27 | return data; 28 | } 29 | } 30 | 31 | public static String getFileName(String url) { 32 | int lastSlashIdx = url.lastIndexOf('/'); 33 | return lastSlashIdx < 0 ? url : url.substring(lastSlashIdx + 1); 34 | } 35 | 36 | public static String getFileSuffix(String url) { 37 | String fileName = getFileName(url); 38 | int lastDotIdx = fileName.lastIndexOf('.'); 39 | return lastDotIdx < 0 ? "" : fileName.substring(lastDotIdx + 1); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /classpy-common/src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | module classpy.common { 2 | exports com.github.zxh.classpy.common; 3 | exports com.github.zxh.classpy.helper; 4 | } -------------------------------------------------------------------------------- /classpy-common/src/test/java/com/github/zxh/classpy/BytesReaderTest.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy; 2 | 3 | import com.github.zxh.classpy.common.BytesReader; 4 | import org.junit.Test; 5 | 6 | import java.nio.ByteOrder; 7 | 8 | import static org.junit.Assert.assertEquals; 9 | 10 | /** 11 | * 12 | * @author zxh 13 | */ 14 | public class BytesReaderTest { 15 | 16 | @Test 17 | public void order() { 18 | BytesReader be = new BytesReader(new byte[] {0x12, 0x34}, 19 | ByteOrder.BIG_ENDIAN); 20 | assertEquals(0x1234, be.readFixedI16()); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /classpy-common/src/test/java/com/github/zxh/classpy/StringHelperTest.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy; 2 | 3 | import com.github.zxh.classpy.helper.StringHelper; 4 | import org.junit.Test; 5 | 6 | import static org.junit.Assert.assertEquals; 7 | 8 | /** 9 | * 10 | * @author zxh 11 | */ 12 | public class StringHelperTest { 13 | 14 | @Test 15 | public void cutAndAppendEllipsis() { 16 | assertEquals("aaaaa", StringHelper.cutAndAppendEllipsis("aaaaa", 5)); 17 | assertEquals("aa...", StringHelper.cutAndAppendEllipsis("aaaaaa", 5)); 18 | assertEquals("aa...", StringHelper.cutAndAppendEllipsis("aa\ud801\udc00aa", 5)); 19 | assertEquals("a...", StringHelper.cutAndAppendEllipsis("a\ud801\udc00aaa", 5)); 20 | 21 | //assertEquals("...", StringHelper.cutAndAppendEllipsis("\naaa", 5)); 22 | //assertEquals("a...", StringHelper.cutAndAppendEllipsis("a\naaa", 5)); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /classpy-gui/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'application' 3 | id 'org.openjfx.javafxplugin' version '0.0.8' 4 | id 'org.beryx.jlink' version '2.12.0' 5 | // https://github.com/openjfx/javafx-gradle-plugin/issues/90 6 | // id 'org.openjfx.javafxplugin' version '0.0.9' 7 | } 8 | 9 | javafx { 10 | modules = [ 'javafx.controls', 'javafx.fxml' ] 11 | } 12 | 13 | dependencies { 14 | implementation project(':classpy-common') 15 | implementation project(':classpy-classfile') 16 | implementation project(':classpy-binarychunk') 17 | implementation project(':classpy-bitcoin') 18 | implementation project(':classpy-wasm') 19 | // implementation project(':utilities') 20 | // testImplementation 'org.junit.jupiter:junit-jupiter-api:5.6.1' 21 | // testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine' 22 | } 23 | 24 | application { 25 | mainModule = 'classpy.gui' 26 | mainClass = 'com.github.zxh.classpy.gui.ClasspyApp' 27 | } 28 | 29 | // https://stackoverflow.com/questions/51864473/where-do-resource-files-go-in-a-gradle-project-that-builds-a-java-9-module/51921521 30 | sourceSets { 31 | main { 32 | output.setResourcesDir(java.outputDir) 33 | } 34 | } 35 | 36 | // https://www.baeldung.com/gradle-fat-jar 37 | // https://stackoverflow.com/questions/57452363/building-fatjar-for-javafx-with-gradle-and-intellij-getting-noclassdeffounderro 38 | // https://stackoverflow.com/questions/52569724/javafx-11-create-a-jar-file-with-gradle 39 | task fatJar(type: Jar) { 40 | manifest { 41 | attributes 'Main-Class': 'com.github.zxh.classpy.gui.Main' 42 | } 43 | archiveBaseName = 'classpy-fat-jar' 44 | from { configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } } 45 | with jar 46 | } 47 | 48 | mainClassName = "$moduleName/com.github.zxh.classpy.gui.ClasspyApp" 49 | 50 | jlink { 51 | options = ['--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages'] 52 | launcher { 53 | name = 'classpy' 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /classpy-gui/src/main/java/com/github/zxh/classpy/gui/AboutDialog.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.gui; 2 | 3 | import com.github.zxh.classpy.gui.events.EventBus; 4 | import com.github.zxh.classpy.gui.events.OpenAboutDialog; 5 | import com.github.zxh.classpy.gui.support.ImageHelper; 6 | import javafx.geometry.Insets; 7 | import javafx.geometry.Pos; 8 | import javafx.scene.Scene; 9 | import javafx.scene.control.Hyperlink; 10 | import javafx.scene.layout.BorderPane; 11 | import javafx.stage.Modality; 12 | import javafx.stage.Stage; 13 | 14 | class AboutDialog { 15 | 16 | public static void showDialog(EventBus eventBus) { 17 | Stage stage = new Stage(); 18 | stage.initModality(Modality.APPLICATION_MODAL); 19 | 20 | BorderPane aboutPane = createAboutPane(stage, eventBus); 21 | Scene scene = new Scene(aboutPane, 300, 180); 22 | 23 | stage.setScene(scene); 24 | stage.setTitle("About"); 25 | stage.show(); 26 | } 27 | 28 | private static BorderPane createAboutPane(Stage dialogStage, 29 | EventBus eventBus) { 30 | BorderPane pane = new BorderPane(); 31 | //pane.setTop(new Label("Classpy")); 32 | pane.setCenter(ImageHelper.createImageView("/spy128.png")); 33 | pane.setBottom(createHomeLink(eventBus)); 34 | pane.setOnMouseClicked(e -> dialogStage.close()); 35 | 36 | return pane; 37 | } 38 | 39 | // https://stackoverflow.com/questions/16604341/how-can-i-open-the-default-system-browser-from-a-java-fx-application 40 | private static Hyperlink createHomeLink(EventBus eventBus) { 41 | String homeUrl = "https://github.com/zxh0/classpy"; 42 | Hyperlink link = new Hyperlink(homeUrl); 43 | link.setOnAction(e -> eventBus.pub(new OpenAboutDialog(homeUrl))); 44 | 45 | BorderPane.setAlignment(link, Pos.CENTER); 46 | BorderPane.setMargin(link, new Insets(8)); 47 | return link; 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /classpy-gui/src/main/java/com/github/zxh/classpy/gui/Main.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.gui; 2 | 3 | // https://stackoverflow.com/questions/52569724/javafx-11-create-a-jar-file-with-gradle 4 | public class Main { 5 | 6 | public static void main(String[] args) { 7 | ClasspyApp.main(args); 8 | } 9 | 10 | } 11 | -------------------------------------------------------------------------------- /classpy-gui/src/main/java/com/github/zxh/classpy/gui/MyFileChooser.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.gui; 2 | 3 | import com.github.zxh.classpy.gui.support.FileType; 4 | import com.github.zxh.classpy.gui.support.RecentFiles; 5 | import javafx.stage.DirectoryChooser; 6 | import javafx.stage.FileChooser; 7 | import javafx.stage.Stage; 8 | 9 | import java.io.File; 10 | 11 | class MyFileChooser { 12 | 13 | private static FileChooser fileChooser; 14 | private static DirectoryChooser dirChooser; 15 | 16 | public static File showFileChooser(Stage stage, FileType ft) { 17 | if (fileChooser == null) { 18 | fileChooser = new FileChooser(); 19 | fileChooser.setTitle("Open file"); 20 | } 21 | 22 | File lastOpenFile = RecentFiles.INSTANCE.getLastOpenFile(ft); 23 | if (lastOpenFile != null && lastOpenFile.getParentFile().isDirectory()) { 24 | fileChooser.setInitialDirectory(lastOpenFile.getParentFile()); 25 | } 26 | 27 | fileChooser.getExtensionFilters().clear(); 28 | fileChooser.getExtensionFilters().add(ft.filter); 29 | 30 | return fileChooser.showOpenDialog(stage); 31 | } 32 | 33 | public static File showDirChooser(Stage stage) { 34 | if (dirChooser == null) { 35 | dirChooser = new DirectoryChooser(); 36 | dirChooser.setTitle("Open folder"); 37 | } 38 | 39 | return dirChooser.showDialog(stage); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /classpy-gui/src/main/java/com/github/zxh/classpy/gui/events/CloseAllTabs.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.gui.events; 2 | 3 | public class CloseAllTabs { 4 | } 5 | -------------------------------------------------------------------------------- /classpy-gui/src/main/java/com/github/zxh/classpy/gui/events/EventBus.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.gui.events; 2 | 3 | import java.util.*; 4 | import java.util.function.Consumer; 5 | 6 | // A very simple EventBus implementation. 7 | public class EventBus { 8 | 9 | private final Map, List>> listeners = new HashMap<>(); 10 | 11 | public void sub(Class cls, Consumer listener) { 12 | listeners.computeIfAbsent(cls, k -> new ArrayList<>()) 13 | .add(listener); 14 | } 15 | 16 | @SuppressWarnings("unchecked") 17 | public void pub(T event) { 18 | listeners.getOrDefault(event.getClass(), Collections.emptyList()) 19 | .forEach(listener -> ((Consumer) listener).accept(event)); 20 | } 21 | 22 | // public static void main(String[] args) { 23 | // var eb = new EventBus(); 24 | // eb.sub(String.class, x -> System.out.println("str:" + x)); 25 | // eb.sub(Integer.class, x -> System.out.println("int:" + x)); 26 | // eb.pub("123"); 27 | // eb.pub(123); 28 | // } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /classpy-gui/src/main/java/com/github/zxh/classpy/gui/events/OpenAboutDialog.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.gui.events; 2 | 3 | public class OpenAboutDialog { 4 | 5 | public final String url; 6 | 7 | public OpenAboutDialog(String url) { 8 | this.url = url; 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /classpy-gui/src/main/java/com/github/zxh/classpy/gui/events/OpenFile.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.gui.events; 2 | 3 | import com.github.zxh.classpy.gui.support.FileType; 4 | 5 | public class OpenFile { 6 | 7 | public final FileType fileType; 8 | public final String fileUrl; 9 | 10 | public OpenFile(FileType fileType, String fileUrl) { 11 | this.fileType = fileType; 12 | this.fileUrl = fileUrl; 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /classpy-gui/src/main/java/com/github/zxh/classpy/gui/events/OpenNewWindow.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.gui.events; 2 | 3 | public class OpenNewWindow { 4 | } 5 | -------------------------------------------------------------------------------- /classpy-gui/src/main/java/com/github/zxh/classpy/gui/events/UpdateRecentFiles.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.gui.events; 2 | 3 | public class UpdateRecentFiles { 4 | } 5 | -------------------------------------------------------------------------------- /classpy-gui/src/main/java/com/github/zxh/classpy/gui/fs/BaseTreeNode.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.gui.fs; 2 | 3 | import java.nio.file.Files; 4 | import java.nio.file.Path; 5 | 6 | public abstract class BaseTreeNode implements Comparable { 7 | 8 | final Path path; 9 | final String name; 10 | 11 | BaseTreeNode(Path path) { 12 | this.path = path; 13 | if (path.getFileName() != null) { 14 | this.name = path.getFileName().toString(); 15 | } else { 16 | this.name = path.toString(); 17 | } 18 | } 19 | 20 | boolean isDir() { 21 | return Files.isDirectory(path); 22 | } 23 | 24 | @Override 25 | public String toString() { 26 | return name; 27 | } 28 | 29 | @Override 30 | public int compareTo(BaseTreeNode that) { 31 | if (this.isDir() && !that.isDir()) { 32 | return -1; 33 | } else if (!this.isDir() && that.isDir()) { 34 | return 1; 35 | } else { 36 | return this.name.compareTo(that.name); 37 | } 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /classpy-gui/src/main/java/com/github/zxh/classpy/gui/fs/DirTreeItem.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.gui.fs; 2 | 3 | import javafx.collections.FXCollections; 4 | import javafx.collections.ObservableList; 5 | import javafx.scene.control.TreeItem; 6 | 7 | import java.util.List; 8 | import java.util.stream.Collectors; 9 | 10 | public class DirTreeItem extends TreeItem { 11 | 12 | private boolean isFirstTimeChildren = true; 13 | 14 | public DirTreeItem(DirTreeNode root) { 15 | super(root); 16 | } 17 | 18 | @Override 19 | public boolean isLeaf() { 20 | return !getValue().hasSubNodes(); 21 | } 22 | 23 | @Override 24 | public ObservableList> getChildren() { 25 | if (isFirstTimeChildren) { 26 | isFirstTimeChildren = false; 27 | System.out.println("get children of " + getValue()); 28 | 29 | // First getChildren() call, so we actually go off and 30 | // determine the children of the File contained in this TreeItem. 31 | super.getChildren().setAll(buildChildren()); 32 | } 33 | 34 | return super.getChildren(); 35 | } 36 | 37 | private ObservableList> buildChildren() { 38 | List items = getValue().getSubNodes().stream() 39 | .map(DirTreeItem::new) 40 | .collect(Collectors.toList()); 41 | 42 | return FXCollections.observableArrayList(items); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /classpy-gui/src/main/java/com/github/zxh/classpy/gui/fs/DirTreeNode.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.gui.fs; 2 | 3 | import java.io.IOException; 4 | import java.nio.file.*; 5 | import java.nio.file.attribute.BasicFileAttributes; 6 | import java.util.ArrayList; 7 | import java.util.Collections; 8 | import java.util.EnumSet; 9 | import java.util.List; 10 | 11 | public class DirTreeNode extends BaseTreeNode { 12 | 13 | public DirTreeNode(Path path) { 14 | super(path); 15 | } 16 | 17 | boolean hasSubNodes() { 18 | return super.isDir(); 19 | } 20 | 21 | List getSubNodes() { 22 | List nodes = new ArrayList<>(); 23 | 24 | try { 25 | Files.walkFileTree(path, EnumSet.noneOf(FileVisitOption.class), 1, 26 | new SimpleFileVisitor<>() { 27 | 28 | @Override 29 | public FileVisitResult visitFile(Path subPath, BasicFileAttributes attrs) { 30 | if (!subPath.getFileName().toString().startsWith(".")) { 31 | nodes.add(new DirTreeNode(subPath)); 32 | } 33 | return FileVisitResult.CONTINUE; 34 | } 35 | 36 | }); 37 | } catch (IOException e) { 38 | e.printStackTrace(System.err); 39 | } 40 | 41 | Collections.sort(nodes); 42 | return nodes; 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /classpy-gui/src/main/java/com/github/zxh/classpy/gui/fs/DirTreeView.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.gui.fs; 2 | 3 | import javafx.scene.control.TreeItem; 4 | import javafx.scene.control.TreeView; 5 | 6 | import java.io.File; 7 | import java.util.function.Consumer; 8 | 9 | public class DirTreeView { 10 | 11 | private final TreeView treeView; 12 | private Consumer openFileHandler; 13 | 14 | public DirTreeView(DirTreeNode rootNode) { 15 | this.treeView = createTreeView(rootNode); 16 | } 17 | 18 | public TreeView getTreeView() { 19 | return treeView; 20 | } 21 | 22 | public void setOpenFileHandler(Consumer openFileHandler) { 23 | this.openFileHandler = openFileHandler; 24 | } 25 | 26 | private TreeView createTreeView(DirTreeNode rootNode) { 27 | DirTreeItem rootItem = new DirTreeItem(rootNode); 28 | rootItem.setExpanded(false); 29 | 30 | TreeView tree = new TreeView<>(rootItem); 31 | tree.setOnMouseClicked(event -> { 32 | if (event.getClickCount() == 2) { 33 | File selectedFile = getSelectedFile(); 34 | if (selectedFile != null && openFileHandler != null) { 35 | System.out.println(selectedFile); 36 | openFileHandler.accept(selectedFile); 37 | } 38 | } 39 | }); 40 | 41 | return tree; 42 | } 43 | 44 | private File getSelectedFile() { 45 | TreeItem selectedItem = treeView.getSelectionModel().getSelectedItem(); 46 | if (selectedItem != null) { 47 | DirTreeNode selectedPath = selectedItem.getValue(); 48 | return selectedPath.path.toFile(); 49 | } 50 | return null; 51 | } 52 | 53 | public static DirTreeView create(File dir) { 54 | return new DirTreeView(new DirTreeNode(dir.toPath())); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /classpy-gui/src/main/java/com/github/zxh/classpy/gui/fs/ZipTreeItem.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.gui.fs; 2 | 3 | import javafx.collections.FXCollections; 4 | import javafx.collections.ObservableList; 5 | import javafx.scene.control.TreeItem; 6 | 7 | import java.util.List; 8 | import java.util.stream.Collectors; 9 | 10 | public class ZipTreeItem extends TreeItem { 11 | 12 | private boolean isFirstTimeChildren = true; 13 | 14 | public ZipTreeItem(ZipTreeNode root) { 15 | super(root); 16 | } 17 | 18 | @Override 19 | public boolean isLeaf() { 20 | return !getValue().hasSubNodes(); 21 | } 22 | 23 | @Override 24 | public ObservableList> getChildren() { 25 | if (isFirstTimeChildren) { 26 | isFirstTimeChildren = false; 27 | System.out.println("get children of " + getValue()); 28 | super.getChildren().setAll(buildChildren()); 29 | } 30 | 31 | return super.getChildren(); 32 | } 33 | 34 | private ObservableList> buildChildren() { 35 | List items = getValue().subNodes.stream() 36 | .map(ZipTreeItem::new) 37 | .collect(Collectors.toList()); 38 | 39 | return FXCollections.observableArrayList(items); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /classpy-gui/src/main/java/com/github/zxh/classpy/gui/fs/ZipTreeLoader.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.gui.fs; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.nio.file.*; 6 | import java.nio.file.attribute.BasicFileAttributes; 7 | import java.util.EnumSet; 8 | 9 | public class ZipTreeLoader { 10 | 11 | public static ZipTreeNode load(File zipFile) throws Exception { 12 | try (FileSystem zipFS = FileSystems.newFileSystem(zipFile.toPath())) { 13 | return path2node(zipFS.getPath("/")); 14 | } 15 | } 16 | 17 | private static ZipTreeNode path2node(Path path) throws IOException { 18 | ZipTreeNode node = new ZipTreeNode(path); 19 | 20 | Files.walkFileTree(path, EnumSet.noneOf(FileVisitOption.class), 1, 21 | new SimpleFileVisitor<>() { 22 | 23 | @Override 24 | public FileVisitResult visitFile(Path subPath, BasicFileAttributes attrs) throws IOException { 25 | if (Files.isDirectory(subPath)) { 26 | ZipTreeNode subNode = path2node(subPath); 27 | if (subNode.hasSubNodes()) { 28 | node.addSubNode(subNode); 29 | } 30 | } else if (isClassFile(subPath)) { 31 | node.addSubNode(new ZipTreeNode(subPath)); 32 | } 33 | 34 | return FileVisitResult.CONTINUE; 35 | } 36 | 37 | }); 38 | 39 | node.sortSubNodes(); 40 | return node; 41 | } 42 | 43 | private static boolean isClassFile(Path p) { 44 | return p.toString().endsWith(".class"); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /classpy-gui/src/main/java/com/github/zxh/classpy/gui/fs/ZipTreeNode.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.gui.fs; 2 | 3 | import java.nio.file.Path; 4 | import java.util.ArrayList; 5 | import java.util.Collections; 6 | import java.util.List; 7 | 8 | public class ZipTreeNode extends BaseTreeNode { 9 | 10 | List subNodes; 11 | 12 | public ZipTreeNode(Path path) { 13 | super(path); 14 | } 15 | 16 | boolean hasSubNodes() { 17 | return subNodes != null && !subNodes.isEmpty(); 18 | } 19 | 20 | void addSubNode(ZipTreeNode node) { 21 | if (subNodes == null) { 22 | subNodes = new ArrayList<>(); 23 | } 24 | subNodes.add(node); 25 | } 26 | 27 | void sortSubNodes() { 28 | if (subNodes != null) { 29 | Collections.sort(subNodes); 30 | } 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /classpy-gui/src/main/java/com/github/zxh/classpy/gui/parsed/BytesBar.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.gui.parsed; 2 | 3 | import com.github.zxh.classpy.common.FilePart; 4 | import javafx.scene.layout.Pane; 5 | import javafx.scene.shape.Line; 6 | import javafx.scene.shape.Rectangle; 7 | 8 | public class BytesBar extends Pane { 9 | 10 | private final int byteCount; 11 | 12 | public BytesBar(int byteCount) { 13 | this.byteCount = byteCount; 14 | } 15 | 16 | public void select(FilePart cc) { 17 | getChildren().clear(); 18 | 19 | final double w = getWidth() - 4; 20 | final double h = getHeight(); 21 | 22 | getChildren().add(new Line(0, h / 2, w, h / 2)); 23 | getChildren().add(new Rectangle(w * cc.getOffset() / byteCount, 4, 24 | w * cc.getLength() / byteCount, h - 8)); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /classpy-gui/src/main/java/com/github/zxh/classpy/gui/parsed/HexPane.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.gui.parsed; 2 | 3 | import javafx.scene.control.TextArea; 4 | import javafx.scene.text.Font; 5 | import com.github.zxh.classpy.common.FilePart; 6 | 7 | public class HexPane extends TextArea { 8 | 9 | private final HexText hex; 10 | 11 | public HexPane(HexText hex) { 12 | super(hex.getText()); 13 | this.hex = hex; 14 | setEditable(false); 15 | // http://stackoverflow.com/questions/24983841/format-text-output-in-javafx 16 | setFont(Font.font("Courier New", 14)); 17 | } 18 | 19 | public void select(FilePart cc) { 20 | HexText.Selection selection = hex.select(cc); 21 | positionCaret(selection.getStartPosition()); 22 | selectPositionCaret(selection.getEndPosition()); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /classpy-gui/src/main/java/com/github/zxh/classpy/gui/parsed/ParsedTreeItem.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.gui.parsed; 2 | 3 | import com.github.zxh.classpy.common.FilePart; 4 | import javafx.collections.FXCollections; 5 | import javafx.collections.ObservableList; 6 | import javafx.scene.control.TreeItem; 7 | 8 | public class ParsedTreeItem extends TreeItem { 9 | 10 | private boolean isFirstTimeChildren = true; 11 | 12 | public ParsedTreeItem(FilePart cc) { 13 | super(cc); 14 | } 15 | 16 | @Override 17 | public boolean isLeaf() { 18 | return getValue().getParts().isEmpty(); 19 | } 20 | 21 | // build children lazily 22 | @Override 23 | public ObservableList> getChildren() { 24 | if (isFirstTimeChildren) { 25 | isFirstTimeChildren = false; 26 | System.out.println("get children of " + getValue()); 27 | 28 | // First getChildren() call, so we actually go off and 29 | // determine the children of the File contained in this TreeItem. 30 | super.getChildren().setAll(buildChildren()); 31 | } 32 | 33 | return super.getChildren(); 34 | } 35 | 36 | private ObservableList> buildChildren() { 37 | ObservableList> children = FXCollections.observableArrayList(); 38 | getValue().getParts().forEach(sub -> children.add(new ParsedTreeItem(sub))); 39 | return children; 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /classpy-gui/src/main/java/com/github/zxh/classpy/gui/support/FileType.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.gui.support; 2 | 3 | import com.github.zxh.classpy.bitcoin.BlockParser; 4 | import com.github.zxh.classpy.bitcoin.TxParser; 5 | import com.github.zxh.classpy.classfile.ClassFileParser; 6 | import com.github.zxh.classpy.common.FileParser; 7 | import com.github.zxh.classpy.lua.BinaryChunkParser; 8 | import com.github.zxh.classpy.wasm.WasmBinParser; 9 | import javafx.scene.image.Image; 10 | import javafx.stage.FileChooser.ExtensionFilter; 11 | 12 | /** 13 | * Supported file types. 14 | */ 15 | public enum FileType { 16 | 17 | FOLDER ("/folder.png", "Folder", "/", null), 18 | JAVA_JAR ("/jar.png", "Java JAR", "*.jar", null), 19 | JAVA_JMOD ("/jmod.png", "Java JMOD", "*.jmod", null), 20 | JAVA_CLASS ("/java.png", "Java Class", "*.class", new ClassFileParser()), 21 | LUA_BC ("/lua.png", "Lua Binary Chunk", "*.luac", new BinaryChunkParser()), 22 | WASM ("/wasm.png", "Wasm Binary Code", "*.wasm", new WasmBinParser()), 23 | BITCOIN_BLOCK("/bitcoin.png", "Bitcoin Block", "?", new BlockParser()), 24 | BITCOIN_TX ("/bitcoin.png", "Bitcoin Transaction", "?", new TxParser()), 25 | UNKNOWN ("/file.png", "Unknown", "*.*", FileParser.NOP), 26 | ; 27 | 28 | public final Image icon; 29 | public final ExtensionFilter filter; 30 | public final FileParser parser; 31 | 32 | FileType(String icon, 33 | String description, 34 | String extension, 35 | FileParser parser) { 36 | this.icon = ImageHelper.loadImage(icon); 37 | this.filter = new ExtensionFilter(description, extension); 38 | this.parser = parser; 39 | } 40 | 41 | public boolean isZip() { 42 | return this == JAVA_JAR || this == JAVA_JMOD; 43 | } 44 | 45 | public boolean isBitcoin() { 46 | return this == BITCOIN_BLOCK || this == BITCOIN_TX; 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /classpy-gui/src/main/java/com/github/zxh/classpy/gui/support/FileTypeInferer.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.gui.support; 2 | 3 | import java.util.Arrays; 4 | 5 | public class FileTypeInferer { 6 | 7 | private static final byte[] wasmMagicNumber = {0, 'a', 's', 'm'}; 8 | private static final byte[] binaryChunkSig = {0x1B, 'L', 'u', 'a'}; 9 | private static final byte[] classMagicNumber = { 10 | (byte) 0xCA, (byte) 0xFE, (byte) 0xBA, (byte) 0xBE 11 | }; 12 | 13 | public static FileType inferFileType(String url) { 14 | url = url.toLowerCase(); 15 | if (url.startsWith("https://blockchain.info/rawblock")) { 16 | return FileType.BITCOIN_BLOCK; 17 | } else if (url.startsWith("https://blockchain.info/rawtx")) { 18 | return FileType.BITCOIN_TX; 19 | } else if (url.endsWith(".jar")) { 20 | return FileType.JAVA_JAR; 21 | } else if (url.endsWith(".jmod")) { 22 | return FileType.JAVA_JMOD; 23 | } else if (url.endsWith(".class")) { 24 | return FileType.JAVA_CLASS; 25 | } else if (url.endsWith(".luac")) { 26 | return FileType.LUA_BC; 27 | } else if (url.endsWith(".wasm")) { 28 | return FileType.WASM; 29 | } else { 30 | return FileType.UNKNOWN; 31 | } 32 | } 33 | 34 | public static FileType inferFileType(byte[] data) { 35 | if (data.length >= 4) { 36 | byte[] magicNumber = Arrays.copyOf(data, 4); 37 | if (Arrays.equals(magicNumber, classMagicNumber)) { 38 | return FileType.JAVA_CLASS; 39 | } else if (Arrays.equals(magicNumber, binaryChunkSig)) { 40 | return FileType.LUA_BC; 41 | } else if (Arrays.equals(magicNumber, wasmMagicNumber)) { 42 | return FileType.WASM; 43 | } 44 | } 45 | return FileType.UNKNOWN; 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /classpy-gui/src/main/java/com/github/zxh/classpy/gui/support/ImageHelper.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.gui.support; 2 | 3 | import java.net.URL; 4 | import javafx.scene.image.Image; 5 | import javafx.scene.image.ImageView; 6 | 7 | public class ImageHelper { 8 | 9 | public static ImageView createImageView(String imgName) { 10 | return new ImageView(loadImage(imgName)); 11 | } 12 | 13 | public static Image loadImage(String imgName) { 14 | URL imgUrl = ImageHelper.class.getResource(imgName); 15 | return new Image(imgUrl.toString()); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /classpy-gui/src/main/java/com/github/zxh/classpy/gui/support/OpenFileResult.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.gui.support; 2 | 3 | import com.github.zxh.classpy.common.FilePart; 4 | import com.github.zxh.classpy.gui.parsed.HexText; 5 | import com.github.zxh.classpy.gui.fs.ZipTreeNode; 6 | 7 | public class OpenFileResult { 8 | 9 | public final String url; 10 | public final FileType fileType; 11 | public final ZipTreeNode zipRootNode; 12 | public final FilePart fileRootNode; 13 | public final HexText hexText; 14 | 15 | public OpenFileResult(String url, FileType fileType, 16 | ZipTreeNode zipRootNode) { 17 | this.url = url; 18 | this.fileType = fileType; 19 | this.zipRootNode = zipRootNode; 20 | this.fileRootNode = null; 21 | this.hexText = null; 22 | } 23 | 24 | public OpenFileResult(String url, FileType fileType, 25 | FilePart fileRootNode, HexText hexText) { 26 | this.url = url; 27 | this.fileType = fileType; 28 | this.zipRootNode = null; 29 | this.fileRootNode = fileRootNode; 30 | this.hexText = hexText; 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /classpy-gui/src/main/java/com/github/zxh/classpy/gui/support/RecentFile.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.gui.support; 2 | 3 | /** 4 | * Recent open file. 5 | */ 6 | public class RecentFile { 7 | 8 | public final FileType type; 9 | public final String url; 10 | 11 | public RecentFile(FileType type, String url) { 12 | this.type = type; 13 | this.url = url; 14 | } 15 | 16 | public RecentFile(String str) { 17 | this(FileType.valueOf(str.split("#=>")[0]), str.split("#=>")[1]); 18 | } 19 | 20 | @Override 21 | public String toString() { 22 | return type + "#=>" + url; 23 | } 24 | 25 | @Override 26 | public int hashCode() { 27 | return url.hashCode(); 28 | } 29 | 30 | @Override 31 | public boolean equals(Object o) { 32 | return (o instanceof RecentFile) 33 | && ((RecentFile) o).url.equals(this.url); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /classpy-gui/src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | module classpy.gui { 2 | exports com.github.zxh.classpy.gui; 3 | requires classpy.common; 4 | requires classpy.classfile; 5 | requires classpy.binarychunk; 6 | requires classpy.bitcoin; 7 | requires classpy.wasm; 8 | requires javafx.controls; 9 | } 10 | -------------------------------------------------------------------------------- /classpy-gui/src/main/resources/bitcoin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zxh0/classpy/c907b08082ac9b6b1e8eaa2fbf9f35320a7d241a/classpy-gui/src/main/resources/bitcoin.png -------------------------------------------------------------------------------- /classpy-gui/src/main/resources/classpy.css: -------------------------------------------------------------------------------- 1 | .tree-cell { 2 | -fx-font-family: Courier New ; 3 | -fx-font-size: 14 ; 4 | } -------------------------------------------------------------------------------- /classpy-gui/src/main/resources/clock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zxh0/classpy/c907b08082ac9b6b1e8eaa2fbf9f35320a7d241a/classpy-gui/src/main/resources/clock.png -------------------------------------------------------------------------------- /classpy-gui/src/main/resources/file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zxh0/classpy/c907b08082ac9b6b1e8eaa2fbf9f35320a7d241a/classpy-gui/src/main/resources/file.png -------------------------------------------------------------------------------- /classpy-gui/src/main/resources/folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zxh0/classpy/c907b08082ac9b6b1e8eaa2fbf9f35320a7d241a/classpy-gui/src/main/resources/folder.png -------------------------------------------------------------------------------- /classpy-gui/src/main/resources/jar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zxh0/classpy/c907b08082ac9b6b1e8eaa2fbf9f35320a7d241a/classpy-gui/src/main/resources/jar.png -------------------------------------------------------------------------------- /classpy-gui/src/main/resources/java.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zxh0/classpy/c907b08082ac9b6b1e8eaa2fbf9f35320a7d241a/classpy-gui/src/main/resources/java.png -------------------------------------------------------------------------------- /classpy-gui/src/main/resources/jmod.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zxh0/classpy/c907b08082ac9b6b1e8eaa2fbf9f35320a7d241a/classpy-gui/src/main/resources/jmod.png -------------------------------------------------------------------------------- /classpy-gui/src/main/resources/lua.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zxh0/classpy/c907b08082ac9b6b1e8eaa2fbf9f35320a7d241a/classpy-gui/src/main/resources/lua.png -------------------------------------------------------------------------------- /classpy-gui/src/main/resources/spy128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zxh0/classpy/c907b08082ac9b6b1e8eaa2fbf9f35320a7d241a/classpy-gui/src/main/resources/spy128.png -------------------------------------------------------------------------------- /classpy-gui/src/main/resources/spy16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zxh0/classpy/c907b08082ac9b6b1e8eaa2fbf9f35320a7d241a/classpy-gui/src/main/resources/spy16.png -------------------------------------------------------------------------------- /classpy-gui/src/main/resources/spy32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zxh0/classpy/c907b08082ac9b6b1e8eaa2fbf9f35320a7d241a/classpy-gui/src/main/resources/spy32.png -------------------------------------------------------------------------------- /classpy-gui/src/main/resources/wasm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zxh0/classpy/c907b08082ac9b6b1e8eaa2fbf9f35320a7d241a/classpy-gui/src/main/resources/wasm.png -------------------------------------------------------------------------------- /classpy-wasm/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java-library' 3 | } 4 | 5 | dependencies { 6 | implementation project(':classpy-common') 7 | 8 | testImplementation 'org.junit.jupiter:junit-jupiter-api:5.6.0' 9 | testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine' 10 | testCompileOnly 'junit:junit:4.13' 11 | testRuntimeOnly 'org.junit.vintage:junit-vintage-engine' 12 | } 13 | -------------------------------------------------------------------------------- /classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/Vector.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.wasm; 2 | 3 | import java.util.function.Supplier; 4 | 5 | public class Vector extends WasmBinPart { 6 | 7 | private final Supplier supplier; 8 | 9 | public Vector(Supplier supplier) { 10 | this.supplier = supplier; 11 | } 12 | 13 | @Override 14 | protected void readContent(WasmBinReader reader) { 15 | int length = readU32(reader, "length"); 16 | for (int i = 0; i < length; i++) { 17 | WasmBinPart element = supplier.get(); 18 | add(null, element); 19 | element.read(reader); 20 | } 21 | setDesc(String.valueOf(length)); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/WasmBinParser.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.wasm; 2 | 3 | import com.github.zxh.classpy.common.FilePart; 4 | import com.github.zxh.classpy.common.FileParser; 5 | 6 | public class WasmBinParser implements FileParser { 7 | 8 | @Override 9 | public WasmBinFile parse(byte[] data) { 10 | WasmBinFile wasm = new WasmBinFile(); 11 | try { 12 | wasm.read(new WasmBinReader(data)); 13 | postRead(wasm, wasm); 14 | } catch (Exception e) { 15 | e.printStackTrace(System.err); 16 | } 17 | return wasm; 18 | } 19 | 20 | private static void postRead(WasmBinPart part, 21 | WasmBinFile wasm) { 22 | for (FilePart p : part.getParts()) { 23 | postRead((WasmBinPart) p, wasm); 24 | } 25 | part.postRead(wasm); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/WasmBinReader.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.wasm; 2 | 3 | import com.github.zxh.classpy.common.BytesReader; 4 | 5 | import java.nio.ByteOrder; 6 | 7 | // https://en.wikipedia.org/wiki/LEB128 8 | public class WasmBinReader extends BytesReader { 9 | 10 | public WasmBinReader(byte[] data) { 11 | super(data, ByteOrder.LITTLE_ENDIAN); 12 | } 13 | 14 | public long readU32() { 15 | return readUnsignedLEB128(32); 16 | } 17 | 18 | public long readU64() { 19 | return readUnsignedLEB128(64); 20 | } 21 | 22 | public long readS32() { 23 | return readSignedLEB128(32); 24 | } 25 | 26 | public long readS64() { 27 | return readSignedLEB128(64); 28 | } 29 | 30 | private long readUnsignedLEB128(int nBits) { 31 | long result = 0; 32 | for (int shift = 0; shift < nBits; shift += 7) { 33 | int b = readByte(); 34 | result |= ((b & 0b0111_1111) << shift); 35 | if ((b & 0b1000_0000) == 0) { 36 | return result; 37 | } 38 | } 39 | 40 | throw new RuntimeException("can not decode unsigned LEB128"); 41 | } 42 | 43 | private long readSignedLEB128(int size) { 44 | long result = 0; 45 | int shift = 0; 46 | //size = number of bits in signed integer; 47 | byte b; 48 | do{ 49 | b = readByte(); 50 | result |= ((b & 0b0111_1111) << shift); 51 | shift += 7; 52 | } while ((b & 0b1000_0000) != 0); 53 | 54 | /* sign bit of byte is second high order bit (0x40) */ 55 | if ((shift < size) && ((b & 0b0100_0000) != 0)) { 56 | /* sign extend */ 57 | result |= (~0 << shift); 58 | } 59 | 60 | return result; 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/instructions/Expr.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.wasm.instructions; 2 | 3 | import com.github.zxh.classpy.wasm.WasmBinPart; 4 | import com.github.zxh.classpy.wasm.WasmBinReader; 5 | 6 | public class Expr extends WasmBinPart { 7 | 8 | @Override 9 | protected void readContent(WasmBinReader reader) { 10 | while (reader.remaining() > 0) { 11 | Instr instr = read(reader, null, new Instr()); 12 | if (instr.getOpcode() == 0x0B) { // end 13 | break; 14 | } 15 | } 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/sections/Code.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.wasm.sections; 2 | 3 | import com.github.zxh.classpy.wasm.WasmBinPart; 4 | import com.github.zxh.classpy.wasm.WasmBinFile; 5 | import com.github.zxh.classpy.wasm.WasmBinReader; 6 | 7 | import java.util.Base64; 8 | 9 | public class Code extends WasmBinPart { 10 | 11 | @Override 12 | protected void readContent(WasmBinReader reader) { 13 | setName("code"); 14 | int size = readU32(reader, "size"); 15 | 16 | int pos = reader.getPosition(); 17 | byte[] code = reader.readBytes(size); 18 | Func func = new Func(); 19 | add("func", func); 20 | 21 | try { 22 | func.read(new WasmBinReader(code) { 23 | @Override 24 | public int getPosition() { 25 | return pos + super.getPosition(); 26 | } 27 | }); 28 | } catch (Exception e) { 29 | System.err.println(e.getMessage()); 30 | System.err.println(Base64.getEncoder().encodeToString(code)); 31 | } 32 | } 33 | 34 | 35 | public static class Func extends WasmBinPart { 36 | 37 | { 38 | vector("locals", Locals::new); 39 | expr("expr"); 40 | } 41 | 42 | } 43 | 44 | 45 | private static class Locals extends WasmBinPart { 46 | 47 | { 48 | u32("n"); 49 | valType("type"); 50 | } 51 | 52 | @Override 53 | protected void postRead(WasmBinFile wasm) { 54 | setDesc(get("type").getDesc() + " x " + get("n").getDesc()); 55 | } 56 | 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/sections/Data.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.wasm.sections; 2 | 3 | import com.github.zxh.classpy.wasm.WasmBinPart; 4 | import com.github.zxh.classpy.wasm.WasmBinReader; 5 | 6 | public class Data extends WasmBinPart { 7 | 8 | { 9 | idx("memidx"); 10 | expr("offset"); 11 | add("init", new Init()); 12 | setName("data"); 13 | } 14 | 15 | private static class Init extends WasmBinPart { 16 | 17 | @Override 18 | protected void readContent(WasmBinReader reader) { 19 | int length = readU32(reader, "length"); 20 | readBytes(reader, "bytes", length); 21 | } 22 | 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/sections/Element.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.wasm.sections; 2 | 3 | import com.github.zxh.classpy.wasm.WasmBinPart; 4 | import com.github.zxh.classpy.wasm.values.U32; 5 | 6 | public class Element extends WasmBinPart { 7 | 8 | { 9 | idx("tableidx"); 10 | expr("offset"); 11 | vector("init", U32::new); 12 | setName("element"); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/sections/Export.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.wasm.sections; 2 | 3 | import com.github.zxh.classpy.common.ParseException; 4 | import com.github.zxh.classpy.wasm.WasmBinPart; 5 | import com.github.zxh.classpy.wasm.WasmBinFile; 6 | import com.github.zxh.classpy.wasm.WasmBinReader; 7 | import com.github.zxh.classpy.wasm.types.FuncType; 8 | 9 | public class Export extends WasmBinPart { 10 | 11 | private int funcIdx = -1; 12 | 13 | public int getFuncIdx() { 14 | return funcIdx; 15 | } 16 | 17 | @Override 18 | protected void readContent(WasmBinReader reader) { 19 | String name = readName(reader, "name"); 20 | Desc desc = read(reader, "desc", new Desc()); 21 | if (desc.b == 0) { 22 | funcIdx = desc.idx; 23 | setDesc(name + "()"); 24 | } else { 25 | setDesc(name); 26 | } 27 | } 28 | 29 | @Override 30 | protected void postRead(WasmBinFile wasm) { 31 | if (funcIdx >= 0) { 32 | int relFuncIdx = funcIdx - wasm.getImportedFuncs().size(); 33 | int funcTypeIdx = wasm.getFuncs().get(relFuncIdx).getIntValue(); 34 | FuncType funcType = wasm.getFuncTypes().get(funcTypeIdx); 35 | setDesc(getDesc().replace("()", funcType.getDesc())); 36 | } 37 | } 38 | 39 | 40 | private static class Desc extends WasmBinPart { 41 | 42 | private int b; 43 | private int idx; 44 | 45 | @Override 46 | protected void readContent(WasmBinReader reader) { 47 | b = readByte(reader, null); 48 | switch (b) { 49 | case 0x00 -> idx = readIndex(reader, "func"); // funcidx 50 | case 0x01 -> idx = readIndex(reader, "table"); // tableidx 51 | case 0x02 -> idx = readIndex(reader, "mem"); // memidx 52 | case 0x03 -> idx = readIndex(reader, "global"); // globalidx 53 | default -> throw new ParseException("Invalid export desc: " + b); 54 | } 55 | } 56 | 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/sections/Global.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.wasm.sections; 2 | 3 | import com.github.zxh.classpy.wasm.WasmBinPart; 4 | import com.github.zxh.classpy.wasm.WasmBinFile; 5 | import com.github.zxh.classpy.wasm.instructions.Expr; 6 | import com.github.zxh.classpy.wasm.types.GlobalType; 7 | import com.github.zxh.classpy.wasm.values.Byte; 8 | 9 | public class Global extends WasmBinPart { 10 | 11 | { 12 | add("type", new GlobalType()); 13 | add("init", new Expr()); 14 | } 15 | 16 | @Override 17 | protected void postRead(WasmBinFile wasm) { 18 | GlobalType gt = (GlobalType) get("type"); 19 | Byte valType = (Byte) gt.getParts().get(1); 20 | String mut = gt.getParts().get(0).getDesc(); 21 | String desc = mut + (valType.getValue() == 0 ? "/const" : "/var"); 22 | setDesc(desc); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/sections/Import.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.wasm.sections; 2 | 3 | import com.github.zxh.classpy.common.ParseException; 4 | import com.github.zxh.classpy.wasm.WasmBinPart; 5 | import com.github.zxh.classpy.wasm.WasmBinFile; 6 | import com.github.zxh.classpy.wasm.WasmBinReader; 7 | import com.github.zxh.classpy.wasm.types.FuncType; 8 | import com.github.zxh.classpy.wasm.types.GlobalType; 9 | import com.github.zxh.classpy.wasm.types.Limits; 10 | import com.github.zxh.classpy.wasm.types.TableType; 11 | 12 | public class Import extends WasmBinPart { 13 | 14 | private Desc desc; 15 | 16 | public boolean isFunc() { 17 | return desc.funcTypeIdx >= 0; 18 | } 19 | 20 | public boolean isGlobal() { 21 | return desc.tag == 0x03; 22 | } 23 | 24 | @Override 25 | protected void readContent(WasmBinReader reader) { 26 | String module = readName(reader, "module"); 27 | String name = readName(reader, "name"); 28 | desc = read(reader, "desc", new Desc()); 29 | setDesc(module + "." + name); 30 | if (desc.tag == 0) { // func 31 | setDesc(getDesc() + "()"); 32 | } 33 | } 34 | 35 | @Override 36 | protected void postRead(WasmBinFile wasm) { 37 | if (desc.funcTypeIdx >= 0) { 38 | FuncType funcType = wasm.getFuncTypes().get(desc.funcTypeIdx); 39 | setDesc(getDesc().replace("()", funcType.getDesc())); 40 | } 41 | } 42 | 43 | 44 | private static class Desc extends WasmBinPart { 45 | 46 | private int tag; 47 | private int funcTypeIdx = -1; 48 | 49 | @Override 50 | protected void readContent(WasmBinReader reader) { 51 | tag = readByte(reader, null); 52 | switch (tag) { 53 | case 0x00 -> funcTypeIdx = readIndex(reader, "type"); // typeidx 54 | case 0x01 -> read(reader, "table", new TableType()); // tabletype 55 | case 0x02 -> read(reader, "mem", new Limits()); // memtype 56 | case 0x03 -> read(reader, "global", new GlobalType()); // globaltype 57 | default -> throw new ParseException("Invalid import desc: " + tag); 58 | } 59 | } 60 | 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/types/BlockType.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.wasm.types; 2 | 3 | import com.github.zxh.classpy.common.ParseException; 4 | import com.github.zxh.classpy.wasm.WasmBinPart; 5 | import com.github.zxh.classpy.wasm.WasmBinReader; 6 | 7 | public class BlockType extends WasmBinPart { 8 | 9 | @Override 10 | protected void readContent(WasmBinReader reader) { 11 | byte valType = reader.readByte(); 12 | switch (valType) { 13 | case 0x40 -> setDesc(""); 14 | case 0x7F -> setDesc("i32"); 15 | case 0x7E -> setDesc("i64"); 16 | case 0x7D -> setDesc("f32"); 17 | case 0x7C -> setDesc("f64"); 18 | default -> throw new ParseException( 19 | String.format("Invalid block type: 0x%02X", valType)); 20 | } 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/types/FuncType.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.wasm.types; 2 | 3 | import com.github.zxh.classpy.common.FilePart; 4 | import com.github.zxh.classpy.wasm.WasmBinPart; 5 | import com.github.zxh.classpy.wasm.WasmBinFile; 6 | 7 | import java.util.stream.Collectors; 8 | 9 | public class FuncType extends WasmBinPart { 10 | 11 | { 12 | _byte(null, (byte) 0x60); 13 | vector("parameters", ValType::new); 14 | vector("results", ValType::new); 15 | } 16 | 17 | @Override 18 | protected void postRead(WasmBinFile wasm) { 19 | String params = get("parameters").getParts().stream() 20 | .skip(1) 21 | .map(FilePart::getDesc) 22 | .collect(Collectors.joining(",", "(", ")")); 23 | String results = get("results").getParts().stream() 24 | .skip(1) 25 | .map(FilePart::getDesc) 26 | .collect(Collectors.joining(",", "(", ")")); 27 | setDesc(params + "->" + results); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/types/GlobalType.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.wasm.types; 2 | 3 | import com.github.zxh.classpy.wasm.WasmBinPart; 4 | import com.github.zxh.classpy.wasm.WasmBinFile; 5 | import com.github.zxh.classpy.wasm.values.Byte; 6 | 7 | public class GlobalType extends WasmBinPart { 8 | 9 | { 10 | valType("valtype"); 11 | _byte("mut", (byte) 0x00, (byte) 0x01); 12 | } 13 | 14 | @Override 15 | protected void postRead(WasmBinFile wasm) { 16 | Byte mut = (Byte) get("mut"); 17 | mut.setDesc(mut.getValue() == 0 ? "const" : "var"); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/types/Limits.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.wasm.types; 2 | 3 | import com.github.zxh.classpy.wasm.WasmBinPart; 4 | import com.github.zxh.classpy.wasm.WasmBinReader; 5 | 6 | public class Limits extends WasmBinPart { 7 | 8 | @Override 9 | protected void readContent(WasmBinReader reader) { 10 | int flag = readByte(reader, "flag", (byte) 0, (byte) 1); 11 | 12 | int min = readU32(reader, "min"); 13 | setDesc("{" + min + ", }"); 14 | 15 | if (flag == 1) { 16 | int max = readU32(reader, "max"); 17 | setDesc("{" + min + ", " + max + "}"); 18 | } 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/types/TableType.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.wasm.types; 2 | 3 | import com.github.zxh.classpy.wasm.WasmBinPart; 4 | 5 | public class TableType extends WasmBinPart { 6 | 7 | { 8 | _byte("elemtype", (byte) 0x70); 9 | add("limits", new Limits()); 10 | setName("table"); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/types/ValType.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.wasm.types; 2 | 3 | import com.github.zxh.classpy.common.ParseException; 4 | import com.github.zxh.classpy.wasm.WasmBinPart; 5 | import com.github.zxh.classpy.wasm.WasmBinReader; 6 | 7 | public class ValType extends WasmBinPart { 8 | 9 | @Override 10 | protected void readContent(WasmBinReader reader) { 11 | byte b = reader.readByte(); 12 | switch (b) { 13 | case 0x7F -> setDesc("i32"); 14 | case 0x7E -> setDesc("i64"); 15 | case 0x7D -> setDesc("f32"); 16 | case 0x7C -> setDesc("f64"); 17 | default -> throw new ParseException( 18 | String.format("Invalid value type: 0x%02X", b)); 19 | } 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/values/Byte.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.wasm.values; 2 | 3 | import com.github.zxh.classpy.common.ParseException; 4 | import com.github.zxh.classpy.wasm.WasmBinPart; 5 | import com.github.zxh.classpy.wasm.WasmBinReader; 6 | 7 | public class Byte extends WasmBinPart { 8 | 9 | private final byte[] expectedValues; 10 | private byte value; 11 | 12 | public Byte() { 13 | this.expectedValues = null; 14 | } 15 | 16 | public Byte(byte... expectedValues) { 17 | this.expectedValues = expectedValues; 18 | } 19 | 20 | public int getValue() { 21 | return value & 0xFF; 22 | } 23 | 24 | @Override 25 | protected void readContent(WasmBinReader reader) { 26 | value = reader.readByte(); 27 | setDesc(String.format("0x%02X", value)); 28 | checkValue(); 29 | } 30 | 31 | private void checkValue() { 32 | if (expectedValues == null || expectedValues.length == 0) { 33 | return; 34 | } 35 | 36 | for (byte expectedValue : expectedValues) { 37 | if (expectedValue == value) { 38 | return; 39 | } 40 | } 41 | 42 | throw new ParseException(String.format( 43 | "Unexpected byte: 0x%02X", value)); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/values/Bytes.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.wasm.values; 2 | 3 | import com.github.zxh.classpy.wasm.WasmBinPart; 4 | import com.github.zxh.classpy.wasm.WasmBinReader; 5 | 6 | public class Bytes extends WasmBinPart { 7 | 8 | private final int n; 9 | private byte[] bytes; 10 | 11 | public Bytes(int n) { 12 | this.n = n; 13 | } 14 | 15 | public byte[] getBytes() { 16 | return bytes; 17 | } 18 | 19 | @Override 20 | protected void readContent(WasmBinReader reader) { 21 | bytes = reader.readBytes(n); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/values/Index.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.wasm.values; 2 | 3 | import com.github.zxh.classpy.wasm.WasmBinReader; 4 | 5 | public class Index extends U32 { 6 | 7 | @Override 8 | protected void readContent(WasmBinReader reader) { 9 | value = reader.readU32(); 10 | setDesc("#" + value); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/values/Name.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.wasm.values; 2 | 3 | import com.github.zxh.classpy.wasm.WasmBinPart; 4 | import com.github.zxh.classpy.wasm.WasmBinReader; 5 | 6 | import java.nio.charset.StandardCharsets; 7 | 8 | public class Name extends WasmBinPart { 9 | 10 | @Override 11 | protected void readContent(WasmBinReader reader) { 12 | int length = readU32(reader, "length"); 13 | byte[] bytes = readBytes(reader, "bytes", length); 14 | setDesc(new String(bytes, StandardCharsets.UTF_8)); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/values/S32.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.wasm.values; 2 | 3 | import com.github.zxh.classpy.wasm.WasmBinPart; 4 | import com.github.zxh.classpy.wasm.WasmBinReader; 5 | 6 | public class S32 extends WasmBinPart { 7 | 8 | private long value; 9 | 10 | public long getValue() { 11 | return value; 12 | } 13 | 14 | public int getIntValue() { 15 | return (int) value; 16 | } 17 | 18 | @Override 19 | protected void readContent(WasmBinReader reader) { 20 | value = reader.readS32(); 21 | setDesc(Long.toString(value)); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/values/S64.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.wasm.values; 2 | 3 | import com.github.zxh.classpy.wasm.WasmBinPart; 4 | import com.github.zxh.classpy.wasm.WasmBinReader; 5 | 6 | public class S64 extends WasmBinPart { 7 | 8 | private long value; 9 | 10 | public long getValue() { 11 | return value; 12 | } 13 | 14 | public int getIntValue() { 15 | return (int) value; 16 | } 17 | 18 | @Override 19 | protected void readContent(WasmBinReader reader) { 20 | value = reader.readS64(); 21 | setDesc(Long.toString(value)); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /classpy-wasm/src/main/java/com/github/zxh/classpy/wasm/values/U32.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.wasm.values; 2 | 3 | import com.github.zxh.classpy.wasm.WasmBinPart; 4 | import com.github.zxh.classpy.wasm.WasmBinReader; 5 | 6 | public class U32 extends WasmBinPart { 7 | 8 | protected long value; 9 | 10 | public long getValue() { 11 | return value; 12 | } 13 | 14 | public int getIntValue() { 15 | return (int) value; 16 | } 17 | 18 | @Override 19 | protected void readContent(WasmBinReader reader) { 20 | value = reader.readU32(); 21 | setDesc(Long.toString(value)); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /classpy-wasm/src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | module classpy.wasm { 2 | exports com.github.zxh.classpy.wasm; 3 | requires classpy.common; 4 | } 5 | -------------------------------------------------------------------------------- /classpy-wasm/src/test/java/com/github/zxh/classpy/wasm/FuncTest.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.wasm; 2 | 3 | import com.github.zxh.classpy.common.FilePart; 4 | import com.github.zxh.classpy.wasm.instructions.Expr; 5 | import com.github.zxh.classpy.wasm.instructions.Instr; 6 | import com.github.zxh.classpy.wasm.sections.Code; 7 | import org.junit.Test; 8 | 9 | import java.util.Base64; 10 | 11 | public class FuncTest { 12 | 13 | @Test 14 | public void read() { 15 | String base64 = "AQJ/IAIEfwNAIAAsAAAiAyABLAAAIgRGBEAgAEEBaiEAIAFBAWohAUEAIAJBf2oiAkUNAxoMAQsLIANB/wFxIARB/wFxawVBAAsPCw=="; 16 | byte[] code = Base64.getDecoder().decode(base64); 17 | Code.Func func = new Code.Func(); 18 | try { 19 | func.read(new WasmBinReader(code)); 20 | } catch (Exception e) { 21 | new ExprPrinter().printExpr((Expr) func.getParts().get(1)); 22 | throw e; 23 | } 24 | } 25 | 26 | 27 | private static class ExprPrinter { 28 | 29 | private int indentation; 30 | 31 | private void printExpr(Expr expr) { 32 | for (FilePart fc : expr.getParts()) { 33 | printInstr((Instr) fc); 34 | } 35 | } 36 | 37 | private void printInstr(Instr instr) { 38 | if (instr.getOpcode() == 0x0B 39 | || instr.getOpcode() == 0x05) { 40 | indentation -= 1; 41 | } 42 | for (int i = 0; i < indentation; i++) { 43 | System.out.print("\t"); 44 | } 45 | System.out.println(instr.getName()); 46 | if (instr.getOpcode() == 0x02 47 | || instr.getOpcode() == 0x03 48 | || instr.getOpcode() == 0x04) { 49 | indentation += 1; 50 | } 51 | for (FilePart fc : instr.getParts()) { 52 | if (fc instanceof Instr) { 53 | printInstr((Instr) fc); 54 | } 55 | } 56 | } 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /classpy-wasm/src/test/java/com/github/zxh/classpy/wasm/LEB128Test.java: -------------------------------------------------------------------------------- 1 | package com.github.zxh.classpy.wasm; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | 6 | public class LEB128Test { 7 | 8 | @Test 9 | public void readU32() { 10 | Assert.assertEquals(624485, 11 | new WasmBinReader(new byte[] {(byte) 0xE5, (byte) 0x8E, 0x26}).readU32()); 12 | } 13 | 14 | @Test 15 | public void readS32() { 16 | Assert.assertEquals(-624485, 17 | new WasmBinReader(new byte[] {(byte) 0x9B, (byte) 0xF1, 0x59}).readS32()); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zxh0/classpy/c907b08082ac9b6b1e8eaa2fbf9f35320a7d241a/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zxh0/classpy/c907b08082ac9b6b1e8eaa2fbf9f35320a7d241a/screenshot.png -------------------------------------------------------------------------------- /screenshot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zxh0/classpy/c907b08082ac9b6b1e8eaa2fbf9f35320a7d241a/screenshot2.png -------------------------------------------------------------------------------- /screenshot3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zxh0/classpy/c907b08082ac9b6b1e8eaa2fbf9f35320a7d241a/screenshot3.png -------------------------------------------------------------------------------- /screenshot4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zxh0/classpy/c907b08082ac9b6b1e8eaa2fbf9f35320a7d241a/screenshot4.png -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'classpy' 2 | 3 | include 'classpy-common' 4 | include 'classpy-classfile' 5 | include 'classpy-binarychunk' 6 | include 'classpy-bitcoin' 7 | include 'classpy-wasm' 8 | include 'classpy-gui' 9 | --------------------------------------------------------------------------------