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.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;
}
}

View File

@ -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);

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.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() {

View File

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

View File

@ -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")) {

View File

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

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