diff --git a/ExampleClass.class b/ExampleClass.class new file mode 100644 index 0000000..adf0486 Binary files /dev/null and b/ExampleClass.class differ diff --git a/ExampleClass.java b/ExampleClass.java new file mode 100644 index 0000000..a926261 --- /dev/null +++ b/ExampleClass.java @@ -0,0 +1,11 @@ +import java.util.function.Consumer; + +final class ExampleClass implements Consumer { + public int toInt() { + return 1; + } + + public void accept(String t) { + + } +} diff --git a/asm/annotation-visitor.go b/asm/annotation-visitor.go new file mode 100644 index 0000000..bbae2fb --- /dev/null +++ b/asm/annotation-visitor.go @@ -0,0 +1,12 @@ +package asm + +// AnnotationVisitor a visitor to visit a Java annotation. The methods of this class must be called in the following +// order: ( visit | visitEnum | visitAnnotation | visitArray )* +// visitEnd. +type AnnotationVisitor interface { + visit(name string, value interface{}) + visitEnum(name, descriptor, value string) + visitAnnotation(name, descriptor string) AnnotationVisitor + visitArray(name string) AnnotationVisitor + visitEnd() +} diff --git a/asm/attribute.go b/asm/attribute.go new file mode 100644 index 0000000..f167bc8 --- /dev/null +++ b/asm/attribute.go @@ -0,0 +1,85 @@ +package asm + +type Attribute struct { + typed string + content []byte + nextAttribute *Attribute +} + +func NewAttribute(typed string) *Attribute { + return &Attribute{ + typed: typed, + } +} + +func (a Attribute) isUnknow() bool { + return true +} + +func (a Attribute) isCodeAttribute() bool { + return false +} + +func (a Attribute) getLabels() []Label { + return nil +} + +func (a Attribute) read(classReader *ClassReader, offset int, length int, charBuffer []rune, codeAttributeOffset int, labels []*Label) *Attribute { + attribute := NewAttribute(a.typed) + attribute.content = make([]byte, length) + //System.arraycopy(classReader.b, offset, attribute.content, 0, length) + return attribute +} + +//ClassWriter +func (a Attribute) write(classWriter interface{}, code []byte, codeLength int, maxStack int, maxLocals int) { + //return new ByteVector(content) +} + +func (a Attribute) getAttributeCount() int { + count := 0 + attribute := &a + for attribute != nil { + count++ + attribute = attribute.nextAttribute + } + return count +} + +func (a Attribute) computeAttributesSize(symbolTable interface{}) int { + codeLength := 0 + maxStack := -1 + maxLocals := -1 + return a._computeAttributesSize(symbolTable, nil, codeLength, maxStack, maxLocals) +} + +func (a Attribute) _computeAttributesSize(symbolTable interface{}, code []byte, codeLength int, maxStack int, maxLocals int) int { + //ClassWriter classWrite = symbolTable.classWriter + size := 0 + attribute := &a + for attribute != nil { + //symbolTable.addConstantUtf8(attribute.typed) + //size += 6 + attribute.write(classWriter, code, codeLength, maxStack, maxLocals).length + attribute = attribute.nextAttribute + } + return size +} + +//SymbolTable, ByteVector +func (a Attribute) putAttribute(symbolTable interface{}, output interface{}) { + codeLength := 0 + maxStack := -1 + maxLocals := -1 + a._putAttribute(symbolTable, nil, codeLength, maxStack, maxLocals, output) +} + +func (a Attribute) _putAttribute(symbolTable interface{}, code []byte, codeLength int, maxStack int, maxLocals int, output interface{}) { + //ClassWriter classWrite = symbolTable.classWriter + attribute := &a + for attribute != nil { + //ByteVector attributeContent = attribute.write(classWriter, code, codeLength, maxStack, maxLocals) + //output.putShort(symbolTable.addConstantUtf8(attribute.typed)).putInt(attributeContent.length) + //output.putByteArray(attributeContent.data, 0, attributeContent.length) + attribute = attribute.nextAttribute + } +} diff --git a/asm/class-reader.go b/asm/class-reader.go new file mode 100644 index 0000000..bbe327a --- /dev/null +++ b/asm/class-reader.go @@ -0,0 +1,636 @@ +package asm + +import ( + "errors" + "fmt" + + "github.com/leaklessgfy/asm/asm/opcodes" + "github.com/leaklessgfy/asm/asm/symbol" +) + +// ClassReader A parser to make a {@link ClassVisitor} visit a ClassFile structure, as defined in the Java +// Virtual Machine Specification (JVMS). This class parses the ClassFile content and calls the +// appropriate visit methods of a given {@link ClassVisitor} for each field, method and bytecode +// instruction encountered. +type ClassReader struct { + b []byte + cpInfoOffsets []int + constantUtf8Values []string + maxStringLength int + header int +} + +// SKIP_CODE a flag to skip the Code attributes. If this flag is set the Code attributes are neither parsed nor visited. +const SKIP_CODE = 1 + +// SKIP_DEBUG a flag to skip the SourceFile, SourceDebugExtension, LocalVariableTable, LocalVariableTypeTable +// and LineNumberTable attributes. If this flag is set these attributes are neither parsed nor +// visited (i.e. {@link ClassVisitor#visitSource}, {@link MethodVisitor#visitLocalVariable} and +// {@link MethodVisitor#visitLineNumber} are not called). +const SKIP_DEBUG = 2 + +// SKIP_FRAMES a flag to skip the StackMap and StackMapTable attributes. If this flag is set these attributes +// are neither parsed nor visited (i.e. {@link MethodVisitor#visitFrame} is not called). This flag +// is useful when the {@link ClassWriter#COMPUTE_FRAMES} option is used: it avoids visiting frames +// that will be ignored and recomputed from scratch. +const SKIP_FRAMES = 4 + +// EXPAND_FRAMS a flag to expand the stack map frames. By default stack map frames are visited in their +// original format (i.e. "expanded" for classes whose version is less than V1_6, and "compressed" +// for the other classes). If this flag is set, stack map frames are always visited in expanded +// format (this option adds a decompression/compression step in ClassReader and ClassWriter which +// degrades performance quite a lot). +const EXPAND_FRAMS = 8 + +// EXPAND_ASM_INSNS A flag to expand the ASM specific instructions into an equivalent sequence of standard bytecode +// instructions. When resolving a forward jump it may happen that the signed 2 bytes offset +// reserved for it is not sufficient to store the bytecode offset. In this case the jump +// instruction is replaced with a temporary ASM specific instruction using an unsigned 2 bytes +// offset (see {@link Label#resolve}). This internal flag is used to re-read classes containing +// such instructions, in order to replace them with standard instructions. In addition, when this +// flag is used, goto_w and jsr_w are not converted into goto and jsr, to make sure that +// infinite loops where a goto_w is replaced with a goto in ClassReader and converted back to a +// goto_w in ClassWriter cannot occur. +const EXPAND_ASM_INSNS = 256 + +// NewClassReader constructs a new {@link ClassReader} object. +func NewClassReader(classFile []byte) (*ClassReader, error) { + return classReader(classFile, 0, len(classFile)) +} + +func classReader(byteBuffer []byte, offset int, length int) (*ClassReader, error) { + reader := &ClassReader{ + b: byteBuffer, + } + + if reader.readShort(offset+6) > opcodes.V10 { + return nil, errors.New("Illegal Argument") + } + + constantPoolCount := reader.readUnsignedShort(offset + 8) + reader.cpInfoOffsets = make([]int, constantPoolCount) + reader.constantUtf8Values = make([]string, constantPoolCount) + currentCpInfoOffset := offset + 10 + maxStringLength := 0 + + for i := 1; i < constantPoolCount; i++ { + reader.cpInfoOffsets[i] = currentCpInfoOffset + 1 + var cpInfoSize int + + switch byteBuffer[currentCpInfoOffset] { + case byte(symbol.CONSTANT_FIELDREF_TAG), byte(symbol.CONSTANT_METHODREF_TAG), byte(symbol.CONSTANT_INTERFACE_METHODREF_TAG), + byte(symbol.CONSTANT_INTEGER_TAG), byte(symbol.CONSTANT_FLOAT_TAG), byte(symbol.CONSTANT_NAME_AND_TYPE_TAG), + byte(symbol.CONSTANT_INVOKE_DYNAMIC_TAG): + cpInfoSize = 5 + break + case byte(symbol.CONSTANT_LONG_TAG), byte(symbol.CONSTANT_DOUBLE_TAG): + cpInfoSize = 9 + i++ + break + case byte(symbol.CONSTANT_UTF8_TAG): + cpInfoSize = 3 + reader.readUnsignedShort(currentCpInfoOffset+1) + if cpInfoSize > maxStringLength { + maxStringLength = cpInfoSize + } + break + case byte(symbol.CONSTANT_METHOD_HANDLE_TAG): + cpInfoSize = 4 + break + case byte(symbol.CONSTANT_CLASS_TAG), byte(symbol.CONSTANT_STRING_TAG), byte(symbol.CONSTANT_METHOD_TYPE_TAG), + byte(symbol.CONSTANT_PACKAGE_TAG), byte(symbol.CONSTANT_MODULE_TAG): + cpInfoSize = 3 + break + default: + return nil, errors.New("Assertion Error") + } + currentCpInfoOffset += cpInfoSize + } + + reader.maxStringLength = maxStringLength + reader.header = currentCpInfoOffset + + return reader, nil +} + +// ----------------------------------------------------------------------------------------------- +// Accessors +// ----------------------------------------------------------------------------------------------- + +// GetAccess returns the class's access flags (see {@link Opcodes}). This value may not reflect Deprecated +// and Synthetic flags when bytecode is before 1.5 and those flags are represented by attributes. +func (c *ClassReader) GetAccess() int { + return c.readUnsignedShort(c.header) +} + +// GetClassName returns the internal name of the class (see {@link Type#getInternalName()}). +func (c *ClassReader) GetClassName() string { + charBuffer := make([]rune, c.maxStringLength) + return c.readClass(c.header+2, charBuffer) +} + +// GetSuperName returns the internal of name of the super class (see {@link Type#getInternalName()}). For +// interfaces, the super class is {@link Object}. +func (c *ClassReader) GetSuperName() string { + charBuffer := make([]rune, c.maxStringLength) + return c.readClass(c.header+4, charBuffer) +} + +// GetInterfaces returns the internal names of the implemented interfaces (see {@link Type#getInternalName()}). +func (c ClassReader) GetInterfaces() []string { + currentOffset := c.header + 6 + interfacesCount := c.readUnsignedShort(currentOffset) + interfaces := make([]string, interfacesCount) + if interfacesCount > 0 { + charBuffer := make([]rune, c.maxStringLength) + for i := 0; i < interfacesCount; i++ { + currentOffset += 2 + interfaces[i] = c.readClass(currentOffset, charBuffer) + } + } + return interfaces +} + +// ----------------------------------------------------------------------------------------------- +// Public methods +// ----------------------------------------------------------------------------------------------- + +// Accept Makes the given visitor visit the JVMS ClassFile structure passed to the constructor of this {@link ClassReader}. +func (c ClassReader) Accept(classVisitor ClassVisitor, parsingOptions int) { + c.AcceptB(classVisitor, make([]Attribute, 0), parsingOptions) +} + +// AcceptB Makes the given visitor visit the JVMS ClassFile structure passed to the constructor of this {@link ClassReader}. +func (c ClassReader) AcceptB(classVisitor ClassVisitor, attributePrototypes []Attribute, parsingOptions int) { + context := &Context{ + attributePrototypes: attributePrototypes, + parsingOptions: parsingOptions, + charBuffer: make([]rune, c.maxStringLength), + } + + charBuffer := context.charBuffer + currentOffset := c.header + accessFlags := c.readUnsignedShort(currentOffset) + thisClass := c.readClass(currentOffset+2, charBuffer) + superClass := c.readClass(currentOffset+4, charBuffer) + interfaces := make([]string, c.readUnsignedShort(currentOffset+6)) + currentOffset += 8 + + for i := 0; i < len(interfaces); i++ { + interfaces[i] = c.readClass(currentOffset, charBuffer) + currentOffset += 2 + } + + innerClassesOffset := 0 + enclosingMethodOffset := 0 + signature := "" + sourceFile := "" + sourceDebugExtension := "" + runtimeVisibleAnnotationsOffset := 0 + runtimeInvisibleAnnotationsOffset := 0 + runtimeVisibleTypeAnnotationsOffset := 0 + runtimeInvisibleTypeAnnotationsOffset := 0 + moduleOffset := 0 + modulePackagesOffset := 0 + moduleMainClass := "" + var attributes *Attribute + + currentAttributeOffset := c.getFirstAttributeOffset() + for i := c.readUnsignedShort(currentAttributeOffset - 2); i > 0; i-- { + attributeName := c.readUTF8(currentAttributeOffset, charBuffer) + attributeLength := c.readInt(currentAttributeOffset + 2) + currentAttributeOffset += 6 + + switch attributeName { + case "SourceFile": + sourceFile = c.readUTF8(currentAttributeOffset, charBuffer) + break + case "InnerClasses": + innerClassesOffset = currentAttributeOffset + break + case "EnclosingMethod": + enclosingMethodOffset = currentAttributeOffset + break + case "Signature": + signature = c.readUTF8(currentAttributeOffset, charBuffer) + break + case "RuntimeVisibleAnnotations": + runtimeVisibleAnnotationsOffset = currentAttributeOffset + break + case "RuntimeVisibleTypeAnnotations": + runtimeVisibleTypeAnnotationsOffset = currentAttributeOffset + break + case "Deprecated": + accessFlags |= opcodes.ACC_DEPRECATED + break + case "Synthetic": + accessFlags |= opcodes.ACC_SYNTHETIC + break + case "SourceDebugExtension": + sourceDebugExtension = c.readUTFB(currentAttributeOffset, attributeLength, make([]rune, attributeLength)) + break + case "RuntimeInvisibleAnnotations": + runtimeInvisibleAnnotationsOffset = currentAttributeOffset + break + case "RuntimeInvisibleTypeAnnotations": + runtimeInvisibleTypeAnnotationsOffset = currentAttributeOffset + break + case "Module": + moduleOffset = currentAttributeOffset + break + case "ModuleMainClass": + moduleMainClass = c.readClass(currentAttributeOffset, charBuffer) + break + case "ModulePackages": + modulePackagesOffset = currentAttributeOffset + break + case "BootstrapMethods": + bootstrapMethodOffsets := make([]int, c.readUnsignedShort(currentAttributeOffset)) + currentBootstrapMethodOffset := currentAttributeOffset + 2 + for j := 0; j < len(bootstrapMethodOffsets); j++ { + bootstrapMethodOffsets[j] = currentBootstrapMethodOffset + currentBootstrapMethodOffset += 4 + c.readUnsignedShort(currentBootstrapMethodOffset+2)*2 + } + context.bootstrapMethodOffsets = bootstrapMethodOffsets + break + default: + attribute := c.readAttribute(attributePrototypes, attributeName, currentAttributeOffset, attributeLength, charBuffer, -1, nil) + attribute.nextAttribute = attributes + attributes = attribute + } + currentAttributeOffset += attributeLength + } + + classVisitor.Visit(c.readInt(c.cpInfoOffsets[1]-7), accessFlags, thisClass, signature, superClass, interfaces) + + if (parsingOptions&SKIP_DEBUG) == 0 && (sourceFile != "" || sourceDebugExtension != "") { + classVisitor.VisitSource(sourceFile, sourceDebugExtension) + } + + if moduleOffset != 0 { + c.readModule(classVisitor, context, moduleOffset, modulePackagesOffset, moduleMainClass) + } + + if enclosingMethodOffset != 0 { + className := c.readClass(enclosingMethodOffset, charBuffer) + methodIndex := c.readUnsignedShort(enclosingMethodOffset + 2) + var name string + var typed string + if methodIndex != 0 { + name = c.readUTF8(c.cpInfoOffsets[methodIndex], charBuffer) + typed = c.readUTF8(c.cpInfoOffsets[methodIndex]+2, charBuffer) + } + classVisitor.VisitOuterClass(className, name, typed) + } + + if runtimeVisibleAnnotationsOffset != 0 { + numAnnotations := c.readUnsignedShort(runtimeVisibleAnnotationsOffset) + currentAnnotationOffset := runtimeVisibleAnnotationsOffset + 2 + for numAnnotations > 0 { + annotationDescriptor := c.readUTF8(currentAnnotationOffset, charBuffer) + currentAnnotationOffset += 2 + currentAnnotationOffset = c.readElementValues(classVisitor.VisitAnnotation(annotationDescriptor, true), currentAnnotationOffset, true, charBuffer) + numAnnotations-- + } + } + + if runtimeInvisibleAnnotationsOffset != 0 { + numAnnotations := c.readUnsignedShort(runtimeInvisibleAnnotationsOffset) + currentAnnotationOffset := runtimeInvisibleAnnotationsOffset + 2 + for numAnnotations > 0 { + annotationDescriptor := c.readUTF8(currentAnnotationOffset, charBuffer) + currentAnnotationOffset += 2 + currentAnnotationOffset = c.readElementValues(classVisitor.VisitAnnotation(annotationDescriptor, false), currentAnnotationOffset, true, charBuffer) + numAnnotations-- + } + } + + if runtimeVisibleTypeAnnotationsOffset != 0 { + numAnnotations := c.readUnsignedShort(runtimeInvisibleAnnotationsOffset) + currentAnnotationOffset := runtimeInvisibleAnnotationsOffset + 2 + for numAnnotations > 0 { + annotationDescriptor := c.readUTF8(currentAnnotationOffset, charBuffer) + currentAnnotationOffset += 2 + currentAnnotationOffset = c.readElementValues(classVisitor.VisitAnnotation(annotationDescriptor, false), currentAnnotationOffset, true, charBuffer) + numAnnotations-- + } + } + + if runtimeInvisibleTypeAnnotationsOffset != 0 { + numAnnotations := c.readUnsignedShort(runtimeInvisibleTypeAnnotationsOffset) + currentAnnotationOffset := runtimeInvisibleTypeAnnotationsOffset + 2 + for numAnnotations > 0 { + currentAnnotationOffset = c.readTypeAnnotationTarget(context, currentAnnotationOffset) + annotationDescriptor := c.readUTF8(currentAnnotationOffset, charBuffer) + currentAnnotationOffset += 2 + currentAnnotationOffset = c.readElementValues(classVisitor.VisitTypeAnnotation(context.currentTypeAnnotationTarget, context.currentTypeAnnotationTargetPath.(int), annotationDescriptor, false), currentAnnotationOffset, true, charBuffer) + numAnnotations-- + } + } + + for attributes != nil { + nextAttribute := attributes.nextAttribute + attributes.nextAttribute = nil + classVisitor.VisitAttribute(attributes) + attributes = nextAttribute + } + + if innerClassesOffset != 0 { + numberOfClasses := c.readUnsignedShort(innerClassesOffset) + currentClassesOffset := innerClassesOffset + 2 + for numberOfClasses > 0 { + classVisitor.VisitInnerClass(c.readClass(currentClassesOffset, charBuffer), c.readClass(currentAttributeOffset+2, charBuffer), c.readClass(currentClassesOffset+4, charBuffer), c.readUnsignedShort(currentClassesOffset+6)) + currentClassesOffset += 8 + numberOfClasses-- + } + } + + fieldsCount := c.readUnsignedShort(currentOffset) + currentOffset += 2 + for fieldsCount > 0 { + currentOffset = c.readField(classVisitor, context, currentOffset) + fieldsCount-- + } + methodsCount := c.readUnsignedShort(currentOffset) + currentOffset += 2 + for methodsCount > 0 { + currentOffset = c.readMethod(classVisitor, context, currentOffset) + methodsCount-- + } + + classVisitor.VisitEnd() +} + +// ---------------------------------------------------------------------------------------------- +// Methods to parse modules, fields and methods +// ---------------------------------------------------------------------------------------------- + +func (c ClassReader) readModule(classVisitor ClassVisitor, context *Context, moduleOffset int, modulePackagesOffset int, moduleMainClass string) { + //TODO +} + +func (c ClassReader) readField(classVisitor ClassVisitor, context *Context, fieldInfoOffset int) int { + //TODO + return 0 +} + +func (c ClassReader) readMethod(classVisitor ClassVisitor, context *Context, methodInfoOffset int) int { + //TODO + return 0 +} + +// ---------------------------------------------------------------------------------------------- +// Methods to parse a Code attribute +// ---------------------------------------------------------------------------------------------- + +func (c ClassReader) readCode(methodVisitor MethodVisitor, contexnt *Context, codeOffset int) { + //TODO +} + +func (c ClassReader) readLabel(bytecodeOffset int, labels []*Label) *Label { + if labels[bytecodeOffset] == nil { + labels[bytecodeOffset] = &Label{} + } + return labels[bytecodeOffset] +} + +func (c ClassReader) createLabel(bytecodeOffset int, labels []*Label) *Label { + label := c.readLabel(bytecodeOffset, labels) + label.flags &= ^FLAG_DEBUG_ONLY + return label +} + +func (c ClassReader) createDebugLabel(bytecodeOffset int, labels []*Label) { + if labels[bytecodeOffset] == nil { + c.readLabel(bytecodeOffset, labels).flags |= FLAG_DEBUG_ONLY + } +} + +// ---------------------------------------------------------------------------------------------- +// Methods to parse annotations, type annotations and parameter annotations +// ---------------------------------------------------------------------------------------------- + +func (c ClassReader) readTypeAnnotations(methodVisitor MethodVisitor, context *Context, runtimeTypeAnnotationsOffset int, visible bool) []int { + //TODO + return nil +} + +func (c ClassReader) getTypeAnnotationBytecodeOffset(typeAnnotationOffsets []int, typeAnnotationIndex int) int { + if typeAnnotationOffsets == nil || typeAnnotationIndex >= len(typeAnnotationOffsets) || c.readByte(typeAnnotationOffsets[typeAnnotationIndex]) < INSTANCEOF { + return -1 + } + + return c.readUnsignedShort(typeAnnotationOffsets[typeAnnotationIndex] + 1) +} + +func (c ClassReader) readTypeAnnotationTarget(context *Context, typeAnnotationOffset int) int { + //TODO + return 0 +} + +func (c ClassReader) readParameterAnnotations(methodVisitor MethodVisitor, context *Context, runtimeParameterAnnotationsOffset int, visible bool) { + //TODO +} + +func (c ClassReader) readElementValues(annotationVisitor AnnotationVisitor, annotationOffset int, named bool, charBuffer []rune) int { + //TODO + return 0 +} + +func (c ClassReader) readElementValue(annotationVisitor AnnotationVisitor, elementValueOffset int, elementName string, charBuffer []rune) int { + return 0 +} + +// ---------------------------------------------------------------------------------------------- +// Methods to parse stack map frames +// ---------------------------------------------------------------------------------------------- + +func (c ClassReader) computeImplicitFame(context *Context) { + //TODO +} + +func (c ClassReader) readStackMapFrame(stackMapFrameOffset int, compressed bool, expand bool, context *Context) int { + //TODO + return 0 +} + +func (c ClassReader) readVerificationTypeInfo(verificationTypeInfoOffset int, frame []interface{}, index int, charBuffer []rune, labels []*Label) int { + //TODO + return 0 +} + +// ---------------------------------------------------------------------------------------------- +// Methods to parse attributes +// ---------------------------------------------------------------------------------------------- + +func (c ClassReader) getFirstAttributeOffset() int { + currentOffset := c.header + 8 + c.readUnsignedShort(c.header+6)*2 + fieldsCount := c.readUnsignedShort(currentOffset) + currentOffset += 2 + for fieldsCount > 0 { + attributesCount := c.readUnsignedShort(currentOffset + 6) + currentOffset += 8 + for attributesCount > 0 { + currentOffset += 6 + c.readInt(currentOffset+2) + attributesCount-- + } + fieldsCount-- + } + + methodsCount := c.readUnsignedShort(currentOffset) + currentOffset += 2 + for methodsCount > 0 { + attributesCount := c.readUnsignedShort(currentOffset + 6) + currentOffset += 8 + for attributesCount > 0 { + currentOffset += 6 + c.readInt(currentOffset+2) + attributesCount-- + } + methodsCount-- + } + + return currentOffset + 2 +} + +func (c ClassReader) readAttribute(attributePrototypes []Attribute, typed string, offset int, length int, charBuffer []rune, codeAttributeOffset int, labels []*Label) *Attribute { + for i := 0; i < len(attributePrototypes); i++ { + if attributePrototypes[i].typed == typed { + return attributePrototypes[i].read(&c, offset, length, charBuffer, codeAttributeOffset, labels) + } + } + return NewAttribute(typed).read(&c, offset, length, nil, -1, nil) +} + +// ----------------------------------------------------------------------------------------------- +// Utility methods: low level parsing +// ----------------------------------------------------------------------------------------------- + +func (c ClassReader) getItemCount() int { + return len(c.cpInfoOffsets) +} + +func (c ClassReader) getItem(constantPoolEntryIndex int) int { + return c.cpInfoOffsets[constantPoolEntryIndex] +} + +func (c ClassReader) getMaxStringLength() int { + return c.maxStringLength +} + +func (c ClassReader) readByte(offset int) byte { + return c.b[offset] & 0xFF +} + +func (c ClassReader) readUnsignedShort(offset int) int { + b := c.b + return int(((b[offset] & 0xFF) << 8) | (b[offset+1] & 0xFF)) +} + +func (c ClassReader) readShort(offset int) int16 { + b := c.b + return int16((((b[offset] & 0xFF) << 8) | (b[offset+1] & 0xFF))) +} + +func (c ClassReader) readInt(offset int) int { + b := c.b + return int(((b[offset] & 0xFF) << 24) | ((b[offset+1] & 0xFF) << 16) | ((b[offset+2] & 0xFF) << 8) | (b[offset+3] & 0xFF)) +} + +func (c ClassReader) readLong(offset int) int64 { + var l1 int64 + var l0 int64 + l1 = int64(c.readInt(offset)) + l0 = int64(c.readInt(offset+4) & 0xFFFFFFFF) + return (l1 << 32) | l0 +} + +func (c ClassReader) readUTF8(offset int, charBuffer []rune) string { + constantPoolEntryIndex := c.readUnsignedShort(offset) + if offset == 0 || constantPoolEntryIndex == 0 { + return "" + } + return c.readUTF(constantPoolEntryIndex, charBuffer) +} + +func (c ClassReader) readUTF(constantPoolEntryIndex int, charBuffer []rune) string { + value := c.constantUtf8Values[constantPoolEntryIndex] + if value != "" { + return value + } + cpInfoOffset := c.cpInfoOffsets[constantPoolEntryIndex] + c.constantUtf8Values[constantPoolEntryIndex] = c.readUTFB(cpInfoOffset+2, c.readUnsignedShort(cpInfoOffset), charBuffer) + + return c.constantUtf8Values[constantPoolEntryIndex] +} + +func (c ClassReader) readUTFB(utfOffset int, utfLength int, charBuffer []rune) string { + currentOffset := utfOffset + endOffset := currentOffset + utfLength + strLength := 0 + b := c.b + for currentOffset < endOffset { + currentByte := b[currentOffset] + currentOffset++ + if (currentByte & 0x80) == 0 { + charBuffer[strLength] = rune(currentByte & 0x7F) + strLength++ + } else if (currentByte & 0xE0) == 0xC0 { + charBuffer[strLength] = rune((((currentByte & 0x1F) << 6) + (b[currentOffset] & 0x3F))) + strLength++ + currentOffset++ + } else { + d := ((currentByte & 0xF) << 12) + ((b[currentOffset] & 0x3F) << 6) + currentOffset++ + charBuffer[strLength] = rune((d + (b[currentOffset] & 0x3F))) + strLength++ + } + } + return string(charBuffer) +} + +func (c ClassReader) readStringish(offset int, charBuffer []rune) string { + return c.readUTF8(c.cpInfoOffsets[c.readUnsignedShort(offset)], charBuffer) +} + +func (c ClassReader) readClass(offset int, charBuffer []rune) string { + return c.readStringish(offset, charBuffer) +} + +func (c ClassReader) readModuleB(offset int, charBuffer []rune) string { + return c.readStringish(offset, charBuffer) +} + +func (c ClassReader) readPackage(offset int, charBuffer []rune) string { + return c.readStringish(offset, charBuffer) +} + +func (c ClassReader) readConst(constantPoolEntryIndex int, charBuffer []rune) (interface{}, error) { + cpInfoOffset := c.cpInfoOffsets[constantPoolEntryIndex] + switch c.b[cpInfoOffset-1] { + case byte(symbol.CONSTANT_INTEGER_TAG): + return c.readInt(cpInfoOffset), nil + case byte(symbol.CONSTANT_FLOAT_TAG): + return float32(c.readInt(cpInfoOffset)), nil + case byte(symbol.CONSTANT_LONG_TAG): + return c.readLong(cpInfoOffset), nil + case byte(symbol.CONSTANT_DOUBLE_TAG): + return float64(c.readLong(cpInfoOffset)), nil + case byte(symbol.CONSTANT_CLASS_TAG): + return 0, nil //Type.getObjectType(c.readUTF8(cpInfoOffset, charBuffer)) + case byte(symbol.CONSTANT_STRING_TAG): + return c.readUTF8(cpInfoOffset, charBuffer), nil + case byte(symbol.CONSTANT_METHOD_TYPE_TAG): + return 0, nil //Type.getMethodType(c.readUTF8(cpInfoOffset, charBuffer)) + case byte(symbol.CONSTANT_METHOD_HANDLE_TAG): + referenceKind := c.readByte(cpInfoOffset) + referenceCpInfoOffset := c.cpInfoOffsets[c.readUnsignedShort(cpInfoOffset+1)] + nameAndTypeCpInfoOffset := c.cpInfoOffsets[c.readUnsignedShort(referenceCpInfoOffset+2)] + owner := c.readClass(referenceCpInfoOffset, charBuffer) + name := c.readUTF8(nameAndTypeCpInfoOffset, charBuffer) + desc := c.readUTF8(nameAndTypeCpInfoOffset+2, charBuffer) + itf := c.b[referenceCpInfoOffset-1] == byte(symbol.CONSTANT_INTERFACE_METHODREF_TAG) + fmt.Println(referenceKind, owner, name, desc, itf) + return 0, nil //new Handle(referenceKind, owner, name, desc, itf) + default: + return nil, errors.New("Assertion Error") + } +} diff --git a/asm/class-visitor.go b/asm/class-visitor.go new file mode 100644 index 0000000..28ba428 --- /dev/null +++ b/asm/class-visitor.go @@ -0,0 +1,19 @@ +package asm + +// ClassVisitor A visitor to visit a Java class. The methods of this class must be called in the following order: +// visit [ visitSource ] [ visitModule ][ visitOuterClass ] ( +// visitAnnotation | visitTypeAnnotation | visitAttribute )* ( +// visitInnerClass | visitField | visitMethod )* visitEnd. +type ClassVisitor interface { + Visit(version, access int, name, signature, superName string, interfaces []string) + VisitSource(source, debug string) + VisitModule(name string, access, version int) //should return modulevisitor + VisitOuterClass(owner, name, descriptor string) + VisitAnnotation(descriptor string, visible bool) AnnotationVisitor + VisitTypeAnnotation(typeRef, typePath int, descriptor string, visible bool) AnnotationVisitor //typePath : TypePath + VisitAttribute(attribute *Attribute) + VisitInnerClass(name, outerName, innerName string, access int) + VisitField(access int, name, descriptor, signature string, value interface{}) //should return FieldVisitor + VisitMethod(access int, name, descriptor, signature string, exceptions []string) MethodVisitor + VisitEnd() +} diff --git a/asm/context.go b/asm/context.go new file mode 100644 index 0000000..8fa215d --- /dev/null +++ b/asm/context.go @@ -0,0 +1,25 @@ +package asm + +// Context information about a class being parsed in a {@link ClassReader}. +type Context struct { + attributePrototypes []Attribute + parsingOptions int + charBuffer []rune + bootstrapMethodOffsets []int + currentMethodAccessFlags int + currentMethodName string + currentMethodDescriptor string + currentMethodLabels []interface{} //[]Label + currentTypeAnnotationTarget int + currentTypeAnnotationTargetPath interface{} //TypePath + currentLocalVariableAnnotationRangeStarts []interface{} //[]Label + currentLocalVariableAnnotationRangeEnds []interface{} //[]Label + currentLocalVariableAnnotationRangeIndices []int + currentFrameOffset int + currentFrameType int + currentFrameLocalCount int + currentFrameLocalCountDelta int + currentFrameLocalTypes []interface{} + currentFrameStackCount int + currentFrameStackTypes []interface{} +} diff --git a/asm/label.go b/asm/label.go new file mode 100644 index 0000000..b017700 --- /dev/null +++ b/asm/label.go @@ -0,0 +1,81 @@ +package asm + +import "errors" + +const FLAG_DEBUG_ONLY = 1 +const FLAG_JUMP_TARGET = 2 +const FLAG_RESOLVED = 4 +const FLAG_REACHABLE = 8 +const FLAG_SUBROUTINE_CALLER = 16 +const FLAG_SUBROUTINE_START = 32 +const FLAG_SUBROUTINE_BODY = 64 +const FLAG_SUBROUTINE_END = 128 +const LINE_NUMBERS_CAPACITY_INCREMENT = 4 +const VALUES_CAPACITY_INCREMENT = 6 +const FORWARD_REFERENCE_TYPE_MASK = 0xF0000000 +const FORWARD_REFERENCE_TYPE_SHORT = 0x10000000 +const FORWARD_REFERENCE_TYPE_WIDE = 0x20000000 +const FORWARD_REFERENCE_HANDLE_MASK = 0x0FFFFFFF + +var EMPTY_LIST = &Label{} + +type Label struct { + info interface{} + flags int16 + lineNumber int16 + otherLineNumbers []int + bytecodeOffset int + valueCount int16 + values []int + inputStackSize int16 + outputStackSize int16 + outputStackMax int16 + frame interface{} //Frame + nextBasicBlock *Label + outgoingEdges interface{} //Edge + nextListElement *Label +} + +func (l Label) getOffset() (int, error) { + if (l.flags & FLAG_RESOLVED) == 0 { + return 0, errors.New("Illegal State - Label offset position has not been resolved yet") + } + return l.bytecodeOffset, nil +} + +func (l Label) getCanonicalInstance() *Label { + if l.frame == nil { + return &l + } + return nil //l.frame.owner +} + +func (l *Label) addLineNumber(lineNumber int) { + if l.lineNumber == 0 { + l.lineNumber = int16(lineNumber) + } else { + if l.otherLineNumbers == nil { + l.otherLineNumbers = make([]int, LINE_NUMBERS_CAPACITY_INCREMENT) + } + otherLineNumberCount := l.otherLineNumbers[0] + l.otherLineNumbers[0]++ + if otherLineNumberCount >= len(l.otherLineNumbers) { + newLineNumbers := make([]int, len(l.otherLineNumbers)+VALUES_CAPACITY_INCREMENT) + //System.arraycopy(l.otherLineNumbers, 0, newLineNumbers, 0, len(l.otherLineNumbers)) + l.otherLineNumbers = newLineNumbers + } + l.otherLineNumbers[otherLineNumberCount] = lineNumber + } +} + +func (l Label) accept(methodVisitor MethodVisitor, visitLineNumbers bool) { + methodVisitor.visitLabel(&l) + if visitLineNumbers && l.lineNumber != 0 { + methodVisitor.visitLineNumber(int(l.lineNumber)&0xFFFF, &l) + if l.otherLineNumbers != nil { + for i := 1; i <= l.otherLineNumbers[0]; i++ { + methodVisitor.visitLineNumber(l.otherLineNumbers[i], &l) + } + } + } +} diff --git a/asm/method-visitor.go b/asm/method-visitor.go new file mode 100644 index 0000000..271fcd5 --- /dev/null +++ b/asm/method-visitor.go @@ -0,0 +1,51 @@ +package asm + +// MethodVisitor a visitor to visit a Java method. The methods of this class must be called in the following +// order: ( visitParameter )* [ visitAnnotationDefault ] ( +// visitAnnotation | visitAnnotableParameterCount | +// visitParameterAnnotation visitTypeAnnotation | visitAttribute )* [ +// visitCode ( visitFrame | visitXInsn | visitLabel | +// visitInsnAnnotation | visitTryCatchBlock | visitTryCatchAnnotation | +// visitLocalVariable | visitLocalVariableAnnotation | visitLineNumber )* +// visitMaxs ] visitEnd. In addition, the visitXInsn and +// visitLabel methods must be called in the sequential order of the bytecode instructions +// of the visited code, visitInsnAnnotation must be called after the annotated +// instruction, visitTryCatchBlock must be called before the labels passed as +// arguments have been visited, visitTryCatchBlockAnnotation must be called after +// the corresponding try catch block has been visited, and the visitLocalVariable, +// visitLocalVariableAnnotation and visitLineNumber methods must be called +// after the labels passed as arguments have been visited. +type MethodVisitor interface { + visitParameter(name string, access int) + visitAnnotationDefault() AnnotationVisitor + visitAnnotation(descriptor string, visible bool) AnnotationVisitor + visitTypeAnnotation(typeRef int, typePath interface{}, descriptor string, visible bool) AnnotationVisitor //TypePath + visitAnnotableParameterCount(parameterCount int, visible bool) + visitParameterAnnotation(parameter int, descriptor string, visible bool) AnnotationVisitor + visitAttribute(attribute *Attribute) + visitCode() + visitFrame(typed, nLocal int, local interface{}, nStack int, stack interface{}) + visitInsn(opcode int) + visitIntInsn(opcode, operand int) + visitVarInsn(opcode, vard int) + visitTypeInsn(opcode, typed int) + visitFieldInsn(opcode int, owner, name, descriptor string) + visitMethodInsn(opcode int, owner, name, descriptor string) + _visitMethodInsn(opcode int, owner, name, descriptor string, isInterface bool) + visitInvokeDynamicInsn(name, descriptor string, bootstrapMethodHande interface{}, bootstrapMethodArguments ...interface{}) //Handle + visitJumpInsn(opcode int, label *Label) + visitLabel(label *Label) + visitLdcInsn(value interface{}) + visitIincInsn(vard, increment int) + visitTableSwitchInsn(min, max int, dflt *Label, labels ...*Label) + visitLookupSwitchInsn(dflt *Label, keys []int, labels []Label) + visitMultiANewArrayInsn(descriptor string, numDimensions int) + visitInsnAnnotation(typeRef int, typePath interface{}, descriptor string, visible bool) AnnotationVisitor //TypePath + visitTryCatchBlock(start, end, handler *Label, typed string) + visitTryCatchAnnotation(typeRef int, typePath interface{}, descriptor string, visible bool) AnnotationVisitor //TypePath + visitLocalVariable(name, descriptor, signature string, start, end *Label, index int) + visitLocalVariableAnnotation(typeRef int, typePath interface{}, start, end *Label, index []int, descriptor string, visible bool) AnnotationVisitor //TypePath + visitLineNumber(line int, start *Label) + visitMaxs(maxStack int, maxLocals int) + visitEnd() +} diff --git a/asm/opcodes/opcodes.go b/asm/opcodes/opcodes.go new file mode 100644 index 0000000..82fc327 --- /dev/null +++ b/asm/opcodes/opcodes.go @@ -0,0 +1,28 @@ +package opcodes + +const V10 = 0<<16 | 54 + +const ACC_PUBLIC = 0x0001 // class, field, method +const ACC_PRIVATE = 0x0002 // class, field, method +const ACC_PROTECTED = 0x0004 // class, field, method +const ACC_STATIC = 0x0008 // field, method +const ACC_FINAL = 0x0010 // class, field, method, parameter +const ACC_SUPER = 0x0020 // class +const ACC_SYNCHRONIZED = 0x0020 // method +const ACC_OPEN = 0x0020 // module +const ACC_TRANSITIVE = 0x0020 // module requires +const ACC_VOLATILE = 0x0040 // field +const ACC_BRIDGE = 0x0040 // method +const ACC_STATIC_PHASE = 0x0040 // module requires +const ACC_VARARGS = 0x0080 // method +const ACC_TRANSIENT = 0x0080 // field +const ACC_NATIVE = 0x0100 // method +const ACC_INTERFACE = 0x0200 // class +const ACC_ABSTRACT = 0x0400 // class, method +const ACC_STRICT = 0x0800 // method +const ACC_SYNTHETIC = 0x1000 // class, field, method, parameter, module * +const ACC_ANNOTATION = 0x2000 // class +const ACC_ENUM = 0x4000 // class(?) field inner +const ACC_MANDATED = 0x8000 // parameter, module, module * +const ACC_MODULE = 0x8000 // class +const ACC_DEPRECATED = 0x20000 // class, field, method diff --git a/asm/symbol/symbol.go b/asm/symbol/symbol.go new file mode 100644 index 0000000..6d3d056 --- /dev/null +++ b/asm/symbol/symbol.go @@ -0,0 +1,26 @@ +package symbol + +// CONSTANT_CLASS_TAG The tag value of CONSTANT_Class_info JVMS structures. +var CONSTANT_CLASS_TAG = 7 + +// CONSTANT_FIELDREF_TAG The tag value of CONSTANT_Fieldref_info JVMS structures. +var CONSTANT_FIELDREF_TAG = 9 + +var CONSTANT_METHODREF_TAG = 10 +var CONSTANT_INTERFACE_METHODREF_TAG = 11 +var CONSTANT_STRING_TAG = 8 +var CONSTANT_INTEGER_TAG = 3 +var CONSTANT_FLOAT_TAG = 4 +var CONSTANT_LONG_TAG = 5 +var CONSTANT_DOUBLE_TAG = 6 +var CONSTANT_NAME_AND_TYPE_TAG = 12 +var CONSTANT_UTF8_TAG = 1 +var CONSTANT_METHOD_HANDLE_TAG = 15 +var CONSTANT_METHOD_TYPE_TAG = 16 +var CONSTANT_INVOKE_DYNAMIC_TAG = 18 +var CONSTANT_MODULE_TAG = 19 +var CONSTANT_PACKAGE_TAG = 20 +var BOOTSTRAP_METHOD_TAG = 64 +var TYPE_TAG = 128 +var UNINITIALIZED_TYPE_TAG = 129 +var MERGED_TYPE_TAG = 130 diff --git a/asm/type-reference.go b/asm/type-reference.go new file mode 100644 index 0000000..0d43ed5 --- /dev/null +++ b/asm/type-reference.go @@ -0,0 +1,109 @@ +package asm + +// CLASS_TYPE_PARAMETER the sort of type references that target a type parameter of a generic class. See {@link #getSort}. +const CLASS_TYPE_PARAMETER = 0x00 + +// METHOD_TYPE_PARAMETER the sort of type references that target a type parameter of a generic method. See {@link #getSort}. +const METHOD_TYPE_PARAMETER = 0x01 + +// CLASS_EXTENDS The sort of type references that target the super class of a class or one of the interfaces it implements. See {@link #getSort}. +const CLASS_EXTENDS = 0x10 + +// CLASS_TYPE_PARAMETER_BOUND the sort of type references that target a bound of a type parameter of a generic class. See {@link #getSort}. +const CLASS_TYPE_PARAMETER_BOUND = 0x11 + +// METHOD_TYPE_PARAMETER_BOUND the sort of type references that target a bound of a type parameter of a generic method. See {@link #getSort}. +const METHOD_TYPE_PARAMETER_BOUND = 0x12 + +// FIELD the sort of type references that target the type of a field. See {@link #getSort}. +const FIELD = 0x13 + +// METHOD_RETURN the sort of type references that target the return type of a method. See {@link #getSort}. +const METHOD_RETURN = 0x14 + +// METHOD_RECEIVER the sort of type references that target the receiver type of a method. See {@link #getSort}. +const METHOD_RECEIVER = 0x15 + +/** +* The sort of type references that target the type of a formal parameter of a method. See {@link +* #getSort}. + */ +const METHOD_FORMAL_PARAMETER = 0x16 + +/** +* The sort of type references that target the type of an exception declared in the throws clause +* of a method. See {@link #getSort}. + */ +const THROWS = 0x17 + +/** +* The sort of type references that target the type of a local variable in a method. See {@link +* #getSort}. + */ +const LOCAL_VARIABLE = 0x40 + +/** +* The sort of type references that target the type of a resource variable in a method. See {@link +* #getSort}. + */ +const RESOURCE_VARIABLE = 0x41 + +/** +* The sort of type references that target the type of the exception of a 'catch' clause in a +* method. See {@link #getSort}. + */ +const EXCEPTION_PARAMETER = 0x42 + +/** +* The sort of type references that target the type declared in an 'instanceof' instruction. See +* {@link #getSort}. + */ +const INSTANCEOF = 0x43 + +/** +* The sort of type references that target the type of the object created by a 'new' instruction. +* See {@link #getSort}. + */ +const NEW = 0x44 + +/** +* The sort of type references that target the receiver type of a constructor reference. See +* {@link #getSort}. + */ +const CONSTRUCTOR_REFERENCE = 0x45 + +/** +* The sort of type references that target the receiver type of a method reference. See {@link +* #getSort}. + */ +const METHOD_REFERENCE = 0x46 + +/** +* The sort of type references that target the type declared in an explicit or implicit cast +* instruction. See {@link #getSort}. + */ +const CAST = 0x47 + +/** +* The sort of type references that target a type parameter of a generic constructor in a +* constructor call. See {@link #getSort}. + */ +const CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT = 0x48 + +/** +* The sort of type references that target a type parameter of a generic method in a method call. +* See {@link #getSort}. + */ +const METHOD_INVOCATION_TYPE_ARGUMENT = 0x49 + +/** +* The sort of type references that target a type parameter of a generic constructor in a +* constructor reference. See {@link #getSort}. + */ +const CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT = 0x4A + +/** +* The sort of type references that target a type parameter of a generic method in a method +* reference. See {@link #getSort}. + */ +const METHOD_REFERENCE_TYPE_ARGUMENT = 0x4B diff --git a/event-visitor.go b/event-visitor.go new file mode 100644 index 0000000..4981723 --- /dev/null +++ b/event-visitor.go @@ -0,0 +1,52 @@ +package main + +import "github.com/leaklessgfy/asm/asm" + +type EventVisitor struct { + OnVisit []func(version, access int, name, signature, superName string, interfaces []string) +} + +func (e EventVisitor) Visit(version, access int, name, signature, superName string, interfaces []string) { + for _, callback := range e.OnVisit { + callback(version, access, name, signature, superName, interfaces) + } +} + +func (e EventVisitor) VisitSource(source, debug string) { + +} + +func (e EventVisitor) VisitModule(name string, access, version int) { + +} + +func (e EventVisitor) VisitOuterClass(owner, name, descriptor string) { + +} + +func (e EventVisitor) VisitAnnotation(descriptor string, visible bool) asm.AnnotationVisitor { + return nil +} + +func (e EventVisitor) VisitTypeAnnotation(typeRef, typePath int, descriptor string, visible bool) asm.AnnotationVisitor { + return nil +} + +func (e EventVisitor) VisitAttribute(attribute *asm.Attribute) { + +} + +func (e EventVisitor) VisitInnerClass(name, outerName, innerName string, access int) { + +} + +func (e EventVisitor) VisitField(access int, name, descriptor, signature string, value interface{}) { + +} + +func (e EventVisitor) VisitMethod(access int, name, descriptor, signature string, exceptions []string) asm.MethodVisitor { + return nil +} + +func (e EventVisitor) VisitEnd() { +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..bdd4196 --- /dev/null +++ b/main.go @@ -0,0 +1,36 @@ +package main + +import ( + "fmt" + "io/ioutil" + "os" + + "github.com/leaklessgfy/asm/asm" +) + +func main() { + if len(os.Args) < 2 { + fmt.Fprintln(os.Stderr, "Bad usage") + os.Exit(1) + } + + bytes, err := ioutil.ReadFile(os.Args[1]) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + + r, err := asm.NewClassReader(bytes) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + + r.Accept(&EventVisitor{ + OnVisit: []func(version, access int, name, signature, superName string, interfaces []string){ + func(version, access int, name, signature, superName string, interfaces []string) { + fmt.Println(name, signature, superName, interfaces) + }, + }, + }, 0) +}