Update dependency versions to hopefully work with Java 9
Dieser Commit ist enthalten in:
Ursprung
adb3c5392c
Commit
20d78832b0
@ -4,16 +4,11 @@ import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
|
||||
import net.sf.cglib.asm.ClassReader;
|
||||
import net.sf.cglib.asm.MethodVisitor;
|
||||
import net.sf.cglib.asm.Opcodes;
|
||||
import net.sf.cglib.asm.Type;
|
||||
|
||||
import com.comphenix.protocol.reflect.ClassAnalyser.AsmMethod.AsmOpcodes;
|
||||
import com.comphenix.protocol.reflect.compiler.EmptyClassVisitor;
|
||||
import com.comphenix.protocol.reflect.compiler.EmptyMethodVisitor;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import net.sf.cglib.asm.*;
|
||||
|
||||
public class ClassAnalyser {
|
||||
/**
|
||||
* Represents a method in ASM.
|
||||
@ -31,11 +26,11 @@ public class ClassAnalyser {
|
||||
|
||||
public static AsmOpcodes fromIntOpcode(int opcode) {
|
||||
switch (opcode) {
|
||||
case Opcodes.INVOKEVIRTUAL: return AsmOpcodes.INVOKE_VIRTUAL;
|
||||
case Opcodes.INVOKESPECIAL: return AsmOpcodes.INVOKE_SPECIAL;
|
||||
case Opcodes.INVOKESTATIC: return AsmOpcodes.INVOKE_STATIC;
|
||||
case Opcodes.INVOKEINTERFACE: return AsmOpcodes.INVOKE_INTERFACE;
|
||||
case Opcodes.INVOKEDYNAMIC: return AsmOpcodes.INVOKE_DYNAMIC;
|
||||
case $Opcodes.INVOKEVIRTUAL: return AsmOpcodes.INVOKE_VIRTUAL;
|
||||
case $Opcodes.INVOKESPECIAL: return AsmOpcodes.INVOKE_SPECIAL;
|
||||
case $Opcodes.INVOKESTATIC: return AsmOpcodes.INVOKE_STATIC;
|
||||
case $Opcodes.INVOKEINTERFACE: return AsmOpcodes.INVOKE_INTERFACE;
|
||||
case $Opcodes.INVOKEDYNAMIC: return AsmOpcodes.INVOKE_DYNAMIC;
|
||||
default: throw new IllegalArgumentException("Unknown opcode: " + opcode);
|
||||
}
|
||||
}
|
||||
@ -109,30 +104,29 @@ public class ClassAnalyser {
|
||||
* @return The method calls.
|
||||
* @throws IOException Cannot access the parent class.
|
||||
*/
|
||||
public List<AsmMethod> getMethodCalls(Class<?> clazz, Method method) throws IOException {
|
||||
final ClassReader reader = new ClassReader(clazz.getCanonicalName());
|
||||
private List<AsmMethod> getMethodCalls(Class<?> clazz, Method method) throws IOException {
|
||||
final $ClassReader reader = new $ClassReader(clazz.getCanonicalName());
|
||||
final List<AsmMethod> output = Lists.newArrayList();
|
||||
|
||||
// The method we are looking for
|
||||
final String methodName = method.getName();
|
||||
final String methodDescription = Type.getMethodDescriptor(method);
|
||||
|
||||
reader.accept(new EmptyClassVisitor() {
|
||||
final String methodDescription = $Type.getMethodDescriptor(method);
|
||||
|
||||
reader.accept(new $ClassVisitor($Opcodes.ASM5) {
|
||||
@Override
|
||||
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
|
||||
// Check method
|
||||
public $MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
|
||||
if (methodName.equals(name) && methodDescription.equals(desc)) {
|
||||
return new EmptyMethodVisitor() {
|
||||
return new $MethodVisitor($Opcodes.ASM5) {
|
||||
@Override
|
||||
public void visitMethodInsn(int opcode, String owner, String name, String desc) {
|
||||
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean flag) {
|
||||
output.add(new AsmMethod(AsmOpcodes.fromIntOpcode(opcode), owner, methodName, desc));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}, ClassReader.EXPAND_FRAMES);
|
||||
}, $ClassReader.EXPAND_FRAMES);
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
@ -17,25 +17,25 @@
|
||||
|
||||
package com.comphenix.protocol.reflect.compiler;
|
||||
|
||||
import net.sf.cglib.asm.MethodVisitor;
|
||||
import net.sf.cglib.asm.Opcodes;
|
||||
import net.sf.cglib.asm.Type;
|
||||
import net.sf.cglib.asm.$MethodVisitor;
|
||||
import net.sf.cglib.asm.$Opcodes;
|
||||
import net.sf.cglib.asm.$Type;
|
||||
|
||||
/**
|
||||
* Used by the compiler to automatically box and unbox values.
|
||||
*/
|
||||
class BoxingHelper {
|
||||
|
||||
private final static Type BYTE_TYPE = Type.getObjectType("java/lang/Byte");
|
||||
private final static Type BOOLEAN_TYPE = Type.getObjectType("java/lang/Boolean");
|
||||
private final static Type SHORT_TYPE = Type.getObjectType("java/lang/Short");
|
||||
private final static Type CHARACTER_TYPE = Type.getObjectType("java/lang/Character");
|
||||
private final static Type INTEGER_TYPE = Type.getObjectType("java/lang/Integer");
|
||||
private final static Type FLOAT_TYPE = Type.getObjectType("java/lang/Float");
|
||||
private final static Type LONG_TYPE = Type.getObjectType("java/lang/Long");
|
||||
private final static Type DOUBLE_TYPE = Type.getObjectType("java/lang/Double");
|
||||
private final static Type NUMBER_TYPE = Type.getObjectType("java/lang/Number");
|
||||
private final static Type OBJECT_TYPE = Type.getObjectType("java/lang/Object");
|
||||
private final static $Type BYTE_$Type = $Type.getObjectType("java/lang/Byte");
|
||||
private final static $Type BOOLEAN_$Type = $Type.getObjectType("java/lang/Boolean");
|
||||
private final static $Type SHORT_$Type = $Type.getObjectType("java/lang/Short");
|
||||
private final static $Type CHARACTER_$Type = $Type.getObjectType("java/lang/Character");
|
||||
private final static $Type INTEGER_$Type = $Type.getObjectType("java/lang/Integer");
|
||||
private final static $Type FLOAT_$Type = $Type.getObjectType("java/lang/Float");
|
||||
private final static $Type LONG_$Type = $Type.getObjectType("java/lang/Long");
|
||||
private final static $Type DOUBLE_$Type = $Type.getObjectType("java/lang/Double");
|
||||
private final static $Type NUMBER_$Type = $Type.getObjectType("java/lang/Number");
|
||||
private final static $Type OBJECT_$Type = $Type.getObjectType("java/lang/Object");
|
||||
|
||||
private final static MethodDescriptor BOOLEAN_VALUE = MethodDescriptor.getMethod("boolean booleanValue()");
|
||||
private final static MethodDescriptor CHAR_VALUE = MethodDescriptor.getMethod("char charValue()");
|
||||
@ -44,9 +44,9 @@ class BoxingHelper {
|
||||
private final static MethodDescriptor LONG_VALUE = MethodDescriptor.getMethod("long longValue()");
|
||||
private final static MethodDescriptor DOUBLE_VALUE = MethodDescriptor.getMethod("double doubleValue()");
|
||||
|
||||
private MethodVisitor mv;
|
||||
private $MethodVisitor mv;
|
||||
|
||||
public BoxingHelper(MethodVisitor mv) {
|
||||
public BoxingHelper($MethodVisitor mv) {
|
||||
this.mv = mv;
|
||||
}
|
||||
|
||||
@ -54,42 +54,42 @@ class BoxingHelper {
|
||||
* Generates the instructions to box the top stack value. This value is
|
||||
* replaced by its boxed equivalent on top of the stack.
|
||||
*
|
||||
* @param type the type of the top stack value.
|
||||
* @param type the $Type of the top stack value.
|
||||
*/
|
||||
public void box(final Type type){
|
||||
if(type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) {
|
||||
public void box(final $Type type){
|
||||
if(type.getSort() == $Type.OBJECT || type.getSort() == $Type.ARRAY) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(type == Type.VOID_TYPE) {
|
||||
if(type == $Type.VOID_TYPE) {
|
||||
push((String) null);
|
||||
} else {
|
||||
Type boxed = type;
|
||||
$Type boxed = type;
|
||||
|
||||
switch(type.getSort()) {
|
||||
case Type.BYTE:
|
||||
boxed = BYTE_TYPE;
|
||||
case $Type.BYTE:
|
||||
boxed = BYTE_$Type;
|
||||
break;
|
||||
case Type.BOOLEAN:
|
||||
boxed = BOOLEAN_TYPE;
|
||||
case $Type.BOOLEAN:
|
||||
boxed = BOOLEAN_$Type;
|
||||
break;
|
||||
case Type.SHORT:
|
||||
boxed = SHORT_TYPE;
|
||||
case $Type.SHORT:
|
||||
boxed = SHORT_$Type;
|
||||
break;
|
||||
case Type.CHAR:
|
||||
boxed = CHARACTER_TYPE;
|
||||
case $Type.CHAR:
|
||||
boxed = CHARACTER_$Type;
|
||||
break;
|
||||
case Type.INT:
|
||||
boxed = INTEGER_TYPE;
|
||||
case $Type.INT:
|
||||
boxed = INTEGER_$Type;
|
||||
break;
|
||||
case Type.FLOAT:
|
||||
boxed = FLOAT_TYPE;
|
||||
case $Type.FLOAT:
|
||||
boxed = FLOAT_$Type;
|
||||
break;
|
||||
case Type.LONG:
|
||||
boxed = LONG_TYPE;
|
||||
case $Type.LONG:
|
||||
boxed = LONG_$Type;
|
||||
break;
|
||||
case Type.DOUBLE:
|
||||
boxed = DOUBLE_TYPE;
|
||||
case $Type.DOUBLE:
|
||||
boxed = DOUBLE_$Type;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -105,46 +105,46 @@ class BoxingHelper {
|
||||
swap();
|
||||
}
|
||||
|
||||
invokeConstructor(boxed, new MethodDescriptor("<init>", Type.VOID_TYPE, new Type[] {type}));
|
||||
invokeConstructor(boxed, new MethodDescriptor("<init>", $Type.VOID_TYPE, new $Type[] {type}));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the instruction to invoke a constructor.
|
||||
*
|
||||
* @param type the class in which the constructor is defined.
|
||||
* @param $Type the class in which the constructor is defined.
|
||||
* @param method the constructor to be invoked.
|
||||
*/
|
||||
public void invokeConstructor(final Type type, final MethodDescriptor method){
|
||||
invokeInsn(Opcodes.INVOKESPECIAL, type, method);
|
||||
public void invokeConstructor(final $Type $Type, final MethodDescriptor method){
|
||||
invokeInsn($Opcodes.INVOKESPECIAL, $Type, method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a DUP_X1 instruction.
|
||||
*/
|
||||
public void dupX1(){
|
||||
mv.visitInsn(Opcodes.DUP_X1);
|
||||
mv.visitInsn($Opcodes.DUP_X1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a DUP_X2 instruction.
|
||||
*/
|
||||
public void dupX2(){
|
||||
mv.visitInsn(Opcodes.DUP_X2);
|
||||
mv.visitInsn($Opcodes.DUP_X2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a POP instruction.
|
||||
*/
|
||||
public void pop(){
|
||||
mv.visitInsn(Opcodes.POP);
|
||||
mv.visitInsn($Opcodes.POP);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a SWAP instruction.
|
||||
*/
|
||||
public void swap(){
|
||||
mv.visitInsn(Opcodes.SWAP);
|
||||
mv.visitInsn($Opcodes.SWAP);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -163,11 +163,11 @@ class BoxingHelper {
|
||||
*/
|
||||
public void push(final int value) {
|
||||
if (value >= -1 && value <= 5) {
|
||||
mv.visitInsn(Opcodes.ICONST_0 + value);
|
||||
mv.visitInsn($Opcodes.ICONST_0 + value);
|
||||
} else if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) {
|
||||
mv.visitIntInsn(Opcodes.BIPUSH, value);
|
||||
mv.visitIntInsn($Opcodes.BIPUSH, value);
|
||||
} else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) {
|
||||
mv.visitIntInsn(Opcodes.SIPUSH, value);
|
||||
mv.visitIntInsn($Opcodes.SIPUSH, value);
|
||||
} else {
|
||||
mv.visitLdcInsn(new Integer(value));
|
||||
}
|
||||
@ -176,10 +176,10 @@ class BoxingHelper {
|
||||
/**
|
||||
* Generates the instruction to create a new object.
|
||||
*
|
||||
* @param type the class of the object to be created.
|
||||
* @param $Type the class of the object to be created.
|
||||
*/
|
||||
public void newInstance(final Type type){
|
||||
typeInsn(Opcodes.NEW, type);
|
||||
public void newInstance(final $Type $Type){
|
||||
$TypeInsn($Opcodes.NEW, $Type);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -189,7 +189,7 @@ class BoxingHelper {
|
||||
*/
|
||||
public void push(final String value) {
|
||||
if (value == null) {
|
||||
mv.visitInsn(Opcodes.ACONST_NULL);
|
||||
mv.visitInsn($Opcodes.ACONST_NULL);
|
||||
} else {
|
||||
mv.visitLdcInsn(value);
|
||||
}
|
||||
@ -200,35 +200,35 @@ class BoxingHelper {
|
||||
* replaced by its unboxed equivalent on top of the stack.
|
||||
*
|
||||
* @param type
|
||||
* the type of the top stack value.
|
||||
* the $Type of the top stack value.
|
||||
*/
|
||||
public void unbox(final Type type){
|
||||
Type t = NUMBER_TYPE;
|
||||
public void unbox(final $Type type){
|
||||
$Type t = NUMBER_$Type;
|
||||
MethodDescriptor sig = null;
|
||||
|
||||
switch(type.getSort()) {
|
||||
case Type.VOID:
|
||||
case $Type.VOID:
|
||||
return;
|
||||
case Type.CHAR:
|
||||
t = CHARACTER_TYPE;
|
||||
case $Type.CHAR:
|
||||
t = CHARACTER_$Type;
|
||||
sig = CHAR_VALUE;
|
||||
break;
|
||||
case Type.BOOLEAN:
|
||||
t = BOOLEAN_TYPE;
|
||||
case $Type.BOOLEAN:
|
||||
t = BOOLEAN_$Type;
|
||||
sig = BOOLEAN_VALUE;
|
||||
break;
|
||||
case Type.DOUBLE:
|
||||
case $Type.DOUBLE:
|
||||
sig = DOUBLE_VALUE;
|
||||
break;
|
||||
case Type.FLOAT:
|
||||
case $Type.FLOAT:
|
||||
sig = FLOAT_VALUE;
|
||||
break;
|
||||
case Type.LONG:
|
||||
case $Type.LONG:
|
||||
sig = LONG_VALUE;
|
||||
break;
|
||||
case Type.INT:
|
||||
case Type.SHORT:
|
||||
case Type.BYTE:
|
||||
case $Type.INT:
|
||||
case $Type.SHORT:
|
||||
case $Type.BYTE:
|
||||
sig = INT_VALUE;
|
||||
}
|
||||
|
||||
@ -242,13 +242,13 @@ class BoxingHelper {
|
||||
|
||||
/**
|
||||
* Generates the instruction to check that the top stack value is of the
|
||||
* given type.
|
||||
* given $Type.
|
||||
*
|
||||
* @param type a class or interface type.
|
||||
* @param $Type a class or interface $Type.
|
||||
*/
|
||||
public void checkCast(final Type type){
|
||||
if(!type.equals(OBJECT_TYPE)) {
|
||||
typeInsn(Opcodes.CHECKCAST, type);
|
||||
public void checkCast(final $Type $Type){
|
||||
if(!$Type.equals(OBJECT_$Type)) {
|
||||
$TypeInsn($Opcodes.CHECKCAST, $Type);
|
||||
}
|
||||
}
|
||||
|
||||
@ -258,35 +258,35 @@ class BoxingHelper {
|
||||
* @param owner the class in which the method is defined.
|
||||
* @param method the method to be invoked.
|
||||
*/
|
||||
public void invokeVirtual(final Type owner, final MethodDescriptor method){
|
||||
invokeInsn(Opcodes.INVOKEVIRTUAL, owner, method);
|
||||
public void invokeVirtual(final $Type owner, final MethodDescriptor method){
|
||||
invokeInsn($Opcodes.INVOKEVIRTUAL, owner, method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an invoke method instruction.
|
||||
*
|
||||
* @param opcode the instruction's opcode.
|
||||
* @param type the class in which the method is defined.
|
||||
* @param $Type the class in which the method is defined.
|
||||
* @param method the method to be invoked.
|
||||
*/
|
||||
private void invokeInsn(final int opcode, final Type type, final MethodDescriptor method){
|
||||
String owner = type.getSort() == Type.ARRAY ? type.getDescriptor() : type.getInternalName();
|
||||
private void invokeInsn(final int opcode, final $Type $Type, final MethodDescriptor method){
|
||||
String owner = $Type.getSort() == $Type.ARRAY ? $Type.getDescriptor() : $Type.getInternalName();
|
||||
mv.visitMethodInsn(opcode, owner, method.getName(), method.getDescriptor());
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a type dependent instruction.
|
||||
* Generates a $Type dependent instruction.
|
||||
*
|
||||
* @param opcode the instruction's opcode.
|
||||
* @param type the instruction's operand.
|
||||
* @param $Type the instruction's operand.
|
||||
*/
|
||||
private void typeInsn(final int opcode, final Type type){
|
||||
private void $TypeInsn(final int opcode, final $Type $Type){
|
||||
String desc;
|
||||
|
||||
if(type.getSort() == Type.ARRAY) {
|
||||
desc = type.getDescriptor();
|
||||
if($Type.getSort() == $Type.ARRAY) {
|
||||
desc = $Type.getDescriptor();
|
||||
} else {
|
||||
desc = type.getInternalName();
|
||||
desc = $Type.getInternalName();
|
||||
}
|
||||
|
||||
mv.visitTypeInsn(opcode, desc);
|
||||
|
@ -1,57 +0,0 @@
|
||||
package com.comphenix.protocol.reflect.compiler;
|
||||
|
||||
import net.sf.cglib.asm.AnnotationVisitor;
|
||||
import net.sf.cglib.asm.Attribute;
|
||||
import net.sf.cglib.asm.ClassVisitor;
|
||||
import net.sf.cglib.asm.FieldVisitor;
|
||||
import net.sf.cglib.asm.MethodVisitor;
|
||||
|
||||
public abstract class EmptyClassVisitor implements ClassVisitor {
|
||||
@Override
|
||||
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
|
||||
// NOP
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitAttribute(Attribute attr) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitEnd() {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
|
||||
// NOP
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitInnerClass(String name, String outerName, String innerName, int access) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
|
||||
// NOP
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitOuterClass(String owner, String name, String desc) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitSource(String source, String debug) {
|
||||
// NOP
|
||||
}
|
||||
}
|
@ -1,132 +0,0 @@
|
||||
package com.comphenix.protocol.reflect.compiler;
|
||||
|
||||
import net.sf.cglib.asm.AnnotationVisitor;
|
||||
import net.sf.cglib.asm.Attribute;
|
||||
import net.sf.cglib.asm.Label;
|
||||
import net.sf.cglib.asm.MethodVisitor;
|
||||
|
||||
public class EmptyMethodVisitor implements MethodVisitor {
|
||||
@Override
|
||||
public AnnotationVisitor visitAnnotationDefault() {
|
||||
// NOP
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
|
||||
// NOP
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) {
|
||||
// NOP
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitAttribute(Attribute attr) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitCode() {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitInsn(int opcode) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitIntInsn(int opcode, int operand) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitVarInsn(int opcode, int var) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitTypeInsn(int opcode, String type) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitFieldInsn(int opcode, String owner, String name, String desc) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMethodInsn(int opcode, String owner, String name, String desc) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitJumpInsn(int opcode, Label label) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitLabel(Label label) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitLdcInsn(Object cst) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitIincInsn(int var, int increment) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitTableSwitchInsn(int min, int max, Label dflt, Label[] labels) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMultiANewArrayInsn(String desc, int dims) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitLocalVariable(String name, String desc, String signature, Label start,
|
||||
Label end, int index) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitLineNumber(int line, Label start) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMaxs(int maxStack, int maxLocals) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitEnd() {
|
||||
// NOP
|
||||
}
|
||||
}
|
@ -20,7 +20,7 @@ package com.comphenix.protocol.reflect.compiler;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import net.sf.cglib.asm.Type;
|
||||
import net.sf.cglib.asm.$Type;
|
||||
|
||||
/**
|
||||
* Represents a method.
|
||||
@ -56,7 +56,7 @@ class MethodDescriptor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link Method}.
|
||||
* Creates a new {@link MethodDescriptor}.
|
||||
*
|
||||
* @param name the method's name.
|
||||
* @param desc the method's descriptor.
|
||||
@ -67,7 +67,7 @@ class MethodDescriptor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link Method}.
|
||||
* Creates a new {@link MethodDescriptor}.
|
||||
*
|
||||
* @param name the method's name.
|
||||
* @param returnType the method's return type.
|
||||
@ -75,14 +75,14 @@ class MethodDescriptor {
|
||||
*/
|
||||
public MethodDescriptor(
|
||||
final String name,
|
||||
final Type returnType,
|
||||
final Type[] argumentTypes)
|
||||
final $Type returnType,
|
||||
final $Type[] argumentTypes)
|
||||
{
|
||||
this(name, Type.getMethodDescriptor(returnType, argumentTypes));
|
||||
this(name, $Type.getMethodDescriptor(returnType, argumentTypes));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link Method} corresponding to the given Java method
|
||||
* Returns a {@link MethodDescriptor} corresponding to the given Java method
|
||||
* declaration.
|
||||
*
|
||||
* @param method a Java method declaration, without argument names, of the
|
||||
@ -91,7 +91,7 @@ class MethodDescriptor {
|
||||
* "java.util.List", ...). Classes of the java.lang package can be
|
||||
* specified by their unqualified name; all other classes names must
|
||||
* be fully qualified.
|
||||
* @return a {@link Method} corresponding to the given Java method
|
||||
* @return a {@link MethodDescriptor} corresponding to the given Java method
|
||||
* declaration.
|
||||
* @throws IllegalArgumentException if <code>method</code> could not get
|
||||
* parsed.
|
||||
@ -103,7 +103,7 @@ class MethodDescriptor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link Method} corresponding to the given Java method
|
||||
* Returns a {@link MethodDescriptor} corresponding to the given Java method
|
||||
* declaration.
|
||||
*
|
||||
* @param method a Java method declaration, without argument names, of the
|
||||
@ -117,7 +117,7 @@ class MethodDescriptor {
|
||||
* default package, or false if they correspond to java.lang classes.
|
||||
* For instance "Object" means "Object" if this option is true, or
|
||||
* "java.lang.Object" otherwise.
|
||||
* @return a {@link Method} corresponding to the given Java method
|
||||
* @return a {@link MethodDescriptor} corresponding to the given Java method
|
||||
* declaration.
|
||||
* @throws IllegalArgumentException if <code>method</code> could not get
|
||||
* parsed.
|
||||
@ -134,7 +134,7 @@ class MethodDescriptor {
|
||||
}
|
||||
String returnType = method.substring(0, space);
|
||||
String methodName = method.substring(space + 1, start - 1).trim();
|
||||
StringBuffer sb = new StringBuffer();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append('(');
|
||||
int p;
|
||||
do {
|
||||
@ -158,7 +158,7 @@ class MethodDescriptor {
|
||||
return type;
|
||||
}
|
||||
|
||||
StringBuffer sb = new StringBuffer();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
int index = 0;
|
||||
while ((index = type.indexOf("[]", index) + 1) > 0) {
|
||||
sb.append('[');
|
||||
@ -206,8 +206,8 @@ class MethodDescriptor {
|
||||
*
|
||||
* @return the return type of the method described by this object.
|
||||
*/
|
||||
public Type getReturnType() {
|
||||
return Type.getReturnType(desc);
|
||||
public $Type getReturnType() {
|
||||
return $Type.getReturnType(desc);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -215,8 +215,8 @@ class MethodDescriptor {
|
||||
*
|
||||
* @return the argument types of the method described by this object.
|
||||
*/
|
||||
public Type[] getArgumentTypes() {
|
||||
return Type.getArgumentTypes(desc);
|
||||
public $Type[] getArgumentTypes() {
|
||||
return $Type.getArgumentTypes(desc);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
|
@ -25,12 +25,12 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import net.sf.cglib.asm.ClassWriter;
|
||||
import net.sf.cglib.asm.FieldVisitor;
|
||||
import net.sf.cglib.asm.Label;
|
||||
import net.sf.cglib.asm.MethodVisitor;
|
||||
import net.sf.cglib.asm.Opcodes;
|
||||
import net.sf.cglib.asm.Type;
|
||||
import net.sf.cglib.asm.$ClassWriter;
|
||||
import net.sf.cglib.asm.$FieldVisitor;
|
||||
import net.sf.cglib.asm.$Label;
|
||||
import net.sf.cglib.asm.$MethodVisitor;
|
||||
import net.sf.cglib.asm.$Opcodes;
|
||||
import net.sf.cglib.asm.$Type;
|
||||
|
||||
import com.comphenix.protocol.ProtocolLibrary;
|
||||
import com.comphenix.protocol.error.Report;
|
||||
@ -95,22 +95,22 @@ import com.google.common.primitives.Primitives;
|
||||
|
||||
/**
|
||||
* Represents a StructureModifier compiler.
|
||||
*
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public final class StructureCompiler {
|
||||
public static final ReportType REPORT_TOO_MANY_GENERATED_CLASSES = new ReportType("Generated too many classes (count: %s)");
|
||||
|
||||
|
||||
// Used to store generated classes of different types
|
||||
@SuppressWarnings("rawtypes")
|
||||
static class StructureKey {
|
||||
private Class targetType;
|
||||
private Class fieldType;
|
||||
|
||||
|
||||
public StructureKey(StructureModifier<?> source) {
|
||||
this(source.getTargetType(), source.getFieldType());
|
||||
}
|
||||
|
||||
|
||||
public StructureKey(Class targetType, Class fieldType) {
|
||||
this.targetType = targetType;
|
||||
this.fieldType = fieldType;
|
||||
@ -120,7 +120,7 @@ public final class StructureCompiler {
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(targetType, fieldType);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj instanceof StructureKey) {
|
||||
@ -131,16 +131,16 @@ public final class StructureCompiler {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Used to load classes
|
||||
private volatile static Method defineMethod;
|
||||
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
private Map<StructureKey, Class> compiledCache = new ConcurrentHashMap<StructureKey, Class>();
|
||||
|
||||
|
||||
// The class loader we'll store our classes
|
||||
private ClassLoader loader;
|
||||
|
||||
|
||||
// References to other classes
|
||||
private static String PACKAGE_NAME = "com/comphenix/protocol/reflect/compiler";
|
||||
private static String SUPER_CLASS = "com/comphenix/protocol/reflect/StructureModifier";
|
||||
@ -156,7 +156,7 @@ public final class StructureCompiler {
|
||||
StructureCompiler(ClassLoader loader) {
|
||||
this.loader = loader;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Lookup the current class loader for any previously generated classes before we attempt to generate something.
|
||||
* @param <TField> Type
|
||||
@ -194,7 +194,7 @@ public final class StructureCompiler {
|
||||
// We need to compile the class
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Compiles the given structure modifier.
|
||||
* <p>
|
||||
@ -211,15 +211,15 @@ public final class StructureCompiler {
|
||||
if (!isAnyPublic(source.getFields())) {
|
||||
return source;
|
||||
}
|
||||
|
||||
|
||||
StructureKey key = new StructureKey(source);
|
||||
Class<?> compiledClass = compiledCache.get(key);
|
||||
|
||||
|
||||
if (!compiledCache.containsKey(key)) {
|
||||
compiledClass = generateClass(source);
|
||||
compiledCache.put(key, compiledClass);
|
||||
}
|
||||
|
||||
|
||||
// Next, create an instance of this class
|
||||
try {
|
||||
return (StructureModifier<TField>) compiledClass.getConstructor(
|
||||
@ -245,7 +245,7 @@ public final class StructureCompiler {
|
||||
throw new IllegalStateException("Cannot happen.", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve a variable identifier that can uniquely represent the given type.
|
||||
* @param type - a type.
|
||||
@ -254,7 +254,7 @@ public final class StructureCompiler {
|
||||
private String getSafeTypeName(Class<?> type) {
|
||||
return type.getCanonicalName().replace("[]", "Array").replace(".", "_");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve the compiled name of a given structure modifier.
|
||||
* @param source - the structure modifier.
|
||||
@ -262,29 +262,29 @@ public final class StructureCompiler {
|
||||
*/
|
||||
private String getCompiledName(StructureModifier<?> source) {
|
||||
Class<?> targetType = source.getTargetType();
|
||||
|
||||
|
||||
// Concat class and field type
|
||||
return "CompiledStructure$" +
|
||||
getSafeTypeName(targetType) + "$" +
|
||||
getSafeTypeName(source.getFieldType());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Compile a structure modifier.
|
||||
* @param source - structure modifier.
|
||||
* @return The compiled structure modifier.
|
||||
*/
|
||||
private <TField> Class<?> generateClass(StructureModifier<TField> source) {
|
||||
|
||||
ClassWriter cw = new ClassWriter(0);
|
||||
|
||||
$ClassWriter cw = new $ClassWriter(0);
|
||||
Class<?> targetType = source.getTargetType();
|
||||
|
||||
|
||||
String className = getCompiledName(source);
|
||||
String targetSignature = Type.getDescriptor(targetType);
|
||||
String targetSignature = $Type.getDescriptor(targetType);
|
||||
String targetName = targetType.getName().replace('.', '/');
|
||||
|
||||
|
||||
// Define class
|
||||
cw.visit(Opcodes.V1_6, Opcodes.ACC_PUBLIC + Opcodes.ACC_SUPER, PACKAGE_NAME + "/" + className,
|
||||
cw.visit($Opcodes.V1_6, $Opcodes.ACC_PUBLIC + $Opcodes.ACC_SUPER, PACKAGE_NAME + "/" + className,
|
||||
null, COMPILED_CLASS, null);
|
||||
|
||||
createFields(cw, targetSignature);
|
||||
@ -292,29 +292,29 @@ public final class StructureCompiler {
|
||||
createReadMethod(cw, className, source.getFields(), targetSignature, targetName);
|
||||
createWriteMethod(cw, className, source.getFields(), targetSignature, targetName);
|
||||
cw.visitEnd();
|
||||
|
||||
|
||||
byte[] data = cw.toByteArray();
|
||||
|
||||
|
||||
// Call the define method
|
||||
try {
|
||||
if (defineMethod == null) {
|
||||
Method defined = ClassLoader.class.getDeclaredMethod("defineClass",
|
||||
new Class<?>[] { String.class, byte[].class, int.class, int.class });
|
||||
|
||||
|
||||
// Awesome. Now, create and return it.
|
||||
defined.setAccessible(true);
|
||||
defineMethod = defined;
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
Class clazz = (Class) defineMethod.invoke(loader, null, data, 0, data.length);
|
||||
|
||||
|
||||
// DEBUG CODE: Print the content of the generated class.
|
||||
//org.objectweb.asm.ClassReader cr = new org.objectweb.asm.ClassReader(data);
|
||||
//cr.accept(new ASMifierClassVisitor(new PrintWriter(System.out)), 0);
|
||||
|
||||
|
||||
return clazz;
|
||||
|
||||
|
||||
} catch (SecurityException e) {
|
||||
throw new RuntimeException("Cannot use reflection to dynamically load a class.", e);
|
||||
} catch (NoSuchMethodException e) {
|
||||
@ -327,7 +327,7 @@ public final class StructureCompiler {
|
||||
throw new RuntimeException("Error occured in code generator.", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine if at least one of the given fields is public.
|
||||
* @param fields - field to test.
|
||||
@ -340,208 +340,208 @@ public final class StructureCompiler {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
private boolean isPublic(Field field) {
|
||||
return Modifier.isPublic(field.getModifiers());
|
||||
}
|
||||
|
||||
|
||||
private boolean isNonFinal(Field field) {
|
||||
return !Modifier.isFinal(field.getModifiers());
|
||||
}
|
||||
|
||||
private void createFields(ClassWriter cw, String targetSignature) {
|
||||
FieldVisitor typedField = cw.visitField(Opcodes.ACC_PRIVATE, "typedTarget", targetSignature, null, null);
|
||||
|
||||
private void createFields($ClassWriter cw, String targetSignature) {
|
||||
$FieldVisitor typedField = cw.visitField($Opcodes.ACC_PRIVATE, "typedTarget", targetSignature, null, null);
|
||||
typedField.visitEnd();
|
||||
}
|
||||
|
||||
private void createWriteMethod(ClassWriter cw, String className, List<Field> fields, String targetSignature, String targetName) {
|
||||
|
||||
|
||||
private void createWriteMethod($ClassWriter cw, String className, List<Field> fields, String targetSignature, String targetName) {
|
||||
|
||||
String methodDescriptor = "(ILjava/lang/Object;)L" + SUPER_CLASS + ";";
|
||||
String methodSignature = "(ILjava/lang/Object;)L" + SUPER_CLASS + "<Ljava/lang/Object;>;";
|
||||
MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PROTECTED, "writeGenerated", methodDescriptor, methodSignature,
|
||||
$MethodVisitor mv = cw.visitMethod($Opcodes.ACC_PROTECTED, "writeGenerated", methodDescriptor, methodSignature,
|
||||
new String[] { FIELD_EXCEPTION_CLASS });
|
||||
BoxingHelper boxingHelper = new BoxingHelper(mv);
|
||||
|
||||
String generatedClassName = PACKAGE_NAME + "/" + className;
|
||||
|
||||
mv.visitCode();
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 0);
|
||||
mv.visitFieldInsn(Opcodes.GETFIELD, generatedClassName, "typedTarget", targetSignature);
|
||||
mv.visitVarInsn(Opcodes.ASTORE, 3);
|
||||
mv.visitVarInsn(Opcodes.ILOAD, 1);
|
||||
|
||||
// The last label is for the default switch
|
||||
Label[] labels = new Label[fields.size()];
|
||||
Label errorLabel = new Label();
|
||||
Label returnLabel = new Label();
|
||||
|
||||
// Generate labels
|
||||
String generatedClassName = PACKAGE_NAME + "/" + className;
|
||||
|
||||
mv.visitCode();
|
||||
mv.visitVarInsn($Opcodes.ALOAD, 0);
|
||||
mv.visitFieldInsn($Opcodes.GETFIELD, generatedClassName, "typedTarget", targetSignature);
|
||||
mv.visitVarInsn($Opcodes.ASTORE, 3);
|
||||
mv.visitVarInsn($Opcodes.ILOAD, 1);
|
||||
|
||||
// The last $Label is for the default switch
|
||||
$Label[] $Labels = new $Label[fields.size()];
|
||||
$Label error$Label = new $Label();
|
||||
$Label return$Label = new $Label();
|
||||
|
||||
// Generate $Labels
|
||||
for (int i = 0; i < fields.size(); i++) {
|
||||
labels[i] = new Label();
|
||||
$Labels[i] = new $Label();
|
||||
}
|
||||
|
||||
mv.visitTableSwitchInsn(0, labels.length - 1, errorLabel, labels);
|
||||
|
||||
|
||||
mv.visitTableSwitchInsn(0, $Labels.length - 1, error$Label, $Labels);
|
||||
|
||||
for (int i = 0; i < fields.size(); i++) {
|
||||
|
||||
|
||||
Field field = fields.get(i);
|
||||
Class<?> outputType = field.getType();
|
||||
Class<?> inputType = Primitives.wrap(outputType);
|
||||
String typeDescriptor = Type.getDescriptor(outputType);
|
||||
String typeDescriptor = $Type.getDescriptor(outputType);
|
||||
String inputPath = inputType.getName().replace('.', '/');
|
||||
|
||||
mv.visitLabel(labels[i]);
|
||||
|
||||
|
||||
mv.visitLabel($Labels[i]);
|
||||
|
||||
// Push the compare object
|
||||
if (i == 0)
|
||||
mv.visitFrame(Opcodes.F_APPEND, 1, new Object[] { targetName }, 0, null);
|
||||
mv.visitFrame($Opcodes.F_APPEND, 1, new Object[] { targetName }, 0, null);
|
||||
else
|
||||
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
|
||||
|
||||
mv.visitFrame($Opcodes.F_SAME, 0, null, 0, null);
|
||||
|
||||
// Only write to public non-final fields
|
||||
if (isPublic(field) && isNonFinal(field)) {
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 3);
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 2);
|
||||
|
||||
mv.visitVarInsn($Opcodes.ALOAD, 3);
|
||||
mv.visitVarInsn($Opcodes.ALOAD, 2);
|
||||
|
||||
if (!outputType.isPrimitive())
|
||||
mv.visitTypeInsn(Opcodes.CHECKCAST, inputPath);
|
||||
mv.visitTypeInsn($Opcodes.CHECKCAST, inputPath);
|
||||
else
|
||||
boxingHelper.unbox(Type.getType(outputType));
|
||||
|
||||
mv.visitFieldInsn(Opcodes.PUTFIELD, targetName, field.getName(), typeDescriptor);
|
||||
|
||||
boxingHelper.unbox($Type.getType(outputType));
|
||||
|
||||
mv.visitFieldInsn($Opcodes.PUTFIELD, targetName, field.getName(), typeDescriptor);
|
||||
|
||||
} else {
|
||||
// Use reflection. We don't have a choice, unfortunately.
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 0);
|
||||
mv.visitVarInsn(Opcodes.ILOAD, 1);
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 2);
|
||||
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, generatedClassName, "writeReflected", "(ILjava/lang/Object;)V");
|
||||
mv.visitVarInsn($Opcodes.ALOAD, 0);
|
||||
mv.visitVarInsn($Opcodes.ILOAD, 1);
|
||||
mv.visitVarInsn($Opcodes.ALOAD, 2);
|
||||
mv.visitMethodInsn($Opcodes.INVOKEVIRTUAL, generatedClassName, "writeReflected", "(ILjava/lang/Object;)V");
|
||||
}
|
||||
|
||||
mv.visitJumpInsn(Opcodes.GOTO, returnLabel);
|
||||
|
||||
mv.visitJumpInsn($Opcodes.GOTO, return$Label);
|
||||
}
|
||||
|
||||
mv.visitLabel(errorLabel);
|
||||
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
|
||||
mv.visitTypeInsn(Opcodes.NEW, FIELD_EXCEPTION_CLASS);
|
||||
mv.visitInsn(Opcodes.DUP);
|
||||
mv.visitTypeInsn(Opcodes.NEW, "java/lang/StringBuilder");
|
||||
mv.visitInsn(Opcodes.DUP);
|
||||
|
||||
mv.visitLabel(error$Label);
|
||||
mv.visitFrame($Opcodes.F_SAME, 0, null, 0, null);
|
||||
mv.visitTypeInsn($Opcodes.NEW, FIELD_EXCEPTION_CLASS);
|
||||
mv.visitInsn($Opcodes.DUP);
|
||||
mv.visitTypeInsn($Opcodes.NEW, "java/lang/StringBuilder");
|
||||
mv.visitInsn($Opcodes.DUP);
|
||||
mv.visitLdcInsn("Invalid index ");
|
||||
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "(Ljava/lang/String;)V");
|
||||
mv.visitVarInsn(Opcodes.ILOAD, 1);
|
||||
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(I)Ljava/lang/StringBuilder;");
|
||||
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;");
|
||||
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, FIELD_EXCEPTION_CLASS, "<init>", "(Ljava/lang/String;)V");
|
||||
mv.visitInsn(Opcodes.ATHROW);
|
||||
|
||||
mv.visitLabel(returnLabel);
|
||||
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 0);
|
||||
mv.visitInsn(Opcodes.ARETURN);
|
||||
mv.visitMethodInsn($Opcodes.INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "(Ljava/lang/String;)V");
|
||||
mv.visitVarInsn($Opcodes.ILOAD, 1);
|
||||
mv.visitMethodInsn($Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(I)Ljava/lang/StringBuilder;");
|
||||
mv.visitMethodInsn($Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;");
|
||||
mv.visitMethodInsn($Opcodes.INVOKESPECIAL, FIELD_EXCEPTION_CLASS, "<init>", "(Ljava/lang/String;)V");
|
||||
mv.visitInsn($Opcodes.ATHROW);
|
||||
|
||||
mv.visitLabel(return$Label);
|
||||
mv.visitFrame($Opcodes.F_SAME, 0, null, 0, null);
|
||||
mv.visitVarInsn($Opcodes.ALOAD, 0);
|
||||
mv.visitInsn($Opcodes.ARETURN);
|
||||
mv.visitMaxs(5, 4);
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
private void createReadMethod(ClassWriter cw, String className, List<Field> fields, String targetSignature, String targetName) {
|
||||
MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PROTECTED, "readGenerated", "(I)Ljava/lang/Object;", null,
|
||||
private void createReadMethod($ClassWriter cw, String className, List<Field> fields, String targetSignature, String targetName) {
|
||||
$MethodVisitor mv = cw.visitMethod($Opcodes.ACC_PROTECTED, "readGenerated", "(I)Ljava/lang/Object;", null,
|
||||
new String[] { "com/comphenix/protocol/reflect/FieldAccessException" });
|
||||
BoxingHelper boxingHelper = new BoxingHelper(mv);
|
||||
|
||||
String generatedClassName = PACKAGE_NAME + "/" + className;
|
||||
|
||||
mv.visitCode();
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 0);
|
||||
mv.visitFieldInsn(Opcodes.GETFIELD, generatedClassName, "typedTarget", targetSignature);
|
||||
mv.visitVarInsn(Opcodes.ASTORE, 2);
|
||||
mv.visitVarInsn(Opcodes.ILOAD, 1);
|
||||
|
||||
// The last label is for the default switch
|
||||
Label[] labels = new Label[fields.size()];
|
||||
Label errorLabel = new Label();
|
||||
|
||||
// Generate labels
|
||||
String generatedClassName = PACKAGE_NAME + "/" + className;
|
||||
|
||||
mv.visitCode();
|
||||
mv.visitVarInsn($Opcodes.ALOAD, 0);
|
||||
mv.visitFieldInsn($Opcodes.GETFIELD, generatedClassName, "typedTarget", targetSignature);
|
||||
mv.visitVarInsn($Opcodes.ASTORE, 2);
|
||||
mv.visitVarInsn($Opcodes.ILOAD, 1);
|
||||
|
||||
// The last $Label is for the default switch
|
||||
$Label[] $Labels = new $Label[fields.size()];
|
||||
$Label error$Label = new $Label();
|
||||
|
||||
// Generate $Labels
|
||||
for (int i = 0; i < fields.size(); i++) {
|
||||
labels[i] = new Label();
|
||||
$Labels[i] = new $Label();
|
||||
}
|
||||
|
||||
mv.visitTableSwitchInsn(0, fields.size() - 1, errorLabel, labels);
|
||||
|
||||
|
||||
mv.visitTableSwitchInsn(0, fields.size() - 1, error$Label, $Labels);
|
||||
|
||||
for (int i = 0; i < fields.size(); i++) {
|
||||
|
||||
|
||||
Field field = fields.get(i);
|
||||
Class<?> outputType = field.getType();
|
||||
String typeDescriptor = Type.getDescriptor(outputType);
|
||||
|
||||
mv.visitLabel(labels[i]);
|
||||
|
||||
String typeDescriptor = $Type.getDescriptor(outputType);
|
||||
|
||||
mv.visitLabel($Labels[i]);
|
||||
|
||||
// Push the compare object
|
||||
if (i == 0)
|
||||
mv.visitFrame(Opcodes.F_APPEND, 1, new Object[] { targetName }, 0, null);
|
||||
mv.visitFrame($Opcodes.F_APPEND, 1, new Object[] { targetName }, 0, null);
|
||||
else
|
||||
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
|
||||
|
||||
mv.visitFrame($Opcodes.F_SAME, 0, null, 0, null);
|
||||
|
||||
// Note that byte code cannot access non-public fields
|
||||
if (isPublic(field)) {
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 2);
|
||||
mv.visitFieldInsn(Opcodes.GETFIELD, targetName, field.getName(), typeDescriptor);
|
||||
|
||||
boxingHelper.box(Type.getType(outputType));
|
||||
mv.visitVarInsn($Opcodes.ALOAD, 2);
|
||||
mv.visitFieldInsn($Opcodes.GETFIELD, targetName, field.getName(), typeDescriptor);
|
||||
|
||||
boxingHelper.box($Type.getType(outputType));
|
||||
} else {
|
||||
// We have to use reflection for private and protected fields.
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 0);
|
||||
mv.visitVarInsn(Opcodes.ILOAD, 1);
|
||||
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, generatedClassName, "readReflected", "(I)Ljava/lang/Object;");
|
||||
mv.visitVarInsn($Opcodes.ALOAD, 0);
|
||||
mv.visitVarInsn($Opcodes.ILOAD, 1);
|
||||
mv.visitMethodInsn($Opcodes.INVOKEVIRTUAL, generatedClassName, "readReflected", "(I)Ljava/lang/Object;");
|
||||
}
|
||||
|
||||
mv.visitInsn(Opcodes.ARETURN);
|
||||
|
||||
mv.visitInsn($Opcodes.ARETURN);
|
||||
}
|
||||
|
||||
mv.visitLabel(errorLabel);
|
||||
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
|
||||
mv.visitTypeInsn(Opcodes.NEW, FIELD_EXCEPTION_CLASS);
|
||||
mv.visitInsn(Opcodes.DUP);
|
||||
mv.visitTypeInsn(Opcodes.NEW, "java/lang/StringBuilder");
|
||||
mv.visitInsn(Opcodes.DUP);
|
||||
mv.visitLabel(error$Label);
|
||||
mv.visitFrame($Opcodes.F_SAME, 0, null, 0, null);
|
||||
mv.visitTypeInsn($Opcodes.NEW, FIELD_EXCEPTION_CLASS);
|
||||
mv.visitInsn($Opcodes.DUP);
|
||||
mv.visitTypeInsn($Opcodes.NEW, "java/lang/StringBuilder");
|
||||
mv.visitInsn($Opcodes.DUP);
|
||||
mv.visitLdcInsn("Invalid index ");
|
||||
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "(Ljava/lang/String;)V");
|
||||
mv.visitVarInsn(Opcodes.ILOAD, 1);
|
||||
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(I)Ljava/lang/StringBuilder;");
|
||||
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;");
|
||||
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, FIELD_EXCEPTION_CLASS, "<init>", "(Ljava/lang/String;)V");
|
||||
mv.visitInsn(Opcodes.ATHROW);
|
||||
mv.visitMethodInsn($Opcodes.INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "(Ljava/lang/String;)V");
|
||||
mv.visitVarInsn($Opcodes.ILOAD, 1);
|
||||
mv.visitMethodInsn($Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(I)Ljava/lang/StringBuilder;");
|
||||
mv.visitMethodInsn($Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;");
|
||||
mv.visitMethodInsn($Opcodes.INVOKESPECIAL, FIELD_EXCEPTION_CLASS, "<init>", "(Ljava/lang/String;)V");
|
||||
mv.visitInsn($Opcodes.ATHROW);
|
||||
mv.visitMaxs(5, 3);
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
private void createConstructor(ClassWriter cw, String className, String targetSignature, String targetName) {
|
||||
MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>",
|
||||
private void createConstructor($ClassWriter cw, String className, String targetSignature, String targetName) {
|
||||
$MethodVisitor mv = cw.visitMethod($Opcodes.ACC_PUBLIC, "<init>",
|
||||
"(L" + SUPER_CLASS + ";L" + PACKAGE_NAME + "/StructureCompiler;)V",
|
||||
"(L" + SUPER_CLASS + "<Ljava/lang/Object;>;L" + PACKAGE_NAME + "/StructureCompiler;)V", null);
|
||||
String fullClassName = PACKAGE_NAME + "/" + className;
|
||||
|
||||
|
||||
mv.visitCode();
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 0);
|
||||
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, COMPILED_CLASS, "<init>", "()V");
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 0);
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 1);
|
||||
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, fullClassName, "initialize", "(L" + SUPER_CLASS + ";)V");
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 0);
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 1);
|
||||
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, SUPER_CLASS, "getTarget", "()Ljava/lang/Object;");
|
||||
mv.visitFieldInsn(Opcodes.PUTFIELD, fullClassName, "target", "Ljava/lang/Object;");
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 0);
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 0);
|
||||
mv.visitFieldInsn(Opcodes.GETFIELD, fullClassName, "target", "Ljava/lang/Object;");
|
||||
mv.visitTypeInsn(Opcodes.CHECKCAST, targetName);
|
||||
mv.visitFieldInsn(Opcodes.PUTFIELD, fullClassName, "typedTarget", targetSignature);
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 0);
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 2);
|
||||
mv.visitFieldInsn(Opcodes.PUTFIELD, fullClassName, "compiler", "L" + PACKAGE_NAME + "/StructureCompiler;");
|
||||
mv.visitInsn(Opcodes.RETURN);
|
||||
mv.visitVarInsn($Opcodes.ALOAD, 0);
|
||||
mv.visitMethodInsn($Opcodes.INVOKESPECIAL, COMPILED_CLASS, "<init>", "()V");
|
||||
mv.visitVarInsn($Opcodes.ALOAD, 0);
|
||||
mv.visitVarInsn($Opcodes.ALOAD, 1);
|
||||
mv.visitMethodInsn($Opcodes.INVOKEVIRTUAL, fullClassName, "initialize", "(L" + SUPER_CLASS + ";)V");
|
||||
mv.visitVarInsn($Opcodes.ALOAD, 0);
|
||||
mv.visitVarInsn($Opcodes.ALOAD, 1);
|
||||
mv.visitMethodInsn($Opcodes.INVOKEVIRTUAL, SUPER_CLASS, "getTarget", "()Ljava/lang/Object;");
|
||||
mv.visitFieldInsn($Opcodes.PUTFIELD, fullClassName, "target", "Ljava/lang/Object;");
|
||||
mv.visitVarInsn($Opcodes.ALOAD, 0);
|
||||
mv.visitVarInsn($Opcodes.ALOAD, 0);
|
||||
mv.visitFieldInsn($Opcodes.GETFIELD, fullClassName, "target", "Ljava/lang/Object;");
|
||||
mv.visitTypeInsn($Opcodes.CHECKCAST, targetName);
|
||||
mv.visitFieldInsn($Opcodes.PUTFIELD, fullClassName, "typedTarget", targetSignature);
|
||||
mv.visitVarInsn($Opcodes.ALOAD, 0);
|
||||
mv.visitVarInsn($Opcodes.ALOAD, 2);
|
||||
mv.visitFieldInsn($Opcodes.PUTFIELD, fullClassName, "compiler", "L" + PACKAGE_NAME + "/StructureCompiler;");
|
||||
mv.visitInsn($Opcodes.RETURN);
|
||||
mv.visitMaxs(2, 3);
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
@ -4,25 +4,24 @@ import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import net.sf.cglib.asm.ClassReader;
|
||||
import net.sf.cglib.asm.MethodVisitor;
|
||||
import net.sf.cglib.asm.Opcodes;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.reflect.accessors.Accessors;
|
||||
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
|
||||
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
|
||||
import com.comphenix.protocol.utility.EnhancerFactory;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import net.sf.cglib.asm.$ClassReader;
|
||||
import net.sf.cglib.asm.$ClassVisitor;
|
||||
import net.sf.cglib.asm.$MethodVisitor;
|
||||
import net.sf.cglib.asm.$Opcodes;
|
||||
import net.sf.cglib.proxy.Enhancer;
|
||||
import net.sf.cglib.proxy.MethodInterceptor;
|
||||
import net.sf.cglib.proxy.MethodProxy;
|
||||
|
||||
import org.bukkit.block.BlockState;
|
||||
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.reflect.accessors.Accessors;
|
||||
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
|
||||
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
|
||||
import com.comphenix.protocol.reflect.compiler.EmptyClassVisitor;
|
||||
import com.comphenix.protocol.reflect.compiler.EmptyMethodVisitor;
|
||||
import com.comphenix.protocol.utility.EnhancerFactory;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
/**
|
||||
* Manipulate tile entities.
|
||||
* @author Kristian
|
||||
@ -90,26 +89,26 @@ class TileEntityAccessor<T extends BlockState> {
|
||||
private void findMethodsUsingASM() throws IOException {
|
||||
final Class<?> nbtCompoundClass = MinecraftReflection.getNBTCompoundClass();
|
||||
final Class<?> tileEntityClass = MinecraftReflection.getTileEntityClass();
|
||||
final ClassReader reader = new ClassReader(tileEntityClass.getCanonicalName());
|
||||
final $ClassReader reader = new $ClassReader(tileEntityClass.getCanonicalName());
|
||||
|
||||
final String tagCompoundName = getJarName(MinecraftReflection.getNBTCompoundClass());
|
||||
final String expectedDesc = "(L" + tagCompoundName + ";)";
|
||||
|
||||
reader.accept(new EmptyClassVisitor() {
|
||||
reader.accept(new $ClassVisitor($Opcodes.ASM5) {
|
||||
@Override
|
||||
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
|
||||
public $MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
|
||||
final String methodName = name;
|
||||
|
||||
// Detect read/write calls to NBTTagCompound
|
||||
if (desc.startsWith(expectedDesc)) {
|
||||
return new EmptyMethodVisitor() {
|
||||
return new $MethodVisitor($Opcodes.ASM5) {
|
||||
private int readMethods;
|
||||
private int writeMethods;
|
||||
|
||||
@Override
|
||||
public void visitMethodInsn(int opcode, String owner, String name, String desc) {
|
||||
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean intf) {
|
||||
// This must be a virtual call on NBTTagCompound that accepts a String
|
||||
if (opcode == Opcodes.INVOKEVIRTUAL
|
||||
if (opcode == $Opcodes.INVOKEVIRTUAL
|
||||
&& tagCompoundName.equals(owner)
|
||||
&& desc.startsWith("(Ljava/lang/String")) {
|
||||
|
||||
|
@ -20,6 +20,8 @@
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.build.number></project.build.number>
|
||||
<project.fullVersion>${project.version}</project.fullVersion>
|
||||
|
||||
<powermock.version>1.7.0RC4</powermock.version>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
@ -238,25 +240,25 @@
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.10</version>
|
||||
<version>4.12</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-all</artifactId>
|
||||
<version>1.8.4</version>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<version>2.8.9</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.powermock</groupId>
|
||||
<artifactId>powermock-module-junit4</artifactId>
|
||||
<version>1.5</version>
|
||||
<version>${powermock.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.powermock</groupId>
|
||||
<artifactId>powermock-api-mockito</artifactId>
|
||||
<version>1.5</version>
|
||||
<artifactId>powermock-api-mockito2</artifactId>
|
||||
<version>${powermock.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
@ -1,25 +0,0 @@
|
||||
package com.comphenix.protocol.wrappers;
|
||||
|
||||
import org.junit.runner.RunWith;
|
||||
import org.powermock.core.classloader.annotations.PowerMockIgnore;
|
||||
|
||||
import com.comphenix.protocol.BukkitInitialization;
|
||||
|
||||
@RunWith(org.powermock.modules.junit4.PowerMockRunner.class)
|
||||
@PowerMockIgnore({ "org.apache.log4j.*", "org.apache.logging.*", "org.bukkit.craftbukkit.libs.jline.*" })
|
||||
//@PrepareForTest(CraftItemFactory.class)
|
||||
public class WrappedWatchableObjectTest {
|
||||
//@BeforeClass
|
||||
public static void initializeBukkit() throws IllegalAccessException {
|
||||
BukkitInitialization.initializeItemMeta();
|
||||
}
|
||||
|
||||
//@Test
|
||||
/* public void testItemStack() {
|
||||
final ItemStack stack = new ItemStack(Material.GOLD_AXE);
|
||||
final WrappedWatchableObject test = new WrappedWatchableObject(0, stack);
|
||||
|
||||
ItemStack value = (ItemStack) test.getValue();
|
||||
assertEquals(value.getType(), stack.getType());
|
||||
} */
|
||||
}
|
4
pom.xml
4
pom.xml
@ -8,7 +8,7 @@
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<minorVersion>4.3.0</minorVersion>
|
||||
<minorVersion>4.3.1-SNAPSHOT</minorVersion>
|
||||
<spigotVersion>1.12-R0.1-SNAPSHOT</spigotVersion>
|
||||
</properties>
|
||||
|
||||
@ -67,7 +67,7 @@
|
||||
<dependency>
|
||||
<groupId>cglib</groupId>
|
||||
<artifactId>cglib-nodep</artifactId>
|
||||
<version>2.2.2</version>
|
||||
<version>3.2.5</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
In neuem Issue referenzieren
Einen Benutzer sperren