From 1216a984a653420db500e87bccd198f7e8015d15 Mon Sep 17 00:00:00 2001 From: Vincent Rasquier Date: Thu, 14 Dec 2017 11:36:01 +0100 Subject: [PATCH] Make readField and readMethod + fix readUTFB --- README.md | 27 ++++ asm/classreader.go | 317 ++++++++++++++++++++++++++++++++++++++++++++- event-visitor.go | 58 --------- main.go | 20 +-- simplevisitor.go | 66 ++++++++++ 5 files changed, 414 insertions(+), 74 deletions(-) create mode 100644 README.md delete mode 100644 event-visitor.go create mode 100644 simplevisitor.go diff --git a/README.md b/README.md new file mode 100644 index 0000000..0651429 --- /dev/null +++ b/README.md @@ -0,0 +1,27 @@ +# ASM + +[IN PROGRESS] + +This repository is a Golang port of the famous ASM java library for JVM bytecode reading and manipulation. +http://asm.ow2.org/. You can find the original source code at this link : https://gitlab.ow2.org/asm/asm. + +## Goal +There are multiple goals for this port. The first one is comparing performance between the actual Java implementation and with this port. +Second reason could be to provide different tools for JVM manipulation through Golang, as, for example, a simple conversion of JVM Bytecode to Go. + +## Progress + +| CLASS | PROGRESS % | +| ClassReader | 60% | +| ClassVisitor | 100% | +| MethodVisitor | 100% | +| FieldVisitor | 100% | +| ModuleVisitor | 100% | +| AnnotationVisitor | 100% | +| Attribute | ? | +| Context | ? | +| Label | ? | +| TypeReference | ? | +| Opcodes | ? | +| Symbol | ? | +| TypePath | 0% | diff --git a/asm/classreader.go b/asm/classreader.go index 9eb1396..5018d63 100644 --- a/asm/classreader.go +++ b/asm/classreader.go @@ -461,20 +461,322 @@ func (c ClassReader) readModule(classVisitor ClassVisitor, context *Context, mod } func (c ClassReader) readField(classVisitor ClassVisitor, context *Context, fieldInfoOffset int) int { - //TODO - return 0 + charBuffer := context.charBuffer + currentOffset := fieldInfoOffset + accessFlags := c.readUnsignedShort(currentOffset) + name := c.readUTF8(currentOffset+2, charBuffer) + descriptor := c.readUTF8(currentOffset+4, charBuffer) + currentOffset += 6 + + var constantValue interface{} + var signature string + + runtimeVisibleAnnotationsOffset := 0 + runtimeInvisibleAnnotationsOffset := 0 + runtimeVisibleTypeAnnotationsOffset := 0 + runtimeInvisibleTypeAnnotationsOffset := 0 + var attributes *Attribute + + attributesCount := c.readUnsignedShort(currentOffset) + currentOffset += 2 + + for attributesCount > 0 { + attributeName := c.readUTF8(currentOffset, charBuffer) + attributeLength := c.readInt(currentOffset + 2) + currentOffset += 6 + + switch attributeName { + case "ConstantValue": + constantvalueIndex := c.readUnsignedShort(currentOffset) + if constantvalueIndex != 0 { + constantValue, _ = c.readConst(constantvalueIndex, charBuffer) + } + break + case "Signature": + signature = c.readUTF8(currentOffset, charBuffer) + break + case "Deprecated": + accessFlags |= opcodes.ACC_DEPRECATED + break + case "Synthetic": + accessFlags |= opcodes.ACC_SYNTHETIC + break + case "RuntimeVisibleAnnotations": + runtimeVisibleAnnotationsOffset = currentOffset + break + case "RuntimeVisibleTypeAnnotations": + runtimeVisibleTypeAnnotationsOffset = currentOffset + break + case "RuntimeInvisibleAnnotations": + runtimeInvisibleAnnotationsOffset = currentOffset + break + case "RuntimeInvisibleTypeAnnotations": + runtimeInvisibleTypeAnnotationsOffset = currentOffset + break + default: + attribute := c.readAttribute(context.attributePrototypes, attributeName, currentOffset, attributeLength, charBuffer, -1, nil) + attribute.nextAttribute = attributes + attributes = attribute + break + } + currentOffset += attributeLength + attributesCount-- + } + + fieldVisitor := classVisitor.VisitField(accessFlags, name, descriptor, signature, constantValue) + if fieldVisitor == nil { + return currentOffset + } + + if runtimeVisibleAnnotationsOffset != 0 { + numAnnotations := c.readUnsignedShort(runtimeVisibleAnnotationsOffset) + currentAnnotationOffset := runtimeVisibleAnnotationsOffset + 2 + for numAnnotations > 0 { + annotationDescriptor := c.readUTF8(currentAnnotationOffset, charBuffer) + currentAnnotationOffset += 2 + currentAnnotationOffset = c.readElementValues(fieldVisitor.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(fieldVisitor.VisitAnnotation(annotationDescriptor, false), currentAnnotationOffset, true, charBuffer) + numAnnotations-- + } + } + + if runtimeVisibleTypeAnnotationsOffset != 0 { + numAnnotations := c.readUnsignedShort(runtimeVisibleTypeAnnotationsOffset) + currentAnnotationOffset := runtimeVisibleTypeAnnotationsOffset + 2 + for numAnnotations > 0 { + currentAnnotationOffset = c.readTypeAnnotationTarget(context, currentAnnotationOffset) + annotationDescriptor := c.readUTF8(currentAnnotationOffset, charBuffer) + currentAnnotationOffset += 2 + annotationVisitor := fieldVisitor.VisitTypeAnnotation(context.currentTypeAnnotationTarget, context.currentTypeAnnotationTargetPath, annotationDescriptor, true) + currentAnnotationOffset = c.readElementValues(annotationVisitor, 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 + annotationVisitor := fieldVisitor.VisitTypeAnnotation(context.currentTypeAnnotationTarget, context.currentTypeAnnotationTargetPath, annotationDescriptor, false) + currentAnnotationOffset = c.readElementValues(annotationVisitor, currentAnnotationOffset, true, charBuffer) + numAnnotations-- + } + } + + for attributes != nil { + nextAttribute := attributes.nextAttribute + attributes.nextAttribute = nil + fieldVisitor.VisitAttribute(attributes) + attributes = nextAttribute + } + + fieldVisitor.VisitEnd() + return currentOffset } func (c ClassReader) readMethod(classVisitor ClassVisitor, context *Context, methodInfoOffset int) int { - //TODO - return 0 + charBuffer := context.charBuffer + currentOffset := methodInfoOffset + context.currentMethodAccessFlags = c.readUnsignedShort(currentOffset) + context.currentMethodName = c.readUTF8(currentOffset+2, charBuffer) + context.currentMethodDescriptor = c.readUTF8(currentOffset+4, charBuffer) + currentOffset += 6 + + codeOffset := 0 + exceptionsOffset := 0 + var exceptions []string + signature := 0 + runtimeVisibleAnnotationsOffset := 0 + runtimeInvisibleAnnotationsOffset := 0 + runtimeVisibleParameterAnnotationsOffset := 0 + runtimeInvisibleParameterAnnotationsOffset := 0 + runtimeVisibleTypeAnnotationsOffset := 0 + runtimeInvisibleTypeAnnotationsOffset := 0 + annotationDefaultOffset := 0 + methodParametersOffset := 0 + var attributes *Attribute + + attributesCount := c.readUnsignedShort(currentOffset) + currentOffset += 2 + for attributesCount > 0 { + attributeName := c.readUTF8(currentOffset, charBuffer) + attributeLength := c.readInt(currentOffset + 2) + currentOffset += 6 + + switch attributeName { + case "Code": + codeOffset = currentOffset + break + case "Exceptions": + exceptionsOffset = currentOffset + exceptions = make([]string, c.readUnsignedShort(exceptionsOffset)) + currentExceptionOffset := exceptionsOffset + 2 + for i := 0; i < len(exceptions); i++ { + exceptions[i] = c.readClass(currentExceptionOffset, charBuffer) + currentExceptionOffset += 2 + } + break + case "Signature": + signature = c.readUnsignedShort(currentOffset) + break + case "Deprecated": + context.currentMethodAccessFlags |= opcodes.ACC_DEPRECATED + break + case "RuntimeVisibleAnnotations": + runtimeVisibleAnnotationsOffset = currentOffset + break + case "RuntimeVisibleTypeAnnotations": + runtimeVisibleTypeAnnotationsOffset = currentOffset + break + case "AnnotationDefault": + annotationDefaultOffset = currentOffset + break + case "Synthetic": + context.currentMethodAccessFlags |= opcodes.ACC_SYNTHETIC + break + case "RuntimeInvisibleAnnotations": + runtimeInvisibleAnnotationsOffset = currentOffset + break + case "RuntimeInvisibleTypeAnnotations": + runtimeInvisibleTypeAnnotationsOffset = currentOffset + break + case "RuntimeVisibleParameterAnnotations": + runtimeVisibleParameterAnnotationsOffset = currentOffset + break + case "RuntimeInvisibleParameterAnnotations": + runtimeInvisibleParameterAnnotationsOffset = currentOffset + break + case "MethodParameters": + methodParametersOffset = currentOffset + break + default: + attribute := c.readAttribute(context.attributePrototypes, attributeName, currentOffset, attributeLength, charBuffer, -1, nil) + attribute.nextAttribute = attributes + attributes = attribute + break + } + currentOffset += attributeLength + attributesCount-- + } + + var sig string + if signature != 0 { + sig = c.readUTF(signature, charBuffer) + } + methodVisitor := classVisitor.VisitMethod(context.currentMethodAccessFlags, context.currentMethodName, context.currentMethodDescriptor, sig, exceptions) + if methodVisitor == nil { + return currentOffset + } + + /* MethodWriter instanceof ? */ + + if methodParametersOffset != 0 { + parametersCount := c.readByte(methodParametersOffset) + currentParameterOffset := methodParametersOffset + 1 + for parametersCount > 0 { + methodVisitor.VisitParameter(c.readUTF8(currentParameterOffset, charBuffer), c.readUnsignedShort(currentParameterOffset+2)) + currentParameterOffset += 4 + parametersCount-- + } + } + + if annotationDefaultOffset != 0 { + annotationVisitor := methodVisitor.VisitAnnotationDefault() + c.readElementValue(annotationVisitor, annotationDefaultOffset, "", charBuffer) + if annotationVisitor != nil { + annotationVisitor.VisitEnd() + } + } + + if runtimeVisibleAnnotationsOffset != 0 { + numAnnotations := c.readUnsignedShort(runtimeVisibleAnnotationsOffset) + currentAnnotationOffset := runtimeVisibleAnnotationsOffset + 2 + for numAnnotations > 0 { + annotationDescriptor := c.readUTF8(currentAnnotationOffset, charBuffer) + currentAnnotationOffset += 2 + currentAnnotationOffset = c.readElementValues(methodVisitor.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(methodVisitor.VisitAnnotation(annotationDescriptor, false), currentAnnotationOffset, true, charBuffer) + numAnnotations-- + } + } + + if runtimeVisibleTypeAnnotationsOffset != 0 { + numAnnotations := c.readUnsignedShort(runtimeVisibleTypeAnnotationsOffset) + currentAnnotationOffset := runtimeVisibleTypeAnnotationsOffset + 2 + for numAnnotations > 0 { + currentAnnotationOffset = c.readTypeAnnotationTarget(context, currentAnnotationOffset) + annotationDescriptor := c.readUTF8(currentAnnotationOffset, charBuffer) + currentAnnotationOffset += 2 + annotationVisitor := methodVisitor.VisitTypeAnnotation(context.currentTypeAnnotationTarget, context.currentTypeAnnotationTargetPath, annotationDescriptor, true) + currentAnnotationOffset = c.readElementValues(annotationVisitor, 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 + annotationVisitor := methodVisitor.VisitTypeAnnotation(context.currentTypeAnnotationTarget, context.currentTypeAnnotationTargetPath, annotationDescriptor, false) + currentAnnotationOffset = c.readElementValues(annotationVisitor, currentAnnotationOffset, true, charBuffer) + } + } + + if runtimeVisibleParameterAnnotationsOffset != 0 { + c.readParameterAnnotations(methodVisitor, context, runtimeVisibleParameterAnnotationsOffset, true) + } + + if runtimeInvisibleParameterAnnotationsOffset != 0 { + c.readParameterAnnotations(methodVisitor, context, runtimeInvisibleParameterAnnotationsOffset, false) + } + + for attributes != nil { + nextAttribute := attributes.nextAttribute + attributes.nextAttribute = nil + methodVisitor.VisitAttribute(attributes) + attributes = nextAttribute + } + + if codeOffset != 0 { + methodVisitor.VisitCode() + c.readCode(methodVisitor, context, codeOffset) + } + + methodVisitor.VisitEnd() + return currentOffset } // ---------------------------------------------------------------------------------------------- // Methods to parse a Code attribute // ---------------------------------------------------------------------------------------------- -func (c ClassReader) readCode(methodVisitor MethodVisitor, contexnt *Context, codeOffset int) { +func (c ClassReader) readCode(methodVisitor MethodVisitor, context *Context, codeOffset int) { //TODO } @@ -673,10 +975,13 @@ func (c ClassReader) readUTFB(utfOffset int, utfLength int, charBuffer []rune) s d := ((currentByte & 0xF) << 12) + ((b[currentOffset] & 0x3F) << 6) currentOffset++ charBuffer[strLength] = rune((d + (b[currentOffset] & 0x3F))) + currentOffset++ strLength++ } } - return string(charBuffer) + str := make([]rune, strLength) + copy(str, charBuffer[0:strLength]) + return string(str) } func (c ClassReader) readStringish(offset int, charBuffer []rune) string { diff --git a/event-visitor.go b/event-visitor.go deleted file mode 100644 index e0a57f3..0000000 --- a/event-visitor.go +++ /dev/null @@ -1,58 +0,0 @@ -package main - -import ( - "github.com/leaklessgfy/asm/asm" -) - -type EventVisitor struct { - OnVisit []func(version, access int, name, signature, superName string, interfaces []string) - OnVisitEnd []func() -} - -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 int, version string) asm.ModuleVisitor { - return nil -} - -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{}) asm.FieldVisitor { - return nil -} - -func (e EventVisitor) VisitMethod(access int, name, descriptor, signature string, exceptions []string) asm.MethodVisitor { - return nil -} - -func (e EventVisitor) VisitEnd() { - for _, callback := range e.OnVisitEnd { - callback() - } -} diff --git a/main.go b/main.go index 1ea6fc9..023b478 100644 --- a/main.go +++ b/main.go @@ -26,16 +26,16 @@ func main() { os.Exit(1) } - reader.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) - }, + reader.Accept(&SimpleVisitor{ + OnVisit: func(version, access int, name, signature, superName string, interfaces []string) { + fmt.Println(name, signature, superName, interfaces) }, - OnVisitEnd: []func(){ - func() { - fmt.Println("End") - }, + OnVisitMethod: func(access int, name, descriptor, signature string, exceptions []string) asm.MethodVisitor { + fmt.Println(name) + return nil }, - }, 0) + OnVisitEnd: func() { + fmt.Println("End") + }, + }, asm.EXPAND_FRAMS) } diff --git a/simplevisitor.go b/simplevisitor.go new file mode 100644 index 0000000..dd171a9 --- /dev/null +++ b/simplevisitor.go @@ -0,0 +1,66 @@ +package main + +import ( + "github.com/leaklessgfy/asm/asm" +) + +type SimpleVisitor struct { + OnVisit func(version, access int, name, signature, superName string, interfaces []string) + OnVisitField func(access int, name, descriptor, signature string, value interface{}) asm.FieldVisitor + OnVisitMethod func(access int, name, descriptor, signature string, exceptions []string) asm.MethodVisitor + OnVisitEnd func() +} + +func (s SimpleVisitor) Visit(version, access int, name, signature, superName string, interfaces []string) { + if s.OnVisit != nil { + s.OnVisit(version, access, name, signature, superName, interfaces) + } +} + +func (s SimpleVisitor) VisitSource(source, debug string) { + +} + +func (s SimpleVisitor) VisitModule(name string, access int, version string) asm.ModuleVisitor { + return nil +} + +func (s SimpleVisitor) VisitOuterClass(owner, name, descriptor string) { + +} + +func (s SimpleVisitor) VisitAnnotation(descriptor string, visible bool) asm.AnnotationVisitor { + return nil +} + +func (s SimpleVisitor) VisitTypeAnnotation(typeRef, typePath int, descriptor string, visible bool) asm.AnnotationVisitor { + return nil +} + +func (s SimpleVisitor) VisitAttribute(attribute *asm.Attribute) { + +} + +func (s SimpleVisitor) VisitInnerClass(name, outerName, innerName string, access int) { + +} + +func (s SimpleVisitor) VisitField(access int, name, descriptor, signature string, value interface{}) asm.FieldVisitor { + if s.OnVisitField != nil { + return s.OnVisitField(access, name, descriptor, signature, value) + } + return nil +} + +func (s SimpleVisitor) VisitMethod(access int, name, descriptor, signature string, exceptions []string) asm.MethodVisitor { + if s.OnVisitMethod != nil { + return s.OnVisitMethod(access, name, descriptor, signature, exceptions) + } + return nil +} + +func (s SimpleVisitor) VisitEnd() { + if s.OnVisitEnd != nil { + s.OnVisitEnd() + } +}