├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── classfile ├── attr_code.go ├── attr_line_number_table.go ├── attribute_info.go ├── class_file.go ├── class_reader.go ├── constant_pool.go ├── cp_class.go ├── cp_fieldref.go ├── cp_interface_methodref.go ├── cp_invoke_dynamic.go ├── cp_methodref.go ├── cp_name_and_type.go ├── cp_numeric.go ├── cp_string.go ├── cp_utf8.go └── member_info.go ├── main.go ├── snippet └── main.go └── test ├── Sample.bytecode ├── Sample.class └── Sample.java /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | .idea 26 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.8.3 5 | env: 6 | 7 | before_install: 8 | - ls 9 | install: 10 | - cd $GOPATH/src/github.com/tinycedar/classp 11 | - go build && ./classp 12 | before_script: 13 | 14 | after_script: 15 | 16 | script: -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # classp [![Build Status](https://travis-ci.org/tinycedar/classp.svg?branch=master)](https://travis-ci.org/tinycedar/classp) 2 | Java class file parser plays the same role as "javap" command 3 | -------------------------------------------------------------------------------- /classfile/attr_code.go: -------------------------------------------------------------------------------- 1 | package classfile 2 | 3 | import "fmt" 4 | 5 | /* 6 | Code_attribute { 7 | u2 attribute_name_index; 8 | u4 attribute_length; 9 | u2 max_stack; 10 | u2 max_locals; 11 | u4 code_length; 12 | u1 code[code_length]; 13 | u2 exception_table_length; 14 | { u2 start_pc; 15 | u2 end_pc; 16 | u2 handler_pc; 17 | u2 catch_type; 18 | } exception_table[exception_table_length]; 19 | u2 attributes_count; 20 | attribute_info attributes[attributes_count]; 21 | } 22 | */ 23 | type CodeAttribute struct { 24 | cp ConstantPool 25 | MaxStack uint16 26 | MaxLocals uint16 27 | Code []uint8 //u4 code_length 28 | Exceptions []exceptionTable //u2 exception_table_length 29 | Attributes []AttributeInfo //u2 attributes_count 30 | } 31 | 32 | func (this *CodeAttribute) ReadInfo(reader *ClassReader) { 33 | this.MaxStack = reader.ReadUint16() 34 | this.MaxLocals = reader.ReadUint16() 35 | this.Code = reader.ReadBytes(int(reader.ReadUint32())) 36 | //parseCode(this.Code) 37 | exceptionTableLength := reader.ReadUint16() 38 | this.Exceptions = make([]exceptionTable, exceptionTableLength) 39 | for i := uint16(0); i < exceptionTableLength; i++ { 40 | exceptionTable := exceptionTable{} 41 | exceptionTable.ReadInfo(reader) 42 | this.Exceptions[i] = exceptionTable 43 | } 44 | this.Attributes = readAttributes(reader, this.cp) 45 | } 46 | 47 | func parseCode(code []uint8) { 48 | fmt.Printf("code: %v\n", code) 49 | for _, c := range code { 50 | switch c { 51 | case 0x2a: 52 | fmt.Printf("%v, ", "aload_0") 53 | case 0x2b: 54 | fmt.Printf("%v, ", "aload_1") 55 | case 0xb7: 56 | fmt.Printf("%v, ", "invokespecial") 57 | case 0x0: 58 | fmt.Printf("%v, ", "nop") 59 | case 0x1: 60 | fmt.Printf("%v, ", "aconst_null") 61 | case 0x2: 62 | fmt.Printf("%v, ", "iconst_m1") 63 | case 0xb1: 64 | fmt.Printf("%v, ", "return") 65 | case 176: 66 | fmt.Printf("%v, ", "areturn") 67 | case 180: 68 | fmt.Printf("%v, ", "getfield") 69 | case 181: 70 | fmt.Printf("%v, ", "putfield") 71 | default: 72 | fmt.Printf("%v, ", "invalid opcode") 73 | } 74 | } 75 | fmt.Println() 76 | } 77 | 78 | type exceptionTable struct { 79 | startPc uint16 80 | endPc uint16 81 | handlerPc uint16 82 | catchType uint16 83 | } 84 | 85 | func (this *exceptionTable) ReadInfo(reader *ClassReader) { 86 | this.startPc = reader.ReadUint16() 87 | this.endPc = reader.ReadUint16() 88 | this.handlerPc = reader.ReadUint16() 89 | this.catchType = reader.ReadUint16() 90 | } 91 | -------------------------------------------------------------------------------- /classfile/attr_line_number_table.go: -------------------------------------------------------------------------------- 1 | package classfile 2 | 3 | /* 4 | LineNumberTable_attribute { 5 | u2 attribute_name_index; 6 | u4 attribute_length; 7 | u2 line_number_table_length; 8 | { u2 start_pc; 9 | u2 line_number; 10 | } line_number_table[line_number_table_length]; 11 | } 12 | */ 13 | type LineNumberTableAttribute struct { 14 | lineNumberTables []lineNumberTable 15 | } 16 | 17 | type lineNumberTable struct { 18 | startPc uint16 19 | lineNumber uint16 20 | } 21 | 22 | func (this LineNumberTableAttribute) ReadInfo(reader *ClassReader) { 23 | lineNumberTableLength := reader.ReadUint16() 24 | this.lineNumberTables = make([]lineNumberTable, lineNumberTableLength) 25 | for i := uint16(0); i < lineNumberTableLength; i++ { 26 | this.lineNumberTables[i] = lineNumberTable{reader.ReadUint16(), reader.ReadUint16()} 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /classfile/attribute_info.go: -------------------------------------------------------------------------------- 1 | package classfile 2 | 3 | /* 4 | attribute_info { 5 | u2 attribute_name_index; 6 | u4 attribute_length; 7 | u1 info[attribute_length]; 8 | } 9 | */ 10 | type AttributeInfo interface { 11 | ReadInfo(reader *ClassReader) 12 | } 13 | 14 | func readAttributes(reader *ClassReader, cp ConstantPool) []AttributeInfo { 15 | attributes := make([]AttributeInfo, reader.ReadUint16()) 16 | for i := 0; i < len(attributes); i++ { 17 | attributes[i] = readAttributeInfo(reader, cp) 18 | } 19 | return attributes 20 | } 21 | 22 | func readAttributeInfo(reader *ClassReader, cp ConstantPool) AttributeInfo { 23 | attrNameIndex := reader.ReadUint16() 24 | attrLength := reader.ReadUint32() 25 | if c, ok := cp.GetConstantInfo(attrNameIndex).(*ConstantUtf8Info); ok { 26 | var attrInfo AttributeInfo 27 | switch attrName := c.String(); attrName { 28 | case "Code": 29 | attrInfo = &CodeAttribute{cp: cp} 30 | case "LineNumberTable": 31 | attrInfo = &LineNumberTableAttribute{} 32 | default: 33 | //TODO not implemented yet, just discard the bytes read 34 | reader.ReadBytes(int(attrLength)) 35 | } 36 | if attrInfo != nil { 37 | attrInfo.ReadInfo(reader) 38 | return attrInfo 39 | } 40 | } 41 | return nil 42 | } 43 | -------------------------------------------------------------------------------- /classfile/class_file.go: -------------------------------------------------------------------------------- 1 | package classfile 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | /* 8 | ClassFile { 9 | u4 magic; 10 | u2 minor_version; 11 | u2 major_version; 12 | u2 constant_pool_count; 13 | cp_info constant_pool[constant_pool_count-1]; 14 | u2 access_flags; 15 | u2 this_class; 16 | u2 super_class; 17 | u2 interfaces_count; 18 | u2 interfaces[interfaces_count]; 19 | u2 fields_count; 20 | field_info fields[fields_count]; 21 | u2 methods_count; 22 | method_info methods[methods_count]; 23 | u2 attributes_count; 24 | attribute_info attributes[attributes_count]; 25 | } 26 | */ 27 | type ClassFile struct { 28 | size int 29 | magic uint32 30 | minorVersion uint16 31 | majorVersion uint16 32 | constantPool ConstantPool 33 | accessFlags uint16 34 | thisClass uint16 35 | superClass uint16 36 | interfaces []uint16 37 | fields []MemberInfo 38 | methods []MemberInfo 39 | attributes []AttributeInfo 40 | } 41 | 42 | func Parse(bytes []byte) *ClassFile { 43 | reader := NewClassReader(bytes) 44 | cf := &ClassFile{} 45 | cf.size = reader.Length() 46 | cf.magic = reader.ReadUint32() 47 | cf.minorVersion = reader.ReadUint16() 48 | cf.majorVersion = reader.ReadUint16() 49 | cf.constantPool = readConstantPool(reader) 50 | cf.accessFlags = reader.ReadUint16() 51 | cf.thisClass = reader.ReadUint16() 52 | cf.superClass = reader.ReadUint16() 53 | cf.readInterfaces(reader) 54 | //TODO from now on, we can speed up by run concurrently 55 | cf.fields = readMembers(reader, cf.constantPool) 56 | cf.methods = readMembers(reader, cf.constantPool) 57 | cf.attributes = readAttributes(reader, cf.constantPool) 58 | return cf 59 | } 60 | 61 | func (cf *ClassFile) readInterfaces(reader *ClassReader) { 62 | cf.interfaces = make([]uint16, reader.ReadUint16()) 63 | for i := 0; i < len(cf.interfaces); i++ { 64 | cf.interfaces[i] = reader.ReadUint16() 65 | } 66 | } 67 | 68 | func (cf *ClassFile) Methods() []MemberInfo { 69 | return cf.methods 70 | } 71 | 72 | func (cf *ClassFile) Print() { 73 | fmt.Printf("Size: %d bytes\n", cf.size) 74 | fmt.Printf("magic: %x\n", cf.magic) 75 | fmt.Printf("minor version: %d\n", cf.minorVersion) 76 | fmt.Printf("major version: %d\n", cf.majorVersion) 77 | 78 | fmt.Printf("accessFlags: %d\n", cf.accessFlags) 79 | fmt.Printf("thisClass: #%d\n", cf.thisClass) 80 | fmt.Printf("superClass: #%d\n", cf.superClass) 81 | 82 | fmt.Println("**********************************************************") 83 | for i, length := 1, len(cf.constantPool); i < length; i++ { 84 | fmt.Printf(" #%2d = ", i) 85 | //fmt.Println(cf.constantPool[i]) 86 | if cp, ok := cf.constantPool[i].(*ConstantClassInfo); ok { 87 | fmt.Printf("Class\t\t#%d\t\t\t// %s", cp.nameIndex, cp.String(cf.constantPool)) 88 | } else if cp, ok := cf.constantPool[i].(*ConstantFieldrefInfo); ok { 89 | fmt.Printf("Fieldref\t\t#%d.#%d\t\t\t// %s", cp.classIndex, cp.nameAndTypeIndex, cp.String(cf.constantPool)) 90 | } else if cp, ok := cf.constantPool[i].(*ConstantMethodrefInfo); ok { 91 | fmt.Printf("Methodref\t#%d.#%d\t\t\t// %s", cp.classIndex, cp.nameAndTypeIndex, cp) 92 | } else if cp, ok := cf.constantPool[i].(*ConstantUtf8Info); ok { 93 | fmt.Printf("Utf8\t\t%s", cp.String()) 94 | } else if cp, ok := cf.constantPool[i].(*ConstantStringInfo); ok { 95 | fmt.Printf("String\t\t#%d\t\t\t// %s", cp.stringIndex, cp.String(cf.constantPool)) 96 | } else if cp, ok := cf.constantPool[i].(*ConstantNameAndTypeInfo); ok { 97 | fmt.Printf("NameAndType\t#%d:#%d\t\t\t// %s", cp.nameIndex, cp.descriptorIndex, cp.String(cf.constantPool)) 98 | } 99 | fmt.Println() 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /classfile/class_reader.go: -------------------------------------------------------------------------------- 1 | package classfile 2 | 3 | import ( 4 | "encoding/binary" 5 | ) 6 | 7 | var bigEndian = binary.BigEndian 8 | 9 | type ClassReader struct { 10 | bytecode []byte 11 | } 12 | 13 | func NewClassReader(bytecode []byte) *ClassReader { 14 | return &ClassReader{bytecode: bytecode} 15 | } 16 | 17 | func (cr *ClassReader) ReadUint32() uint32 { 18 | value := bigEndian.Uint32(cr.bytecode[:4]) 19 | cr.bytecode = cr.bytecode[4:] 20 | return value 21 | } 22 | 23 | func (cr *ClassReader) ReadUint16() uint16 { 24 | value := bigEndian.Uint16(cr.bytecode[:2]) 25 | cr.bytecode = cr.bytecode[2:] 26 | return value 27 | } 28 | 29 | func (cr *ClassReader) ReadUint8() uint8 { 30 | return uint8(cr.ReadBytes(1)[0]) 31 | } 32 | 33 | func (cr *ClassReader) ReadBytes(len int) []byte { 34 | bytes := cr.bytecode[:len] 35 | cr.bytecode = cr.bytecode[len:] 36 | return bytes 37 | } 38 | 39 | func (cr *ClassReader) Length() int { 40 | return len(cr.bytecode) 41 | } 42 | -------------------------------------------------------------------------------- /classfile/constant_pool.go: -------------------------------------------------------------------------------- 1 | package classfile 2 | 3 | const ( 4 | CONSTANT_Class = 7 5 | CONSTANT_Fieldref = 9 6 | CONSTANT_Methodref = 10 7 | CONSTANT_InterfaceMethodref = 11 8 | CONSTANT_String = 8 9 | CONSTANT_Integer = 3 10 | CONSTANT_Float = 4 11 | CONSTANT_Long = 5 12 | CONSTANT_Double = 6 13 | CONSTANT_NameAndType = 12 14 | CONSTANT_Utf8 = 1 15 | CONSTANT_MethodHandle = 15 16 | CONSTANT_MethodType = 16 17 | CONSTANT_InvokeDynamic = 18 18 | ) 19 | 20 | type ConstantPool []ConstantPoolInfo 21 | 22 | type ConstantPoolInfo interface { 23 | ReadInfo(reader *ClassReader) 24 | } 25 | 26 | func (p ConstantPool) GetConstantInfo(index uint16) ConstantPoolInfo { 27 | return p[index] 28 | } 29 | 30 | func (self ConstantPool) getNameAndType(index uint16) (string, string) { 31 | ntInfo := self.GetConstantInfo(index).(*ConstantNameAndTypeInfo) 32 | name := self.getUtf8(ntInfo.nameIndex) 33 | _type := self.getUtf8(ntInfo.descriptorIndex) 34 | return name, _type 35 | } 36 | 37 | func (self ConstantPool) getClassName(index uint16) string { 38 | classInfo := self.GetConstantInfo(index).(*ConstantClassInfo) 39 | return self.getUtf8(classInfo.nameIndex) 40 | } 41 | 42 | func (self ConstantPool) getUtf8(index uint16) string { 43 | utf8Info := self.GetConstantInfo(index).(*ConstantUtf8Info) 44 | return utf8Info.String() 45 | } 46 | 47 | func readConstantPool(reader *ClassReader) ConstantPool { 48 | constantPool := make([]ConstantPoolInfo, reader.ReadUint16()) 49 | for i := 1; i < len(constantPool); i++ { 50 | cpInfo := newConstantPoolInfo(reader.ReadUint8(), constantPool) 51 | if cpInfo != nil { 52 | cpInfo.ReadInfo(reader) 53 | constantPool[i] = cpInfo 54 | } 55 | } 56 | return constantPool 57 | } 58 | 59 | func newConstantPoolInfo(constType uint8, cp ConstantPool) ConstantPoolInfo { 60 | switch constType { 61 | case CONSTANT_Class: 62 | return &ConstantClassInfo{} 63 | case CONSTANT_Fieldref: 64 | return &ConstantFieldrefInfo{} 65 | case CONSTANT_Methodref: 66 | return &ConstantMethodrefInfo{cp: cp} 67 | case CONSTANT_InterfaceMethodref: 68 | return &ConstantInterfaceMethodrefInfo{} 69 | case CONSTANT_String: 70 | return &ConstantStringInfo{} 71 | case CONSTANT_Integer: 72 | return &ConstantIntegerInfo{} 73 | case CONSTANT_Float: 74 | return &ConstantFloatInfo{} 75 | case CONSTANT_Long: 76 | return &ConstantLongInfo{} 77 | case CONSTANT_Double: 78 | return &ConstantDoubleInfo{} 79 | case CONSTANT_NameAndType: 80 | return &ConstantNameAndTypeInfo{} 81 | case CONSTANT_Utf8: 82 | return &ConstantUtf8Info{} 83 | case CONSTANT_MethodHandle: 84 | return &ConstantMethodHandleInfo{} 85 | case CONSTANT_MethodType: 86 | return &ConstantMethodTypeInfo{} 87 | case CONSTANT_InvokeDynamic: 88 | return &ConstantInvokeDynamicInfo{} 89 | default: 90 | panic("Invalid const type: ") 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /classfile/cp_class.go: -------------------------------------------------------------------------------- 1 | package classfile 2 | 3 | import "fmt" 4 | 5 | type ConstantClassInfo struct { 6 | nameIndex uint16 7 | } 8 | 9 | func (this *ConstantClassInfo) ReadInfo(reader *ClassReader) { 10 | this.nameIndex = reader.ReadUint16() 11 | //fmt.Printf("Class\t\t#%d\n", this.nameIndex) 12 | } 13 | 14 | func (this ConstantClassInfo) String(constantPool ConstantPool) string { 15 | return fmt.Sprint(constantPool[this.nameIndex]) 16 | } 17 | -------------------------------------------------------------------------------- /classfile/cp_fieldref.go: -------------------------------------------------------------------------------- 1 | package classfile 2 | 3 | import "fmt" 4 | 5 | type ConstantFieldrefInfo struct { 6 | classIndex uint16 7 | nameAndTypeIndex uint16 8 | } 9 | 10 | func (this *ConstantFieldrefInfo) ReadInfo(reader *ClassReader) { 11 | this.classIndex = reader.ReadUint16() 12 | this.nameAndTypeIndex = reader.ReadUint16() 13 | //fmt.Printf("Fieldref\t\t#%d.#%d\n", this.classIndex, this.nameAndTypeIndex) 14 | } 15 | 16 | func (this ConstantFieldrefInfo) String(constantPool ConstantPool) string { 17 | class, _ := constantPool[this.classIndex].(*ConstantClassInfo) 18 | nameAndType, _ := constantPool[this.nameAndTypeIndex].(*ConstantNameAndTypeInfo) 19 | return fmt.Sprintf("%s.%s", class.String(constantPool), nameAndType.String(constantPool)) 20 | } 21 | -------------------------------------------------------------------------------- /classfile/cp_interface_methodref.go: -------------------------------------------------------------------------------- 1 | package classfile 2 | 3 | type ConstantInterfaceMethodrefInfo struct { 4 | classIndex uint16 5 | nameAndTypeIndex uint16 6 | } 7 | 8 | func (this *ConstantInterfaceMethodrefInfo) ReadInfo(reader *ClassReader) { 9 | this.classIndex = reader.ReadUint16() 10 | this.nameAndTypeIndex = reader.ReadUint16() 11 | //fmt.Printf("InterfaceMethodref\t\t#%d.#%d\n", this.classIndex, this.nameAndTypeIndex) 12 | } 13 | -------------------------------------------------------------------------------- /classfile/cp_invoke_dynamic.go: -------------------------------------------------------------------------------- 1 | package classfile 2 | 3 | type ConstantMethodHandleInfo struct { 4 | referenceKind uint8 5 | referenceIndex uint16 6 | } 7 | 8 | func (this *ConstantMethodHandleInfo) ReadInfo(reader *ClassReader) { 9 | this.referenceKind = reader.ReadBytes(1)[0] 10 | this.referenceIndex = reader.ReadUint16() 11 | //fmt.Printf("MethodHandle\t\t%s%s\n", this.referenceKind, this.referenceIndex) 12 | } 13 | 14 | type ConstantMethodTypeInfo struct { 15 | descriptorIndex uint16 16 | } 17 | 18 | func (this *ConstantMethodTypeInfo) ReadInfo(reader *ClassReader) { 19 | this.descriptorIndex = reader.ReadUint16() 20 | //fmt.Printf("MethodType\t%s\n", this.descriptorIndex) 21 | } 22 | 23 | type ConstantInvokeDynamicInfo struct { 24 | bootstrapMethodAttrIndex uint16 25 | nameAndTypeIndex uint16 26 | } 27 | 28 | func (this *ConstantInvokeDynamicInfo) ReadInfo(reader *ClassReader) { 29 | this.bootstrapMethodAttrIndex = reader.ReadUint16() 30 | this.nameAndTypeIndex = reader.ReadUint16() 31 | //fmt.Printf("InvokeDynamic\t\t%s%s\n", this.bootstrapMethodAttrIndex, this.nameAndTypeIndex) 32 | } 33 | -------------------------------------------------------------------------------- /classfile/cp_methodref.go: -------------------------------------------------------------------------------- 1 | package classfile 2 | 3 | import "fmt" 4 | 5 | type ConstantMethodrefInfo struct { 6 | cp ConstantPool 7 | classIndex uint16 8 | nameAndTypeIndex uint16 9 | } 10 | 11 | func (this *ConstantMethodrefInfo) ReadInfo(reader *ClassReader) { 12 | this.classIndex = reader.ReadUint16() 13 | this.nameAndTypeIndex = reader.ReadUint16() 14 | //fmt.Printf("Methodref\t#%d.#%d\n", this.classIndex, this.nameAndTypeIndex) 15 | } 16 | 17 | func (this *ConstantMethodrefInfo) String() string { 18 | class, _ := this.cp[this.classIndex].(*ConstantClassInfo) 19 | nameAndType, _ := this.cp[this.nameAndTypeIndex].(*ConstantNameAndTypeInfo) 20 | return fmt.Sprintf("%s.%s", class.String(this.cp), nameAndType.String(this.cp)) 21 | } 22 | 23 | func (self *ConstantMethodrefInfo) ClassName() string { 24 | return self.cp.getClassName(self.classIndex) 25 | } 26 | func (self *ConstantMethodrefInfo) NameAndDescriptor() (string, string) { 27 | return self.cp.getNameAndType(self.nameAndTypeIndex) 28 | } 29 | 30 | func (self *ConstantMethodrefInfo) ConstantPool() ConstantPool { 31 | return self.cp 32 | } -------------------------------------------------------------------------------- /classfile/cp_name_and_type.go: -------------------------------------------------------------------------------- 1 | package classfile 2 | 3 | import "fmt" 4 | 5 | type ConstantNameAndTypeInfo struct { 6 | nameIndex uint16 7 | descriptorIndex uint16 8 | } 9 | 10 | func (this *ConstantNameAndTypeInfo) ReadInfo(reader *ClassReader) { 11 | this.nameIndex = reader.ReadUint16() 12 | this.descriptorIndex = reader.ReadUint16() 13 | //fmt.Printf("NameAndType\t#%d:#%d\n", this.nameIndex, this.descriptorIndex) 14 | } 15 | 16 | func (this ConstantNameAndTypeInfo) String(constantPool ConstantPool) string { 17 | return fmt.Sprintf("%s:%s", constantPool[this.nameIndex], constantPool[this.descriptorIndex]) 18 | } 19 | -------------------------------------------------------------------------------- /classfile/cp_numeric.go: -------------------------------------------------------------------------------- 1 | package classfile 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | ) 7 | 8 | type ConstantIntegerInfo struct { 9 | bytes uint32 10 | } 11 | 12 | func (this *ConstantIntegerInfo) ReadInfo(reader *ClassReader) { 13 | this.bytes = reader.ReadUint32() 14 | //fmt.Printf("Integer\t\t%s\n", this.bytes) 15 | } 16 | 17 | type ConstantFloatInfo struct { 18 | bytes uint32 19 | } 20 | 21 | func (this *ConstantFloatInfo) ReadInfo(reader *ClassReader) { 22 | this.bytes = reader.ReadUint32() 23 | //fmt.Printf("Float\t\t%s\n", this.bytes) 24 | } 25 | 26 | func (this *ConstantFloatInfo) Value() float32 { 27 | return math.Float32frombits(this.bytes) 28 | } 29 | 30 | type ConstantLongInfo struct { 31 | highBytes uint32 32 | lowBytes uint32 33 | } 34 | 35 | func (this *ConstantLongInfo) ReadInfo(reader *ClassReader) { 36 | this.highBytes = reader.ReadUint32() 37 | this.lowBytes = reader.ReadUint32() 38 | fmt.Printf("Long\t\t%s%s\n", this.highBytes, this.lowBytes) 39 | } 40 | 41 | type ConstantDoubleInfo struct { 42 | highBytes uint32 43 | lowBytes uint32 44 | } 45 | 46 | func (this *ConstantDoubleInfo) ReadInfo(reader *ClassReader) { 47 | this.highBytes = reader.ReadUint32() 48 | this.lowBytes = reader.ReadUint32() 49 | //fmt.Printf("Double\t\t%s%s\n", this.highBytes, this.lowBytes) 50 | } 51 | -------------------------------------------------------------------------------- /classfile/cp_string.go: -------------------------------------------------------------------------------- 1 | package classfile 2 | 3 | type ConstantStringInfo struct { 4 | stringIndex uint16 5 | } 6 | 7 | func (this *ConstantStringInfo) ReadInfo(reader *ClassReader) { 8 | this.stringIndex = reader.ReadUint16() 9 | //fmt.Printf("String\t\t#%d\n", this.stringIndex) 10 | } 11 | 12 | func (this *ConstantStringInfo) String(constantPool ConstantPool) string { 13 | if cp, ok := constantPool[this.stringIndex].(*ConstantUtf8Info); ok { 14 | return cp.String() 15 | } 16 | return "" 17 | } 18 | -------------------------------------------------------------------------------- /classfile/cp_utf8.go: -------------------------------------------------------------------------------- 1 | package classfile 2 | 3 | import "fmt" 4 | 5 | type ConstantUtf8Info struct { 6 | bytes []byte //u2 length 7 | } 8 | 9 | func (this *ConstantUtf8Info) ReadInfo(reader *ClassReader) { 10 | this.bytes = reader.ReadBytes(int(reader.ReadUint16())) 11 | //fmt.Printf("Utf8\t\t%s\n", this.bytes) 12 | } 13 | 14 | func (this ConstantUtf8Info) String() string { 15 | return fmt.Sprintf("%s", this.bytes) 16 | } 17 | -------------------------------------------------------------------------------- /classfile/member_info.go: -------------------------------------------------------------------------------- 1 | package classfile 2 | 3 | /* 4 | field/method_info { 5 | u2 access_flags; 6 | u2 name_index; 7 | u2 descriptor_index; 8 | u2 attributes_count; 9 | attribute_info attributes[attributes_count]; 10 | } 11 | */ 12 | type MemberInfo struct { 13 | cp ConstantPool 14 | accessFlags uint16 15 | nameIndex uint16 16 | descriptorIndex uint16 17 | attributes []AttributeInfo 18 | } 19 | 20 | func readMembers(reader *ClassReader, cp ConstantPool) []MemberInfo { 21 | members := make([]MemberInfo, reader.ReadUint16()) 22 | for i := 0; i < len(members); i++ { 23 | members[i] = MemberInfo{ 24 | cp, 25 | reader.ReadUint16(), 26 | reader.ReadUint16(), 27 | reader.ReadUint16(), 28 | readAttributes(reader, cp)} 29 | } 30 | return members 31 | } 32 | 33 | func (self *MemberInfo) AccessFlags() uint16 { 34 | return self.accessFlags 35 | } 36 | 37 | func (m *MemberInfo) Name() string { 38 | name := m.cp[m.nameIndex] 39 | if name, ok := name.(*ConstantUtf8Info); ok { 40 | return name.String() 41 | } 42 | return "" 43 | } 44 | 45 | func (m *MemberInfo) Descriptor() string { 46 | desc := m.cp[m.descriptorIndex] 47 | if desc, ok := desc.(*ConstantUtf8Info); ok { 48 | return desc.String() 49 | } 50 | return "" 51 | } 52 | 53 | func (m *MemberInfo) ConstantPool() ConstantPool { 54 | return m.cp 55 | } 56 | 57 | func (m *MemberInfo) CodeAttribute() *CodeAttribute { 58 | for _, attr := range m.attributes { 59 | if code, ok := attr.(*CodeAttribute); ok { 60 | return code 61 | } 62 | } 63 | return nil 64 | } 65 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/tinycedar/classp/classfile" 5 | "io/ioutil" 6 | "log" 7 | ) 8 | 9 | func main() { 10 | bytes, err := ioutil.ReadFile("test/Sample.class") 11 | if err != nil { 12 | log.Fatal("Error reading class file") 13 | } 14 | 15 | classfile.Parse(bytes).Print() 16 | } 17 | -------------------------------------------------------------------------------- /snippet/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func main() { 8 | s := []string{"1", "2"} 9 | fmt.Println(s) 10 | 11 | for i := range s { 12 | fmt.Println(i) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /test/Sample.bytecode: -------------------------------------------------------------------------------- 1 | Classfile /Users/daniel/go/src/github.com/tinycedar/class-parser/test/Sample.class 2 | Last modified Jun 8, 2017; size 877 bytes 3 | MD5 checksum 9f5ad585aeec00d459cf4a12ee7413b7 4 | Compiled from "Sample.java" 5 | public class Sample implements java.lang.Runnable 6 | minor version: 0 7 | major version: 52 8 | flags: ACC_PUBLIC, ACC_SUPER 9 | Constant pool: 10 | #1 = Methodref #16.#33 // java/lang/Object."":()V 11 | #2 = Fieldref #3.#34 // Sample.name:Ljava/lang/String; 12 | #3 = Class #35 // Sample 13 | #4 = Methodref #3.#33 // Sample."":()V 14 | #5 = String #36 // Daniel 15 | #6 = Methodref #3.#37 // Sample.setName:(Ljava/lang/String;)V 16 | #7 = Fieldref #38.#39 // java/lang/System.out:Ljava/io/PrintStream; 17 | #8 = Class #40 // java/lang/StringBuilder 18 | #9 = Methodref #8.#33 // java/lang/StringBuilder."":()V 19 | #10 = String #41 // Hello 20 | #11 = Methodref #8.#42 // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 21 | #12 = Methodref #3.#43 // Sample.getName:()Ljava/lang/String; 22 | #13 = String #44 // ! 23 | #14 = Methodref #8.#45 // java/lang/StringBuilder.toString:()Ljava/lang/String; 24 | #15 = Methodref #46.#47 // java/io/PrintStream.println:(Ljava/lang/String;)V 25 | #16 = Class #48 // java/lang/Object 26 | #17 = Class #49 // java/lang/Runnable 27 | #18 = Utf8 name 28 | #19 = Utf8 Ljava/lang/String; 29 | #20 = Utf8 30 | #21 = Utf8 ()V 31 | #22 = Utf8 Code 32 | #23 = Utf8 LineNumberTable 33 | #24 = Utf8 getName 34 | #25 = Utf8 ()Ljava/lang/String; 35 | #26 = Utf8 setName 36 | #27 = Utf8 (Ljava/lang/String;)V 37 | #28 = Utf8 run 38 | #29 = Utf8 main 39 | #30 = Utf8 ([Ljava/lang/String;)V 40 | #31 = Utf8 SourceFile 41 | #32 = Utf8 Sample.java 42 | #33 = NameAndType #20:#21 // "":()V 43 | #34 = NameAndType #18:#19 // name:Ljava/lang/String; 44 | #35 = Utf8 Sample 45 | #36 = Utf8 Daniel 46 | #37 = NameAndType #26:#27 // setName:(Ljava/lang/String;)V 47 | #38 = Class #50 // java/lang/System 48 | #39 = NameAndType #51:#52 // out:Ljava/io/PrintStream; 49 | #40 = Utf8 java/lang/StringBuilder 50 | #41 = Utf8 Hello 51 | #42 = NameAndType #53:#54 // append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 52 | #43 = NameAndType #24:#25 // getName:()Ljava/lang/String; 53 | #44 = Utf8 ! 54 | #45 = NameAndType #55:#25 // toString:()Ljava/lang/String; 55 | #46 = Class #56 // java/io/PrintStream 56 | #47 = NameAndType #57:#27 // println:(Ljava/lang/String;)V 57 | #48 = Utf8 java/lang/Object 58 | #49 = Utf8 java/lang/Runnable 59 | #50 = Utf8 java/lang/System 60 | #51 = Utf8 out 61 | #52 = Utf8 Ljava/io/PrintStream; 62 | #53 = Utf8 append 63 | #54 = Utf8 (Ljava/lang/String;)Ljava/lang/StringBuilder; 64 | #55 = Utf8 toString 65 | #56 = Utf8 java/io/PrintStream 66 | #57 = Utf8 println 67 | { 68 | public Sample(); 69 | descriptor: ()V 70 | flags: ACC_PUBLIC 71 | Code: 72 | stack=1, locals=1, args_size=1 73 | 0: aload_0 74 | 1: invokespecial #1 // Method java/lang/Object."":()V 75 | 4: return 76 | LineNumberTable: 77 | line 1: 0 78 | 79 | public java.lang.String getName(); 80 | descriptor: ()Ljava/lang/String; 81 | flags: ACC_PUBLIC 82 | Code: 83 | stack=1, locals=1, args_size=1 84 | 0: aload_0 85 | 1: getfield #2 // Field name:Ljava/lang/String; 86 | 4: areturn 87 | LineNumberTable: 88 | line 5: 0 89 | 90 | public void setName(java.lang.String); 91 | descriptor: (Ljava/lang/String;)V 92 | flags: ACC_PUBLIC 93 | Code: 94 | stack=2, locals=2, args_size=2 95 | 0: aload_0 96 | 1: aload_1 97 | 2: putfield #2 // Field name:Ljava/lang/String; 98 | 5: return 99 | LineNumberTable: 100 | line 9: 0 101 | line 10: 5 102 | 103 | public void run(); 104 | descriptor: ()V 105 | flags: ACC_PUBLIC 106 | Code: 107 | stack=0, locals=1, args_size=1 108 | 0: return 109 | LineNumberTable: 110 | line 14: 0 111 | 112 | public static void main(java.lang.String[]); 113 | descriptor: ([Ljava/lang/String;)V 114 | flags: ACC_PUBLIC, ACC_STATIC 115 | Code: 116 | stack=3, locals=2, args_size=1 117 | 0: new #3 // class Sample 118 | 3: dup 119 | 4: invokespecial #4 // Method "":()V 120 | 7: astore_1 121 | 8: aload_1 122 | 9: ldc #5 // String Daniel 123 | 11: invokevirtual #6 // Method setName:(Ljava/lang/String;)V 124 | 14: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream; 125 | 17: new #8 // class java/lang/StringBuilder 126 | 20: dup 127 | 21: invokespecial #9 // Method java/lang/StringBuilder."":()V 128 | 24: ldc #10 // String Hello 129 | 26: invokevirtual #11 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 130 | 29: aload_1 131 | 30: invokevirtual #12 // Method getName:()Ljava/lang/String; 132 | 33: invokevirtual #11 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 133 | 36: ldc #13 // String ! 134 | 38: invokevirtual #11 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 135 | 41: invokevirtual #14 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 136 | 44: invokevirtual #15 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 137 | 47: return 138 | LineNumberTable: 139 | line 17: 0 140 | line 18: 8 141 | line 19: 14 142 | line 20: 47 143 | } 144 | SourceFile: "Sample.java" -------------------------------------------------------------------------------- /test/Sample.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinycedar/classp/258751fffce45231b9d2c3ef122bf479af353a0c/test/Sample.class -------------------------------------------------------------------------------- /test/Sample.java: -------------------------------------------------------------------------------- 1 | public class Sample implements Runnable{ 2 | private String name; 3 | 4 | public String getName(){ 5 | return this.name; 6 | } 7 | 8 | public void setName(String name){ 9 | this.name = name; 10 | } 11 | 12 | public void run(){ 13 | 14 | } 15 | 16 | public static void main(String[] args){ 17 | Sample sample = new Sample(); 18 | sample.setName("Daniel"); 19 | System.out.println("Hello " + sample.getName() + " !"); 20 | } 21 | } 22 | --------------------------------------------------------------------------------