Update dependency versions to hopefully work with Java 9

This commit is contained in:
Dan Mulloy 2017-06-14 15:41:00 -04:00
parent adb3c5392c
commit 20d78832b0
10 changed files with 317 additions and 536 deletions

View File

@ -4,16 +4,11 @@ import java.io.IOException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.List; 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.ClassAnalyser.AsmMethod.AsmOpcodes;
import com.comphenix.protocol.reflect.compiler.EmptyClassVisitor;
import com.comphenix.protocol.reflect.compiler.EmptyMethodVisitor;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import net.sf.cglib.asm.*;
public class ClassAnalyser { public class ClassAnalyser {
/** /**
* Represents a method in ASM. * Represents a method in ASM.
@ -31,11 +26,11 @@ public class ClassAnalyser {
public static AsmOpcodes fromIntOpcode(int opcode) { public static AsmOpcodes fromIntOpcode(int opcode) {
switch (opcode) { switch (opcode) {
case Opcodes.INVOKEVIRTUAL: return AsmOpcodes.INVOKE_VIRTUAL; case $Opcodes.INVOKEVIRTUAL: return AsmOpcodes.INVOKE_VIRTUAL;
case Opcodes.INVOKESPECIAL: return AsmOpcodes.INVOKE_SPECIAL; case $Opcodes.INVOKESPECIAL: return AsmOpcodes.INVOKE_SPECIAL;
case Opcodes.INVOKESTATIC: return AsmOpcodes.INVOKE_STATIC; case $Opcodes.INVOKESTATIC: return AsmOpcodes.INVOKE_STATIC;
case Opcodes.INVOKEINTERFACE: return AsmOpcodes.INVOKE_INTERFACE; case $Opcodes.INVOKEINTERFACE: return AsmOpcodes.INVOKE_INTERFACE;
case Opcodes.INVOKEDYNAMIC: return AsmOpcodes.INVOKE_DYNAMIC; case $Opcodes.INVOKEDYNAMIC: return AsmOpcodes.INVOKE_DYNAMIC;
default: throw new IllegalArgumentException("Unknown opcode: " + opcode); default: throw new IllegalArgumentException("Unknown opcode: " + opcode);
} }
} }
@ -109,30 +104,29 @@ public class ClassAnalyser {
* @return The method calls. * @return The method calls.
* @throws IOException Cannot access the parent class. * @throws IOException Cannot access the parent class.
*/ */
public List<AsmMethod> getMethodCalls(Class<?> clazz, Method method) throws IOException { private List<AsmMethod> getMethodCalls(Class<?> clazz, Method method) throws IOException {
final ClassReader reader = new ClassReader(clazz.getCanonicalName()); final $ClassReader reader = new $ClassReader(clazz.getCanonicalName());
final List<AsmMethod> output = Lists.newArrayList(); final List<AsmMethod> output = Lists.newArrayList();
// The method we are looking for // The method we are looking for
final String methodName = method.getName(); final String methodName = method.getName();
final String methodDescription = Type.getMethodDescriptor(method); final String methodDescription = $Type.getMethodDescriptor(method);
reader.accept(new EmptyClassVisitor() { reader.accept(new $ClassVisitor($Opcodes.ASM5) {
@Override @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) {
// Check method
if (methodName.equals(name) && methodDescription.equals(desc)) { if (methodName.equals(name) && methodDescription.equals(desc)) {
return new EmptyMethodVisitor() { return new $MethodVisitor($Opcodes.ASM5) {
@Override @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)); output.add(new AsmMethod(AsmOpcodes.fromIntOpcode(opcode), owner, methodName, desc));
} }
}; };
} }
return null; return null;
} }
}, $ClassReader.EXPAND_FRAMES);
}, ClassReader.EXPAND_FRAMES);
return output; return output;
} }
} }

View File

@ -17,25 +17,25 @@
package com.comphenix.protocol.reflect.compiler; package com.comphenix.protocol.reflect.compiler;
import net.sf.cglib.asm.MethodVisitor; import net.sf.cglib.asm.$MethodVisitor;
import net.sf.cglib.asm.Opcodes; import net.sf.cglib.asm.$Opcodes;
import net.sf.cglib.asm.Type; import net.sf.cglib.asm.$Type;
/** /**
* Used by the compiler to automatically box and unbox values. * Used by the compiler to automatically box and unbox values.
*/ */
class BoxingHelper { class BoxingHelper {
private final static Type BYTE_TYPE = Type.getObjectType("java/lang/Byte"); 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 BOOLEAN_$Type = $Type.getObjectType("java/lang/Boolean");
private final static Type SHORT_TYPE = Type.getObjectType("java/lang/Short"); 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 CHARACTER_$Type = $Type.getObjectType("java/lang/Character");
private final static Type INTEGER_TYPE = Type.getObjectType("java/lang/Integer"); 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 FLOAT_$Type = $Type.getObjectType("java/lang/Float");
private final static Type LONG_TYPE = Type.getObjectType("java/lang/Long"); 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 DOUBLE_$Type = $Type.getObjectType("java/lang/Double");
private final static Type NUMBER_TYPE = Type.getObjectType("java/lang/Number"); 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 OBJECT_$Type = $Type.getObjectType("java/lang/Object");
private final static MethodDescriptor BOOLEAN_VALUE = MethodDescriptor.getMethod("boolean booleanValue()"); private final static MethodDescriptor BOOLEAN_VALUE = MethodDescriptor.getMethod("boolean booleanValue()");
private final static MethodDescriptor CHAR_VALUE = MethodDescriptor.getMethod("char charValue()"); 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 LONG_VALUE = MethodDescriptor.getMethod("long longValue()");
private final static MethodDescriptor DOUBLE_VALUE = MethodDescriptor.getMethod("double doubleValue()"); 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; this.mv = mv;
} }
@ -54,42 +54,42 @@ class BoxingHelper {
* Generates the instructions to box the top stack value. This value is * Generates the instructions to box the top stack value. This value is
* replaced by its boxed equivalent on top of the stack. * 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){ public void box(final $Type type){
if(type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) { if(type.getSort() == $Type.OBJECT || type.getSort() == $Type.ARRAY) {
return; return;
} }
if(type == Type.VOID_TYPE) { if(type == $Type.VOID_TYPE) {
push((String) null); push((String) null);
} else { } else {
Type boxed = type; $Type boxed = type;
switch(type.getSort()) { switch(type.getSort()) {
case Type.BYTE: case $Type.BYTE:
boxed = BYTE_TYPE; boxed = BYTE_$Type;
break; break;
case Type.BOOLEAN: case $Type.BOOLEAN:
boxed = BOOLEAN_TYPE; boxed = BOOLEAN_$Type;
break; break;
case Type.SHORT: case $Type.SHORT:
boxed = SHORT_TYPE; boxed = SHORT_$Type;
break; break;
case Type.CHAR: case $Type.CHAR:
boxed = CHARACTER_TYPE; boxed = CHARACTER_$Type;
break; break;
case Type.INT: case $Type.INT:
boxed = INTEGER_TYPE; boxed = INTEGER_$Type;
break; break;
case Type.FLOAT: case $Type.FLOAT:
boxed = FLOAT_TYPE; boxed = FLOAT_$Type;
break; break;
case Type.LONG: case $Type.LONG:
boxed = LONG_TYPE; boxed = LONG_$Type;
break; break;
case Type.DOUBLE: case $Type.DOUBLE:
boxed = DOUBLE_TYPE; boxed = DOUBLE_$Type;
break; break;
} }
@ -105,46 +105,46 @@ class BoxingHelper {
swap(); 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. * 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. * @param method the constructor to be invoked.
*/ */
public void invokeConstructor(final Type type, final MethodDescriptor method){ public void invokeConstructor(final $Type $Type, final MethodDescriptor method){
invokeInsn(Opcodes.INVOKESPECIAL, type, method); invokeInsn($Opcodes.INVOKESPECIAL, $Type, method);
} }
/** /**
* Generates a DUP_X1 instruction. * Generates a DUP_X1 instruction.
*/ */
public void dupX1(){ public void dupX1(){
mv.visitInsn(Opcodes.DUP_X1); mv.visitInsn($Opcodes.DUP_X1);
} }
/** /**
* Generates a DUP_X2 instruction. * Generates a DUP_X2 instruction.
*/ */
public void dupX2(){ public void dupX2(){
mv.visitInsn(Opcodes.DUP_X2); mv.visitInsn($Opcodes.DUP_X2);
} }
/** /**
* Generates a POP instruction. * Generates a POP instruction.
*/ */
public void pop(){ public void pop(){
mv.visitInsn(Opcodes.POP); mv.visitInsn($Opcodes.POP);
} }
/** /**
* Generates a SWAP instruction. * Generates a SWAP instruction.
*/ */
public void swap(){ public void swap(){
mv.visitInsn(Opcodes.SWAP); mv.visitInsn($Opcodes.SWAP);
} }
/** /**
@ -163,11 +163,11 @@ class BoxingHelper {
*/ */
public void push(final int value) { public void push(final int value) {
if (value >= -1 && value <= 5) { 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) { } 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) { } else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) {
mv.visitIntInsn(Opcodes.SIPUSH, value); mv.visitIntInsn($Opcodes.SIPUSH, value);
} else { } else {
mv.visitLdcInsn(new Integer(value)); mv.visitLdcInsn(new Integer(value));
} }
@ -176,10 +176,10 @@ class BoxingHelper {
/** /**
* Generates the instruction to create a new object. * 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){ public void newInstance(final $Type $Type){
typeInsn(Opcodes.NEW, type); $TypeInsn($Opcodes.NEW, $Type);
} }
/** /**
@ -189,7 +189,7 @@ class BoxingHelper {
*/ */
public void push(final String value) { public void push(final String value) {
if (value == null) { if (value == null) {
mv.visitInsn(Opcodes.ACONST_NULL); mv.visitInsn($Opcodes.ACONST_NULL);
} else { } else {
mv.visitLdcInsn(value); mv.visitLdcInsn(value);
} }
@ -200,35 +200,35 @@ class BoxingHelper {
* replaced by its unboxed equivalent on top of the stack. * replaced by its unboxed equivalent on top of the stack.
* *
* @param type * @param type
* the type of the top stack value. * the $Type of the top stack value.
*/ */
public void unbox(final Type type){ public void unbox(final $Type type){
Type t = NUMBER_TYPE; $Type t = NUMBER_$Type;
MethodDescriptor sig = null; MethodDescriptor sig = null;
switch(type.getSort()) { switch(type.getSort()) {
case Type.VOID: case $Type.VOID:
return; return;
case Type.CHAR: case $Type.CHAR:
t = CHARACTER_TYPE; t = CHARACTER_$Type;
sig = CHAR_VALUE; sig = CHAR_VALUE;
break; break;
case Type.BOOLEAN: case $Type.BOOLEAN:
t = BOOLEAN_TYPE; t = BOOLEAN_$Type;
sig = BOOLEAN_VALUE; sig = BOOLEAN_VALUE;
break; break;
case Type.DOUBLE: case $Type.DOUBLE:
sig = DOUBLE_VALUE; sig = DOUBLE_VALUE;
break; break;
case Type.FLOAT: case $Type.FLOAT:
sig = FLOAT_VALUE; sig = FLOAT_VALUE;
break; break;
case Type.LONG: case $Type.LONG:
sig = LONG_VALUE; sig = LONG_VALUE;
break; break;
case Type.INT: case $Type.INT:
case Type.SHORT: case $Type.SHORT:
case Type.BYTE: case $Type.BYTE:
sig = INT_VALUE; sig = INT_VALUE;
} }
@ -242,13 +242,13 @@ class BoxingHelper {
/** /**
* Generates the instruction to check that the top stack value is of the * 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){ public void checkCast(final $Type $Type){
if(!type.equals(OBJECT_TYPE)) { if(!$Type.equals(OBJECT_$Type)) {
typeInsn(Opcodes.CHECKCAST, type); $TypeInsn($Opcodes.CHECKCAST, $Type);
} }
} }
@ -258,35 +258,35 @@ class BoxingHelper {
* @param owner the class in which the method is defined. * @param owner the class in which the method is defined.
* @param method the method to be invoked. * @param method the method to be invoked.
*/ */
public void invokeVirtual(final Type owner, final MethodDescriptor method){ public void invokeVirtual(final $Type owner, final MethodDescriptor method){
invokeInsn(Opcodes.INVOKEVIRTUAL, owner, method); invokeInsn($Opcodes.INVOKEVIRTUAL, owner, method);
} }
/** /**
* Generates an invoke method instruction. * Generates an invoke method instruction.
* *
* @param opcode the instruction's opcode. * @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. * @param method the method to be invoked.
*/ */
private void invokeInsn(final int opcode, final Type type, final MethodDescriptor method){ private void invokeInsn(final int opcode, final $Type $Type, final MethodDescriptor method){
String owner = type.getSort() == Type.ARRAY ? type.getDescriptor() : type.getInternalName(); String owner = $Type.getSort() == $Type.ARRAY ? $Type.getDescriptor() : $Type.getInternalName();
mv.visitMethodInsn(opcode, owner, method.getName(), method.getDescriptor()); 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 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; String desc;
if(type.getSort() == Type.ARRAY) { if($Type.getSort() == $Type.ARRAY) {
desc = type.getDescriptor(); desc = $Type.getDescriptor();
} else { } else {
desc = type.getInternalName(); desc = $Type.getInternalName();
} }
mv.visitTypeInsn(opcode, desc); mv.visitTypeInsn(opcode, desc);

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -20,7 +20,7 @@ package com.comphenix.protocol.reflect.compiler;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import net.sf.cglib.asm.Type; import net.sf.cglib.asm.$Type;
/** /**
* Represents a method. * 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 name the method's name.
* @param desc the method's descriptor. * @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 name the method's name.
* @param returnType the method's return type. * @param returnType the method's return type.
@ -75,14 +75,14 @@ class MethodDescriptor {
*/ */
public MethodDescriptor( public MethodDescriptor(
final String name, final String name,
final Type returnType, final $Type returnType,
final Type[] argumentTypes) 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. * declaration.
* *
* @param method a Java method declaration, without argument names, of the * @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 * "java.util.List", ...). Classes of the java.lang package can be
* specified by their unqualified name; all other classes names must * specified by their unqualified name; all other classes names must
* be fully qualified. * be fully qualified.
* @return a {@link Method} corresponding to the given Java method * @return a {@link MethodDescriptor} corresponding to the given Java method
* declaration. * declaration.
* @throws IllegalArgumentException if <code>method</code> could not get * @throws IllegalArgumentException if <code>method</code> could not get
* parsed. * 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. * declaration.
* *
* @param method a Java method declaration, without argument names, of the * @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. * default package, or false if they correspond to java.lang classes.
* For instance "Object" means "Object" if this option is true, or * For instance "Object" means "Object" if this option is true, or
* "java.lang.Object" otherwise. * "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. * declaration.
* @throws IllegalArgumentException if <code>method</code> could not get * @throws IllegalArgumentException if <code>method</code> could not get
* parsed. * parsed.
@ -134,7 +134,7 @@ class MethodDescriptor {
} }
String returnType = method.substring(0, space); String returnType = method.substring(0, space);
String methodName = method.substring(space + 1, start - 1).trim(); String methodName = method.substring(space + 1, start - 1).trim();
StringBuffer sb = new StringBuffer(); StringBuilder sb = new StringBuilder();
sb.append('('); sb.append('(');
int p; int p;
do { do {
@ -158,7 +158,7 @@ class MethodDescriptor {
return type; return type;
} }
StringBuffer sb = new StringBuffer(); StringBuilder sb = new StringBuilder();
int index = 0; int index = 0;
while ((index = type.indexOf("[]", index) + 1) > 0) { while ((index = type.indexOf("[]", index) + 1) > 0) {
sb.append('['); sb.append('[');
@ -206,8 +206,8 @@ class MethodDescriptor {
* *
* @return the return type of the method described by this object. * @return the return type of the method described by this object.
*/ */
public Type getReturnType() { public $Type getReturnType() {
return Type.getReturnType(desc); return $Type.getReturnType(desc);
} }
/** /**
@ -215,8 +215,8 @@ class MethodDescriptor {
* *
* @return the argument types of the method described by this object. * @return the argument types of the method described by this object.
*/ */
public Type[] getArgumentTypes() { public $Type[] getArgumentTypes() {
return Type.getArgumentTypes(desc); return $Type.getArgumentTypes(desc);
} }
public String toString() { public String toString() {

View File

@ -25,12 +25,12 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import net.sf.cglib.asm.ClassWriter; import net.sf.cglib.asm.$ClassWriter;
import net.sf.cglib.asm.FieldVisitor; import net.sf.cglib.asm.$FieldVisitor;
import net.sf.cglib.asm.Label; import net.sf.cglib.asm.$Label;
import net.sf.cglib.asm.MethodVisitor; import net.sf.cglib.asm.$MethodVisitor;
import net.sf.cglib.asm.Opcodes; import net.sf.cglib.asm.$Opcodes;
import net.sf.cglib.asm.Type; import net.sf.cglib.asm.$Type;
import com.comphenix.protocol.ProtocolLibrary; import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.error.Report; import com.comphenix.protocol.error.Report;
@ -95,22 +95,22 @@ import com.google.common.primitives.Primitives;
/** /**
* Represents a StructureModifier compiler. * Represents a StructureModifier compiler.
* *
* @author Kristian * @author Kristian
*/ */
public final class StructureCompiler { public final class StructureCompiler {
public static final ReportType REPORT_TOO_MANY_GENERATED_CLASSES = new ReportType("Generated too many classes (count: %s)"); 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 // Used to store generated classes of different types
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
static class StructureKey { static class StructureKey {
private Class targetType; private Class targetType;
private Class fieldType; private Class fieldType;
public StructureKey(StructureModifier<?> source) { public StructureKey(StructureModifier<?> source) {
this(source.getTargetType(), source.getFieldType()); this(source.getTargetType(), source.getFieldType());
} }
public StructureKey(Class targetType, Class fieldType) { public StructureKey(Class targetType, Class fieldType) {
this.targetType = targetType; this.targetType = targetType;
this.fieldType = fieldType; this.fieldType = fieldType;
@ -120,7 +120,7 @@ public final class StructureCompiler {
public int hashCode() { public int hashCode() {
return Objects.hashCode(targetType, fieldType); return Objects.hashCode(targetType, fieldType);
} }
@Override @Override
public boolean equals(Object obj) { public boolean equals(Object obj) {
if (obj instanceof StructureKey) { if (obj instanceof StructureKey) {
@ -131,16 +131,16 @@ public final class StructureCompiler {
return false; return false;
} }
} }
// Used to load classes // Used to load classes
private volatile static Method defineMethod; private volatile static Method defineMethod;
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
private Map<StructureKey, Class> compiledCache = new ConcurrentHashMap<StructureKey, Class>(); private Map<StructureKey, Class> compiledCache = new ConcurrentHashMap<StructureKey, Class>();
// The class loader we'll store our classes // The class loader we'll store our classes
private ClassLoader loader; private ClassLoader loader;
// References to other classes // References to other classes
private static String PACKAGE_NAME = "com/comphenix/protocol/reflect/compiler"; private static String PACKAGE_NAME = "com/comphenix/protocol/reflect/compiler";
private static String SUPER_CLASS = "com/comphenix/protocol/reflect/StructureModifier"; private static String SUPER_CLASS = "com/comphenix/protocol/reflect/StructureModifier";
@ -156,7 +156,7 @@ public final class StructureCompiler {
StructureCompiler(ClassLoader loader) { StructureCompiler(ClassLoader loader) {
this.loader = loader; this.loader = loader;
} }
/** /**
* Lookup the current class loader for any previously generated classes before we attempt to generate something. * Lookup the current class loader for any previously generated classes before we attempt to generate something.
* @param <TField> Type * @param <TField> Type
@ -194,7 +194,7 @@ public final class StructureCompiler {
// We need to compile the class // We need to compile the class
return false; return false;
} }
/** /**
* Compiles the given structure modifier. * Compiles the given structure modifier.
* <p> * <p>
@ -211,15 +211,15 @@ public final class StructureCompiler {
if (!isAnyPublic(source.getFields())) { if (!isAnyPublic(source.getFields())) {
return source; return source;
} }
StructureKey key = new StructureKey(source); StructureKey key = new StructureKey(source);
Class<?> compiledClass = compiledCache.get(key); Class<?> compiledClass = compiledCache.get(key);
if (!compiledCache.containsKey(key)) { if (!compiledCache.containsKey(key)) {
compiledClass = generateClass(source); compiledClass = generateClass(source);
compiledCache.put(key, compiledClass); compiledCache.put(key, compiledClass);
} }
// Next, create an instance of this class // Next, create an instance of this class
try { try {
return (StructureModifier<TField>) compiledClass.getConstructor( return (StructureModifier<TField>) compiledClass.getConstructor(
@ -245,7 +245,7 @@ public final class StructureCompiler {
throw new IllegalStateException("Cannot happen.", e); throw new IllegalStateException("Cannot happen.", e);
} }
} }
/** /**
* Retrieve a variable identifier that can uniquely represent the given type. * Retrieve a variable identifier that can uniquely represent the given type.
* @param type - a type. * @param type - a type.
@ -254,7 +254,7 @@ public final class StructureCompiler {
private String getSafeTypeName(Class<?> type) { private String getSafeTypeName(Class<?> type) {
return type.getCanonicalName().replace("[]", "Array").replace(".", "_"); return type.getCanonicalName().replace("[]", "Array").replace(".", "_");
} }
/** /**
* Retrieve the compiled name of a given structure modifier. * Retrieve the compiled name of a given structure modifier.
* @param source - the structure modifier. * @param source - the structure modifier.
@ -262,29 +262,29 @@ public final class StructureCompiler {
*/ */
private String getCompiledName(StructureModifier<?> source) { private String getCompiledName(StructureModifier<?> source) {
Class<?> targetType = source.getTargetType(); Class<?> targetType = source.getTargetType();
// Concat class and field type // Concat class and field type
return "CompiledStructure$" + return "CompiledStructure$" +
getSafeTypeName(targetType) + "$" + getSafeTypeName(targetType) + "$" +
getSafeTypeName(source.getFieldType()); getSafeTypeName(source.getFieldType());
} }
/** /**
* Compile a structure modifier. * Compile a structure modifier.
* @param source - structure modifier. * @param source - structure modifier.
* @return The compiled structure modifier. * @return The compiled structure modifier.
*/ */
private <TField> Class<?> generateClass(StructureModifier<TField> source) { private <TField> Class<?> generateClass(StructureModifier<TField> source) {
ClassWriter cw = new ClassWriter(0); $ClassWriter cw = new $ClassWriter(0);
Class<?> targetType = source.getTargetType(); Class<?> targetType = source.getTargetType();
String className = getCompiledName(source); String className = getCompiledName(source);
String targetSignature = Type.getDescriptor(targetType); String targetSignature = $Type.getDescriptor(targetType);
String targetName = targetType.getName().replace('.', '/'); String targetName = targetType.getName().replace('.', '/');
// Define class // 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); null, COMPILED_CLASS, null);
createFields(cw, targetSignature); createFields(cw, targetSignature);
@ -292,29 +292,29 @@ public final class StructureCompiler {
createReadMethod(cw, className, source.getFields(), targetSignature, targetName); createReadMethod(cw, className, source.getFields(), targetSignature, targetName);
createWriteMethod(cw, className, source.getFields(), targetSignature, targetName); createWriteMethod(cw, className, source.getFields(), targetSignature, targetName);
cw.visitEnd(); cw.visitEnd();
byte[] data = cw.toByteArray(); byte[] data = cw.toByteArray();
// Call the define method // Call the define method
try { try {
if (defineMethod == null) { if (defineMethod == null) {
Method defined = ClassLoader.class.getDeclaredMethod("defineClass", Method defined = ClassLoader.class.getDeclaredMethod("defineClass",
new Class<?>[] { String.class, byte[].class, int.class, int.class }); new Class<?>[] { String.class, byte[].class, int.class, int.class });
// Awesome. Now, create and return it. // Awesome. Now, create and return it.
defined.setAccessible(true); defined.setAccessible(true);
defineMethod = defined; defineMethod = defined;
} }
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
Class clazz = (Class) defineMethod.invoke(loader, null, data, 0, data.length); Class clazz = (Class) defineMethod.invoke(loader, null, data, 0, data.length);
// DEBUG CODE: Print the content of the generated class. // DEBUG CODE: Print the content of the generated class.
//org.objectweb.asm.ClassReader cr = new org.objectweb.asm.ClassReader(data); //org.objectweb.asm.ClassReader cr = new org.objectweb.asm.ClassReader(data);
//cr.accept(new ASMifierClassVisitor(new PrintWriter(System.out)), 0); //cr.accept(new ASMifierClassVisitor(new PrintWriter(System.out)), 0);
return clazz; return clazz;
} catch (SecurityException e) { } catch (SecurityException e) {
throw new RuntimeException("Cannot use reflection to dynamically load a class.", e); throw new RuntimeException("Cannot use reflection to dynamically load a class.", e);
} catch (NoSuchMethodException e) { } catch (NoSuchMethodException e) {
@ -327,7 +327,7 @@ public final class StructureCompiler {
throw new RuntimeException("Error occured in code generator.", e); throw new RuntimeException("Error occured in code generator.", e);
} }
} }
/** /**
* Determine if at least one of the given fields is public. * Determine if at least one of the given fields is public.
* @param fields - field to test. * @param fields - field to test.
@ -340,208 +340,208 @@ public final class StructureCompiler {
return true; return true;
} }
} }
return false; return false;
} }
private boolean isPublic(Field field) { private boolean isPublic(Field field) {
return Modifier.isPublic(field.getModifiers()); return Modifier.isPublic(field.getModifiers());
} }
private boolean isNonFinal(Field field) { private boolean isNonFinal(Field field) {
return !Modifier.isFinal(field.getModifiers()); return !Modifier.isFinal(field.getModifiers());
} }
private void createFields(ClassWriter cw, String targetSignature) { private void createFields($ClassWriter cw, String targetSignature) {
FieldVisitor typedField = cw.visitField(Opcodes.ACC_PRIVATE, "typedTarget", targetSignature, null, null); $FieldVisitor typedField = cw.visitField($Opcodes.ACC_PRIVATE, "typedTarget", targetSignature, null, null);
typedField.visitEnd(); 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 methodDescriptor = "(ILjava/lang/Object;)L" + SUPER_CLASS + ";";
String methodSignature = "(ILjava/lang/Object;)L" + SUPER_CLASS + "<Ljava/lang/Object;>;"; 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 }); new String[] { FIELD_EXCEPTION_CLASS });
BoxingHelper boxingHelper = new BoxingHelper(mv); 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 String generatedClassName = PACKAGE_NAME + "/" + className;
Label[] labels = new Label[fields.size()];
Label errorLabel = new Label(); mv.visitCode();
Label returnLabel = new Label(); mv.visitVarInsn($Opcodes.ALOAD, 0);
mv.visitFieldInsn($Opcodes.GETFIELD, generatedClassName, "typedTarget", targetSignature);
// Generate labels 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++) { 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++) { for (int i = 0; i < fields.size(); i++) {
Field field = fields.get(i); Field field = fields.get(i);
Class<?> outputType = field.getType(); Class<?> outputType = field.getType();
Class<?> inputType = Primitives.wrap(outputType); Class<?> inputType = Primitives.wrap(outputType);
String typeDescriptor = Type.getDescriptor(outputType); String typeDescriptor = $Type.getDescriptor(outputType);
String inputPath = inputType.getName().replace('.', '/'); String inputPath = inputType.getName().replace('.', '/');
mv.visitLabel(labels[i]); mv.visitLabel($Labels[i]);
// Push the compare object // Push the compare object
if (i == 0) 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 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 // Only write to public non-final fields
if (isPublic(field) && isNonFinal(field)) { if (isPublic(field) && isNonFinal(field)) {
mv.visitVarInsn(Opcodes.ALOAD, 3); mv.visitVarInsn($Opcodes.ALOAD, 3);
mv.visitVarInsn(Opcodes.ALOAD, 2); mv.visitVarInsn($Opcodes.ALOAD, 2);
if (!outputType.isPrimitive()) if (!outputType.isPrimitive())
mv.visitTypeInsn(Opcodes.CHECKCAST, inputPath); mv.visitTypeInsn($Opcodes.CHECKCAST, inputPath);
else else
boxingHelper.unbox(Type.getType(outputType)); boxingHelper.unbox($Type.getType(outputType));
mv.visitFieldInsn(Opcodes.PUTFIELD, targetName, field.getName(), typeDescriptor); mv.visitFieldInsn($Opcodes.PUTFIELD, targetName, field.getName(), typeDescriptor);
} else { } else {
// Use reflection. We don't have a choice, unfortunately. // Use reflection. We don't have a choice, unfortunately.
mv.visitVarInsn(Opcodes.ALOAD, 0); mv.visitVarInsn($Opcodes.ALOAD, 0);
mv.visitVarInsn(Opcodes.ILOAD, 1); mv.visitVarInsn($Opcodes.ILOAD, 1);
mv.visitVarInsn(Opcodes.ALOAD, 2); mv.visitVarInsn($Opcodes.ALOAD, 2);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, generatedClassName, "writeReflected", "(ILjava/lang/Object;)V"); 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.visitLabel(error$Label);
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); mv.visitFrame($Opcodes.F_SAME, 0, null, 0, null);
mv.visitTypeInsn(Opcodes.NEW, FIELD_EXCEPTION_CLASS); mv.visitTypeInsn($Opcodes.NEW, FIELD_EXCEPTION_CLASS);
mv.visitInsn(Opcodes.DUP); mv.visitInsn($Opcodes.DUP);
mv.visitTypeInsn(Opcodes.NEW, "java/lang/StringBuilder"); mv.visitTypeInsn($Opcodes.NEW, "java/lang/StringBuilder");
mv.visitInsn(Opcodes.DUP); mv.visitInsn($Opcodes.DUP);
mv.visitLdcInsn("Invalid index "); mv.visitLdcInsn("Invalid index ");
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "(Ljava/lang/String;)V"); mv.visitMethodInsn($Opcodes.INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "(Ljava/lang/String;)V");
mv.visitVarInsn(Opcodes.ILOAD, 1); mv.visitVarInsn($Opcodes.ILOAD, 1);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(I)Ljava/lang/StringBuilder;"); 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.INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;");
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, FIELD_EXCEPTION_CLASS, "<init>", "(Ljava/lang/String;)V"); mv.visitMethodInsn($Opcodes.INVOKESPECIAL, FIELD_EXCEPTION_CLASS, "<init>", "(Ljava/lang/String;)V");
mv.visitInsn(Opcodes.ATHROW); mv.visitInsn($Opcodes.ATHROW);
mv.visitLabel(returnLabel); mv.visitLabel(return$Label);
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); mv.visitFrame($Opcodes.F_SAME, 0, null, 0, null);
mv.visitVarInsn(Opcodes.ALOAD, 0); mv.visitVarInsn($Opcodes.ALOAD, 0);
mv.visitInsn(Opcodes.ARETURN); mv.visitInsn($Opcodes.ARETURN);
mv.visitMaxs(5, 4); mv.visitMaxs(5, 4);
mv.visitEnd(); mv.visitEnd();
} }
private void createReadMethod(ClassWriter cw, String className, List<Field> fields, String targetSignature, String targetName) { 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, $MethodVisitor mv = cw.visitMethod($Opcodes.ACC_PROTECTED, "readGenerated", "(I)Ljava/lang/Object;", null,
new String[] { "com/comphenix/protocol/reflect/FieldAccessException" }); new String[] { "com/comphenix/protocol/reflect/FieldAccessException" });
BoxingHelper boxingHelper = new BoxingHelper(mv); 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 String generatedClassName = PACKAGE_NAME + "/" + className;
Label[] labels = new Label[fields.size()];
Label errorLabel = new Label(); mv.visitCode();
mv.visitVarInsn($Opcodes.ALOAD, 0);
// Generate labels 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++) { 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++) { for (int i = 0; i < fields.size(); i++) {
Field field = fields.get(i); Field field = fields.get(i);
Class<?> outputType = field.getType(); Class<?> outputType = field.getType();
String typeDescriptor = Type.getDescriptor(outputType); String typeDescriptor = $Type.getDescriptor(outputType);
mv.visitLabel(labels[i]); mv.visitLabel($Labels[i]);
// Push the compare object // Push the compare object
if (i == 0) 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 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 // Note that byte code cannot access non-public fields
if (isPublic(field)) { if (isPublic(field)) {
mv.visitVarInsn(Opcodes.ALOAD, 2); mv.visitVarInsn($Opcodes.ALOAD, 2);
mv.visitFieldInsn(Opcodes.GETFIELD, targetName, field.getName(), typeDescriptor); mv.visitFieldInsn($Opcodes.GETFIELD, targetName, field.getName(), typeDescriptor);
boxingHelper.box(Type.getType(outputType)); boxingHelper.box($Type.getType(outputType));
} else { } else {
// We have to use reflection for private and protected fields. // We have to use reflection for private and protected fields.
mv.visitVarInsn(Opcodes.ALOAD, 0); mv.visitVarInsn($Opcodes.ALOAD, 0);
mv.visitVarInsn(Opcodes.ILOAD, 1); mv.visitVarInsn($Opcodes.ILOAD, 1);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, generatedClassName, "readReflected", "(I)Ljava/lang/Object;"); mv.visitMethodInsn($Opcodes.INVOKEVIRTUAL, generatedClassName, "readReflected", "(I)Ljava/lang/Object;");
} }
mv.visitInsn(Opcodes.ARETURN); mv.visitInsn($Opcodes.ARETURN);
} }
mv.visitLabel(errorLabel); mv.visitLabel(error$Label);
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); mv.visitFrame($Opcodes.F_SAME, 0, null, 0, null);
mv.visitTypeInsn(Opcodes.NEW, FIELD_EXCEPTION_CLASS); mv.visitTypeInsn($Opcodes.NEW, FIELD_EXCEPTION_CLASS);
mv.visitInsn(Opcodes.DUP); mv.visitInsn($Opcodes.DUP);
mv.visitTypeInsn(Opcodes.NEW, "java/lang/StringBuilder"); mv.visitTypeInsn($Opcodes.NEW, "java/lang/StringBuilder");
mv.visitInsn(Opcodes.DUP); mv.visitInsn($Opcodes.DUP);
mv.visitLdcInsn("Invalid index "); mv.visitLdcInsn("Invalid index ");
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "(Ljava/lang/String;)V"); mv.visitMethodInsn($Opcodes.INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "(Ljava/lang/String;)V");
mv.visitVarInsn(Opcodes.ILOAD, 1); mv.visitVarInsn($Opcodes.ILOAD, 1);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(I)Ljava/lang/StringBuilder;"); 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.INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;");
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, FIELD_EXCEPTION_CLASS, "<init>", "(Ljava/lang/String;)V"); mv.visitMethodInsn($Opcodes.INVOKESPECIAL, FIELD_EXCEPTION_CLASS, "<init>", "(Ljava/lang/String;)V");
mv.visitInsn(Opcodes.ATHROW); mv.visitInsn($Opcodes.ATHROW);
mv.visitMaxs(5, 3); mv.visitMaxs(5, 3);
mv.visitEnd(); mv.visitEnd();
} }
private void createConstructor(ClassWriter cw, String className, String targetSignature, String targetName) { private void createConstructor($ClassWriter cw, String className, String targetSignature, String targetName) {
MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>", $MethodVisitor mv = cw.visitMethod($Opcodes.ACC_PUBLIC, "<init>",
"(L" + SUPER_CLASS + ";L" + PACKAGE_NAME + "/StructureCompiler;)V", "(L" + SUPER_CLASS + ";L" + PACKAGE_NAME + "/StructureCompiler;)V",
"(L" + SUPER_CLASS + "<Ljava/lang/Object;>;L" + PACKAGE_NAME + "/StructureCompiler;)V", null); "(L" + SUPER_CLASS + "<Ljava/lang/Object;>;L" + PACKAGE_NAME + "/StructureCompiler;)V", null);
String fullClassName = PACKAGE_NAME + "/" + className; String fullClassName = PACKAGE_NAME + "/" + className;
mv.visitCode(); mv.visitCode();
mv.visitVarInsn(Opcodes.ALOAD, 0); mv.visitVarInsn($Opcodes.ALOAD, 0);
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, COMPILED_CLASS, "<init>", "()V"); mv.visitMethodInsn($Opcodes.INVOKESPECIAL, COMPILED_CLASS, "<init>", "()V");
mv.visitVarInsn(Opcodes.ALOAD, 0); mv.visitVarInsn($Opcodes.ALOAD, 0);
mv.visitVarInsn(Opcodes.ALOAD, 1); mv.visitVarInsn($Opcodes.ALOAD, 1);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, fullClassName, "initialize", "(L" + SUPER_CLASS + ";)V"); mv.visitMethodInsn($Opcodes.INVOKEVIRTUAL, fullClassName, "initialize", "(L" + SUPER_CLASS + ";)V");
mv.visitVarInsn(Opcodes.ALOAD, 0); mv.visitVarInsn($Opcodes.ALOAD, 0);
mv.visitVarInsn(Opcodes.ALOAD, 1); mv.visitVarInsn($Opcodes.ALOAD, 1);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, SUPER_CLASS, "getTarget", "()Ljava/lang/Object;"); mv.visitMethodInsn($Opcodes.INVOKEVIRTUAL, SUPER_CLASS, "getTarget", "()Ljava/lang/Object;");
mv.visitFieldInsn(Opcodes.PUTFIELD, fullClassName, "target", "Ljava/lang/Object;"); mv.visitFieldInsn($Opcodes.PUTFIELD, fullClassName, "target", "Ljava/lang/Object;");
mv.visitVarInsn(Opcodes.ALOAD, 0); mv.visitVarInsn($Opcodes.ALOAD, 0);
mv.visitVarInsn(Opcodes.ALOAD, 0); mv.visitVarInsn($Opcodes.ALOAD, 0);
mv.visitFieldInsn(Opcodes.GETFIELD, fullClassName, "target", "Ljava/lang/Object;"); mv.visitFieldInsn($Opcodes.GETFIELD, fullClassName, "target", "Ljava/lang/Object;");
mv.visitTypeInsn(Opcodes.CHECKCAST, targetName); mv.visitTypeInsn($Opcodes.CHECKCAST, targetName);
mv.visitFieldInsn(Opcodes.PUTFIELD, fullClassName, "typedTarget", targetSignature); mv.visitFieldInsn($Opcodes.PUTFIELD, fullClassName, "typedTarget", targetSignature);
mv.visitVarInsn(Opcodes.ALOAD, 0); mv.visitVarInsn($Opcodes.ALOAD, 0);
mv.visitVarInsn(Opcodes.ALOAD, 2); mv.visitVarInsn($Opcodes.ALOAD, 2);
mv.visitFieldInsn(Opcodes.PUTFIELD, fullClassName, "compiler", "L" + PACKAGE_NAME + "/StructureCompiler;"); mv.visitFieldInsn($Opcodes.PUTFIELD, fullClassName, "compiler", "L" + PACKAGE_NAME + "/StructureCompiler;");
mv.visitInsn(Opcodes.RETURN); mv.visitInsn($Opcodes.RETURN);
mv.visitMaxs(2, 3); mv.visitMaxs(2, 3);
mv.visitEnd(); mv.visitEnd();
} }

View File

@ -4,25 +4,24 @@ import java.io.IOException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
import net.sf.cglib.asm.ClassReader; import com.comphenix.protocol.reflect.FuzzyReflection;
import net.sf.cglib.asm.MethodVisitor; import com.comphenix.protocol.reflect.accessors.Accessors;
import net.sf.cglib.asm.Opcodes; 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.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy; import net.sf.cglib.proxy.MethodProxy;
import org.bukkit.block.BlockState; 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. * Manipulate tile entities.
* @author Kristian * @author Kristian
@ -90,26 +89,26 @@ class TileEntityAccessor<T extends BlockState> {
private void findMethodsUsingASM() throws IOException { private void findMethodsUsingASM() throws IOException {
final Class<?> nbtCompoundClass = MinecraftReflection.getNBTCompoundClass(); final Class<?> nbtCompoundClass = MinecraftReflection.getNBTCompoundClass();
final Class<?> tileEntityClass = MinecraftReflection.getTileEntityClass(); 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 tagCompoundName = getJarName(MinecraftReflection.getNBTCompoundClass());
final String expectedDesc = "(L" + tagCompoundName + ";)"; final String expectedDesc = "(L" + tagCompoundName + ";)";
reader.accept(new EmptyClassVisitor() { reader.accept(new $ClassVisitor($Opcodes.ASM5) {
@Override @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; final String methodName = name;
// Detect read/write calls to NBTTagCompound // Detect read/write calls to NBTTagCompound
if (desc.startsWith(expectedDesc)) { if (desc.startsWith(expectedDesc)) {
return new EmptyMethodVisitor() { return new $MethodVisitor($Opcodes.ASM5) {
private int readMethods; private int readMethods;
private int writeMethods; private int writeMethods;
@Override @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 // This must be a virtual call on NBTTagCompound that accepts a String
if (opcode == Opcodes.INVOKEVIRTUAL if (opcode == $Opcodes.INVOKEVIRTUAL
&& tagCompoundName.equals(owner) && tagCompoundName.equals(owner)
&& desc.startsWith("(Ljava/lang/String")) { && desc.startsWith("(Ljava/lang/String")) {

View File

@ -20,6 +20,8 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.build.number></project.build.number> <project.build.number></project.build.number>
<project.fullVersion>${project.version}</project.fullVersion> <project.fullVersion>${project.version}</project.fullVersion>
<powermock.version>1.7.0RC4</powermock.version>
</properties> </properties>
<build> <build>
@ -238,25 +240,25 @@
<dependency> <dependency>
<groupId>junit</groupId> <groupId>junit</groupId>
<artifactId>junit</artifactId> <artifactId>junit</artifactId>
<version>4.10</version> <version>4.12</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.mockito</groupId> <groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId> <artifactId>mockito-core</artifactId>
<version>1.8.4</version> <version>2.8.9</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.powermock</groupId> <groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId> <artifactId>powermock-module-junit4</artifactId>
<version>1.5</version> <version>${powermock.version}</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.powermock</groupId> <groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito</artifactId> <artifactId>powermock-api-mockito2</artifactId>
<version>1.5</version> <version>${powermock.version}</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
</dependencies> </dependencies>

View File

@ -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());
} */
}

View File

@ -8,7 +8,7 @@
<properties> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <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> <spigotVersion>1.12-R0.1-SNAPSHOT</spigotVersion>
</properties> </properties>
@ -67,7 +67,7 @@
<dependency> <dependency>
<groupId>cglib</groupId> <groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId> <artifactId>cglib-nodep</artifactId>
<version>2.2.2</version> <version>3.2.5</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency> <dependency>