/* 94: */ /* 95: */ protected int match( int c, int pos, CodeIterator iterator, int typedesc, ConstPool cp) /* 96: */ throws BadBytecode /* 97: */ { /* 98:110 */ if (this.newIndex == 0) /* 99: */ { /* 100:111 */ int nt = cp.addNameAndTypeInfo(cp.addUtf8Info(this.newMethodname), typedesc); /* 101: */ /* 102:113 */ int ci = cp.addClassInfo(this.newClassname); /* 103:114 */ if (c == 185) /* 104: */ { /* 105:115 */ this.newIndex = cp.addInterfaceMethodrefInfo(ci, nt); /* 106: */ } /* 107: */ else /* 108: */ { /* 109:117 */ if ((this.newMethodIsPrivate) && (c == 182)) { /* 110:118 */ iterator.writeByte(183, pos); /* 111: */ } /* 112:120 */ this.newIndex = cp.addMethodrefInfo(ci, nt); /* 113: */ } /* 114:123 */ this.constPool = cp; /* 115: */ } /* 116:126 */ iterator.write16bit(this.newIndex, pos + 1); /* 117:127 */ return pos; /* 118: */ }
public static void rewriteFakeMethod(CodeIterator methodBody, String methodDescriptor) { String ret = DescriptorUtils.getReturnType(methodDescriptor); // if the return type is larger than one then it is not a primitive // so it does not need to be boxed if (ret.length() != 1) { return; } byte ar = (byte) Opcode.ARETURN; byte[] areturn = {ar}; // void methods are special if (ret.equals("V")) { while (methodBody.hasNext()) { try { int index = methodBody.next(); int opcode = methodBody.byteAt(index); // replace a RETURN opcode with // ACONST_NULL // ARETURN // to return a null value if (opcode == Opcode.RETURN) { Bytecode code = new Bytecode(methodBody.get().getConstPool()); code.add(Opcode.ACONST_NULL); code.add(Opcode.ARETURN); methodBody.insertAt(index, code.get()); } } catch (BadBytecode e) { throw new RuntimeException(e); } } } else { while (methodBody.hasNext()) { try { int index = methodBody.next(); int opcode = methodBody.byteAt(index); switch (opcode) { case Opcode.IRETURN: case Opcode.LRETURN: case Opcode.DRETURN: case Opcode.FRETURN: // write a NOP over the old return instruction // insert the boxing code to get an object on the stack Bytecode b = new Bytecode(methodBody.get().getConstPool()); Boxing.box(b, ret.charAt(0)); b.addOpcode(Opcode.ARETURN); methodBody.insertAt(index, b.get()); } } catch (BadBytecode e) { throw new RuntimeException(e); } } } }
@Override protected void onApply(CtBehavior behavior, Bytecode bytecode) throws BadBytecode { bytecode = BytecodeTools.prepareMethodForBytecode(behavior, bytecode); // loop through the opcodes and change any GT/GE opcodes to ICMPGT/ICMPGE CodeIterator iterator = behavior.getMethodInfo().getCodeAttribute().iterator(); while (iterator.hasNext()) { int index = iterator.next(); int opcode = iterator.byteAt(index); switch (opcode) { case Opcode.IFGT: // overwrite the opcode iterator.writeByte(Opcode.IF_ICMPGT, index); // insert the method call iterator.insertAt(index, bytecode.get()); behavior.getMethodInfo().getCodeAttribute().computeMaxStack(); break; case Opcode.IFGE: // overwrite the opcode iterator.writeByte(Opcode.IF_ICMPGE, index); // insert the method call iterator.insertAt(index, bytecode.get()); behavior.getMethodInfo().getCodeAttribute().computeMaxStack(); break; } } }
protected void enhanceAttributesAccess( CtClass managedCtClass, IdentityHashMap<String, PersistentAttributeAccessMethods> attributeDescriptorMap) { final ConstPool constPool = managedCtClass.getClassFile().getConstPool(); for (Object oMethod : managedCtClass.getClassFile().getMethods()) { final MethodInfo methodInfo = (MethodInfo) oMethod; final String methodName = methodInfo.getName(); // skip methods added by enhancement and abstract methods (methods without any code) if (methodName.startsWith("$$_hibernate_") || methodInfo.getCodeAttribute() == null) { continue; } try { final CodeIterator itr = methodInfo.getCodeAttribute().iterator(); while (itr.hasNext()) { final int index = itr.next(); final int op = itr.byteAt(index); if (op != Opcode.PUTFIELD && op != Opcode.GETFIELD) { continue; } final String fieldName = constPool.getFieldrefName(itr.u16bitAt(index + 1)); final PersistentAttributeAccessMethods attributeMethods = attributeDescriptorMap.get(fieldName); // its not a field we have enhanced for interception, so skip it if (attributeMethods == null) { continue; } // System.out.printf( "Transforming access to field [%s] from method [%s]%n", fieldName, // methodName ); log.debugf("Transforming access to field [%s] from method [%s]", fieldName, methodName); if (op == Opcode.GETFIELD) { final int methodIndex = MethodWriter.addMethod(constPool, attributeMethods.getReader()); itr.writeByte(Opcode.INVOKESPECIAL, index); itr.write16bit(methodIndex, index + 1); } else { final int methodIndex = MethodWriter.addMethod(constPool, attributeMethods.getWriter()); itr.writeByte(Opcode.INVOKESPECIAL, index); itr.write16bit(methodIndex, index + 1); } } methodInfo.getCodeAttribute().setAttribute(MapMaker.make(classPool, methodInfo)); } catch (BadBytecode bb) { final String msg = String.format( "Unable to perform field access transformation in method [%s]", methodName); throw new EnhancementException(msg, bb); } } }
/** * Changes a super constructor called by this constructor. * * <p>This method modifies a call to <code>super()</code>, which should be at the head of a * constructor body, so that a constructor in a different super class is called. This method does * not change actural parameters. Hence the new super class must have a constructor with the same * signature as the original one. * * <p>This method should be called when the super class of the class declaring this method is * changed. * * <p>This method does not perform anything unless this <code>MethodInfo</code> represents a * constructor. * * @param superclass the new super class */ public void setSuperclass(String superclass) throws BadBytecode { if (!isConstructor()) return; CodeAttribute ca = getCodeAttribute(); byte[] code = ca.getCode(); CodeIterator iterator = ca.iterator(); int pos = iterator.skipSuperConstructor(); if (pos >= 0) { // not this() ConstPool cp = constPool; int mref = ByteArray.readU16bit(code, pos + 1); int nt = cp.getMethodrefNameAndType(mref); int sc = cp.addClassInfo(superclass); int mref2 = cp.addMethodrefInfo(sc, nt); ByteArray.write16bit(mref2, code, pos + 1); } }
/* 49: */ /* 50: */ public int transform(CtClass clazz, int pos, CodeIterator iterator, ConstPool cp) /* 51: */ throws BadBytecode /* 52: */ { /* 53: 68 */ int c = iterator.byteAt(pos); /* 54: 69 */ if ((c == 185) || (c == 183) || (c == 184) || (c == 182)) /* 55: */ { /* 56: 71 */ int index = iterator.u16bitAt(pos + 1); /* 57: 72 */ String cname = cp.eqMember(this.methodname, this.methodDescriptor, index); /* 58: 73 */ if ((cname != null) && (matchClass(cname, clazz.getClassPool()))) /* 59: */ { /* 60: 74 */ int ntinfo = cp.getMemberNameAndType(index); /* 61: 75 */ pos = match(c, pos, iterator, cp.getNameAndTypeDescriptor(ntinfo), cp); /* 62: */ } /* 63: */ } /* 64: 80 */ return pos; /* 65: */ }
private static String lookupSwitch(CodeIterator iter, int pos) { StringBuffer buffer = new StringBuffer("lookupswitch {\n"); int index = (pos & ~3) + 4; // default buffer.append("\t\tdefault: ").append(pos + iter.s32bitAt(index)).append("\n"); int npairs = iter.s32bitAt(index += 4); int end = npairs * 8 + (index += 4); for (; index < end; index += 8) { int match = iter.s32bitAt(index); int target = iter.s32bitAt(index + 4) + pos; buffer.append("\t\t").append(match).append(": ").append(target).append("\n"); } buffer.setCharAt(buffer.length() - 1, '}'); return buffer.toString(); }
/** Prints the bytecode instructions of a given method. */ public void print(CtMethod method) { MethodInfo info = method.getMethodInfo2(); ConstPool pool = info.getConstPool(); CodeAttribute code = info.getCodeAttribute(); if (code == null) return; CodeIterator iterator = code.iterator(); while (iterator.hasNext()) { int pos; try { pos = iterator.next(); } catch (BadBytecode e) { throw new RuntimeException(e); } stream.println(pos + ": " + instructionString(iterator, pos, pool)); } }
private static String tableSwitch(CodeIterator iter, int pos) { StringBuffer buffer = new StringBuffer("tableswitch {\n"); int index = (pos & ~3) + 4; // default buffer.append("\t\tdefault: ").append(pos + iter.s32bitAt(index)).append("\n"); int low = iter.s32bitAt(index += 4); int high = iter.s32bitAt(index += 4); int end = (high - low + 1) * 4 + (index += 4); // Offset table for (int key = low; index < end; index += 4, key++) { int target = iter.s32bitAt(index) + pos; buffer.append("\t\t").append(key).append(": ").append(target).append("\n"); } buffer.setCharAt(buffer.length() - 1, '}'); return buffer.toString(); }
private static String wide(CodeIterator iter, int pos) { int opcode = iter.byteAt(pos + 1); int index = iter.u16bitAt(pos + 2); switch (opcode) { case ILOAD: case LLOAD: case FLOAD: case DLOAD: case ALOAD: case ISTORE: case LSTORE: case FSTORE: case DSTORE: case ASTORE: case IINC: case RET: return opcodes[opcode] + " " + index; default: throw new RuntimeException("Invalid WIDE operand"); } }
private void initExtraHarvest() { try { CtClass terraForming = HookManager.getInstance() .getClassPool() .get("com.wurmonline.server.behaviours.Terraforming"); CtClass[] paramTypes = { HookManager.getInstance().getClassPool().get("com.wurmonline.server.creatures.Creature"), CtPrimitiveType.intType, CtPrimitiveType.intType, CtPrimitiveType.booleanType, CtPrimitiveType.intType, CtPrimitiveType.floatType, HookManager.getInstance().getClassPool().get("com.wurmonline.server.items.Item") }; CtMethod method = terraForming.getMethod( "harvest", Descriptor.ofMethod(CtPrimitiveType.booleanType, paramTypes)); MethodInfo methodInfo = method.getMethodInfo(); CodeAttribute codeAttribute = methodInfo.getCodeAttribute(); CodeIterator codeIterator = codeAttribute.iterator(); LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag); int quantityIndex = -1; for (int i = 0; i < attr.tableLength(); i++) { if ("quantity".equals(attr.variableName(i))) { quantityIndex = attr.index(i); } } if (quantityIndex == -1) { throw new HookException("Quantity variable can not be resolved"); } while (codeIterator.hasNext()) { int pos = codeIterator.next(); int op = codeIterator.byteAt(pos); if (op == CodeIterator.ISTORE) { int fieldRefIdx = codeIterator.byteAt(pos + 1); if (quantityIndex == fieldRefIdx) { Bytecode bytecode = new Bytecode(codeIterator.get().getConstPool()); bytecode.addIconst(extraHarvest); bytecode.add(Bytecode.IADD); codeIterator.insertAt(pos, bytecode.get()); break; } } } } catch (NotFoundException | BadBytecode e) { throw new HookException(e); } }
protected void enhanceAttributesAccess(Map<String, CtField> fieldsMap, CtClass managedCtClass) throws Exception { final ConstPool constPool = managedCtClass.getClassFile().getConstPool(); final ClassPool classPool = managedCtClass.getClassPool(); for (Object oMethod : managedCtClass.getClassFile().getMethods()) { final MethodInfo methodInfo = (MethodInfo) oMethod; final String methodName = methodInfo.getName(); // skip methods added by enhancement, and abstract methods (methods without any code) if (methodName.startsWith(DROOLS_PREFIX) || methodInfo.getCodeAttribute() == null) { continue; } try { final CodeIterator itr = methodInfo.getCodeAttribute().iterator(); while (itr.hasNext()) { final int index = itr.next(); final int op = itr.byteAt(index); if (op != Opcode.PUTFIELD && op != Opcode.GETFIELD) { continue; } final String fieldName = constPool.getFieldrefName(itr.u16bitAt(index + 1)); CtField ctField = fieldsMap.get(fieldName); if (ctField == null) { continue; } // if we are in constructors, only need to intercept assignment statement for Reactive // Collection/List/... (regardless they may be final) if (methodInfo.isConstructor() && !(isCtFieldACollection(ctField))) { continue; } if (op == Opcode.PUTFIELD) { // addMethod is a safe add, if constant already present it return the existing value // without adding. final int methodIndex = addMethod(constPool, writeMethods.get(fieldName)); itr.writeByte(Opcode.INVOKEVIRTUAL, index); itr.write16bit(methodIndex, index + 1); } } methodInfo.getCodeAttribute().setAttribute(MapMaker.make(classPool, methodInfo)); } catch (BadBytecode bb) { final String msg = String.format( "Unable to perform field access transformation in method [%s]", methodName); throw new Exception(msg, bb); } } }
static byte[] doit(byte[] code, LdcEntry ldc, ExceptionTable etable, CodeAttribute ca) throws BadBytecode { if (ldc != null) code = CodeIterator.changeLdcToLdcW(code, etable, ca, ldc); /* The original code was the following: while (ldc != null) { int where = ldc.where; code = CodeIterator.insertGapCore0(code, where, 1, false, etable, ca); code[where] = (byte)Opcode.LDC_W; ByteArray.write16bit(ldc.index, code, where + 1); ldc = ldc.next; } But this code does not support a large method > 32KB. */ return code; }
private static void shiftIndex8(CodeIterator ci, int index, int opcode, int lessThan, int delta) throws BadBytecode { int var = ci.byteAt(index + 1); if (var < lessThan) return; var += delta; if (var < 0x100) ci.writeByte(var, index + 1); else { int pos = ci.insertExGap(2); ci.writeByte(WIDE, pos - 2); ci.writeByte(opcode, pos - 1); ci.write16bit(var, pos); } }
private static void shiftIndex0( CodeIterator ci, int index, int opcode, int lessThan, int delta, int opcode_i_0, int opcode_i) throws BadBytecode { int var = (opcode - opcode_i_0) % 4; if (var < lessThan) return; var += delta; if (var < 4) ci.writeByte(opcode + delta, index); else { opcode = (opcode - opcode_i_0) / 4 + opcode_i; if (var < 0x100) { int pos = ci.insertExGap(1); ci.writeByte(opcode, pos - 1); ci.writeByte(var, pos); } else { int pos = ci.insertExGap(3); ci.writeByte(WIDE, pos - 1); ci.writeByte(opcode, pos); ci.write16bit(var, pos + 1); } } }
@Override public boolean transform( ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, ClassFile file) throws IllegalClassFormatException, BadBytecode { /** * Hack up the proxy factory so it stores the proxy ClassFile. We need this to regenerate * proxies. */ if (file.getName().equals("org.jboss.weld.bean.proxy.ProxyFactory")) { for (final MethodInfo method : (List<MethodInfo>) file.getMethods()) { if (method.getName().equals("createProxyClass")) { final MethodInvokationManipulator methodInvokationManipulator = new MethodInvokationManipulator(); methodInvokationManipulator.replaceVirtualMethodInvokationWithStatic( ClassLoader.class.getName(), WeldProxyClassLoadingDelegate.class.getName(), "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;", "(Ljava/lang/ClassLoader;Ljava/lang/String;)Ljava/lang/Class;", loader); methodInvokationManipulator.replaceVirtualMethodInvokationWithStatic( "org.jboss.weld.util.bytecode.ClassFileUtils", WeldProxyClassLoadingDelegate.class.getName(), "toClass", "(Ljavassist/bytecode/ClassFile;Ljava/lang/ClassLoader;Ljava/security/ProtectionDomain;)Ljava/lang/Class;", "(Ljavassist/bytecode/ClassFile;Ljava/lang/ClassLoader;Ljava/security/ProtectionDomain;)Ljava/lang/Class;", loader); HashSet<MethodInfo> modifiedMethods = new HashSet<MethodInfo>(); methodInvokationManipulator.transformClass(file, loader, true, modifiedMethods); for (MethodInfo m : modifiedMethods) { m.rebuildStackMap(ClassPool.getDefault()); } return true; } else if (method.getName().equals("<init>")) { Integer beanArgument = null; int count = 0; for (final String paramType : DescriptorUtils.descriptorStringToParameterArray(method.getDescriptor())) { if (paramType.equals("javax/enterprise/inject/spi/Bean")) { beanArgument = count; break; } else if (paramType.equals("D") || paramType.equals("J")) { count += 2; } else { count++; } } if (beanArgument == null) { log.error( "Constructor org.jboss.weld.bean.proxy.ProxyFactory.<init>" + method.getDescriptor() + " does not have a bean parameter, proxies produced by this factory will not be reloadable"); continue; } // similar to other tracked instances // but we need a strong ref Bytecode code = new Bytecode(file.getConstPool()); code.addAload(0); code.addAload(beanArgument); code.addInvokestatic( WeldClassChangeAware.class.getName(), "addProxyFactory", "(Lorg/jboss/weld/bean/proxy/ProxyFactory;)V"); CodeIterator it = method.getCodeAttribute().iterator(); it.skipConstructor(); it.insert(code.get()); } } } return false; }
/** * @param lessThan If the index of the local variable is less than this value, it does not change. * Otherwise, the index is increased. * @param delta the indexes of the local variables are increased by this value. */ private static void shiftIndex(CodeIterator ci, int lessThan, int delta) throws BadBytecode { int index = ci.next(); int opcode = ci.byteAt(index); if (opcode < ILOAD) return; else if (opcode < IASTORE) { if (opcode < ILOAD_0) { // iload, lload, fload, dload, aload shiftIndex8(ci, index, opcode, lessThan, delta); } else if (opcode < IALOAD) { // iload_0, ..., aload_3 shiftIndex0(ci, index, opcode, lessThan, delta, ILOAD_0, ILOAD); } else if (opcode < ISTORE) return; else if (opcode < ISTORE_0) { // istore, lstore, ... shiftIndex8(ci, index, opcode, lessThan, delta); } else { // istore_0, ..., astore_3 shiftIndex0(ci, index, opcode, lessThan, delta, ISTORE_0, ISTORE); } } else if (opcode == IINC) { int var = ci.byteAt(index + 1); if (var < lessThan) return; var += delta; if (var < 0x100) ci.writeByte(var, index + 1); else { int plus = (byte) ci.byteAt(index + 2); int pos = ci.insertExGap(3); ci.writeByte(WIDE, pos - 3); ci.writeByte(IINC, pos - 2); ci.write16bit(var, pos - 1); ci.write16bit(plus, pos + 1); } } else if (opcode == RET) shiftIndex8(ci, index, opcode, lessThan, delta); else if (opcode == WIDE) { int var = ci.u16bitAt(index + 2); if (var < lessThan) return; var += delta; ci.write16bit(var, index + 2); } }
/** * Changes the index numbers of the local variables to append a new parameter. This method does * not update <code>LocalVariableAttribute</code>, <code>LocalVariableTypeAttribute</code>, <code> * StackMapTable</code>, or <code>StackMap</code>. These attributes must be explicitly updated. * * @param where the index of the new parameter. * @param size the type size of the new parameter (1 or 2). * @see LocalVariableAttribute#shiftIndex(int, int) * @see LocalVariableTypeAttribute#shiftIndex(int, int) * @see StackMapTable#insertLocal(int, int, int) * @see StackMap#insertLocal(int, int, int) */ public void insertLocalVar(int where, int size) throws BadBytecode { CodeIterator ci = iterator(); while (ci.hasNext()) shiftIndex(ci, where, size); setMaxLocals(getMaxLocals() + size); }
private static LdcEntry copyCode( byte[] code, int beginPos, int endPos, ConstPool srcCp, byte[] newcode, ConstPool destCp, Map classnameMap) throws BadBytecode { int i2, index; LdcEntry ldcEntry = null; for (int i = beginPos; i < endPos; i = i2) { i2 = CodeIterator.nextOpcode(code, i); byte c = code[i]; newcode[i] = c; switch (c & 0xff) { case LDC_W: case LDC2_W: case GETSTATIC: case PUTSTATIC: case GETFIELD: case PUTFIELD: case INVOKEVIRTUAL: case INVOKESPECIAL: case INVOKESTATIC: case NEW: case ANEWARRAY: case CHECKCAST: case INSTANCEOF: copyConstPoolInfo(i + 1, code, srcCp, newcode, destCp, classnameMap); break; case LDC: index = code[i + 1] & 0xff; index = srcCp.copy(index, destCp, classnameMap); if (index < 0x100) newcode[i + 1] = (byte) index; else { newcode[i] = NOP; newcode[i + 1] = NOP; LdcEntry ldc = new LdcEntry(); ldc.where = i; ldc.index = index; ldc.next = ldcEntry; ldcEntry = ldc; } break; case INVOKEINTERFACE: copyConstPoolInfo(i + 1, code, srcCp, newcode, destCp, classnameMap); newcode[i + 3] = code[i + 3]; newcode[i + 4] = code[i + 4]; break; case INVOKEDYNAMIC: copyConstPoolInfo(i + 1, code, srcCp, newcode, destCp, classnameMap); newcode[i + 3] = 0; newcode[i + 4] = 0; break; case MULTIANEWARRAY: copyConstPoolInfo(i + 1, code, srcCp, newcode, destCp, classnameMap); newcode[i + 3] = code[i + 3]; break; default: while (++i < i2) newcode[i] = code[i]; break; } } return ldcEntry; }
/** Gets a string representation of the bytecode instruction at the specified position. */ public static String instructionString(CodeIterator iter, int pos, ConstPool pool) { int opcode = iter.byteAt(pos); if (opcode > opcodes.length || opcode < 0) throw new IllegalArgumentException("Invalid opcode, opcode: " + opcode + " pos: " + pos); String opstring = opcodes[opcode]; switch (opcode) { case BIPUSH: return opstring + " " + iter.byteAt(pos + 1); case SIPUSH: return opstring + " " + iter.s16bitAt(pos + 1); case LDC: return opstring + " " + ldc(pool, iter.byteAt(pos + 1)); case LDC_W: case LDC2_W: return opstring + " " + ldc(pool, iter.u16bitAt(pos + 1)); case ILOAD: case LLOAD: case FLOAD: case DLOAD: case ALOAD: case ISTORE: case LSTORE: case FSTORE: case DSTORE: case ASTORE: return opstring + " " + iter.byteAt(pos + 1); case IFEQ: case IFGE: case IFGT: case IFLE: case IFLT: case IFNE: case IFNONNULL: case IFNULL: case IF_ACMPEQ: case IF_ACMPNE: case IF_ICMPEQ: case IF_ICMPGE: case IF_ICMPGT: case IF_ICMPLE: case IF_ICMPLT: case IF_ICMPNE: return opstring + " " + (iter.s16bitAt(pos + 1) + pos); case IINC: return opstring + " " + iter.byteAt(pos + 1); case GOTO: case JSR: return opstring + " " + (iter.s16bitAt(pos + 1) + pos); case RET: return opstring + " " + iter.byteAt(pos + 1); case TABLESWITCH: return tableSwitch(iter, pos); case LOOKUPSWITCH: return lookupSwitch(iter, pos); case GETSTATIC: case PUTSTATIC: case GETFIELD: case PUTFIELD: return opstring + " " + fieldInfo(pool, iter.u16bitAt(pos + 1)); case INVOKEVIRTUAL: case INVOKESPECIAL: case INVOKESTATIC: return opstring + " " + methodInfo(pool, iter.u16bitAt(pos + 1)); case INVOKEINTERFACE: return opstring + " " + interfaceMethodInfo(pool, iter.u16bitAt(pos + 1)); case INVOKEDYNAMIC: return opstring + " " + iter.u16bitAt(pos + 1); case NEW: return opstring + " " + classInfo(pool, iter.u16bitAt(pos + 1)); case NEWARRAY: return opstring + " " + arrayInfo(iter.byteAt(pos + 1)); case ANEWARRAY: case CHECKCAST: return opstring + " " + classInfo(pool, iter.u16bitAt(pos + 1)); case WIDE: return wide(iter, pos); case MULTIANEWARRAY: return opstring + " " + classInfo(pool, iter.u16bitAt(pos + 1)); case GOTO_W: case JSR_W: return opstring + " " + (iter.s32bitAt(pos + 1) + pos); default: return opstring; } }
protected void checkClassFile(ClassFile file) throws Exception { Map<Integer, Triple> calls = new HashMap<>(); ConstPool pool = file.getConstPool(); for (int i = 1; i < pool.getSize(); ++i) { // we have a method call BytecodeUtils.Ref ref = BytecodeUtils.getRef(pool, i); String className = ref.getClassName(pool, i); if (className != null) { String methodName = ref.getName(pool, i); String methodDesc = ref.getDesc(pool, i); fillCalls(i, className, methodName, methodDesc, calls); } } if (calls.isEmpty() && annotations.isEmpty()) { return; } String className = file.getName(); AnnotationsAttribute faa = (AnnotationsAttribute) file.getAttribute(AnnotationsAttribute.visibleTag); checkAnnotations(className, TYPE_USAGE.getMethodName(), faa, -1); List<MethodInfo> methods = file.getMethods(); for (MethodInfo m : methods) { try { // ignore abstract methods if (m.getCodeAttribute() == null) { continue; } AnnotationsAttribute maa = (AnnotationsAttribute) m.getAttribute(AnnotationsAttribute.visibleTag); boolean annotationsChecked = false; int firstLine = -1; CodeIterator it = m.getCodeAttribute().iterator(); while (it.hasNext()) { // loop through the bytecode final int index = it.next(); final int line = m.getLineNumber(index); if (annotationsChecked == false) { annotationsChecked = true; firstLine = line; checkAnnotations( className, m.getName(), maa, line - 2); // -2 to get the line above the method } int op = it.byteAt(index); // if the bytecode is a method invocation if (op == CodeIterator.INVOKEVIRTUAL || op == CodeIterator.INVOKESTATIC || op == CodeIterator.INVOKEINTERFACE || op == CodeIterator.INVOKESPECIAL) { int val = it.s16bitAt(index + 1); Triple triple = calls.get(val); if (triple != null) { Map<Tuple, Set<CodeLine>> map = report.get(triple.className); Set<CodeLine> set = map.get(triple.tuple); CodeLine cl = new CodeLine(className, m.getName(), line); set.add(cl.modify()); // check for .jsp, etc } } } if (BaseMethodExclusion.isBridge(m) == false) { SignatureAttribute.MethodSignature signature = SignatureAttribute.toMethodSignature(m.getDescriptor()); handleMethodSignature(className, m.getName(), firstLine - 1, signature.getReturnType()); handleMethodSignature( className, m.getName(), firstLine - 1, signature.getParameterTypes()); handleMethodSignature( className, m.getName(), firstLine - 1, signature.getExceptionTypes()); } ParameterAnnotationsAttribute paa = (ParameterAnnotationsAttribute) m.getAttribute(ParameterAnnotationsAttribute.visibleTag); if (paa != null) { Annotation[][] paas = paa.getAnnotations(); if (paas != null) { for (Annotation[] params : paas) { for (Annotation a : params) { for (Map.Entry<String, Boolean> entry : annotations.entrySet()) { if (entry.getKey().equals(a.getTypeName())) { checkAnnotation( className, m.getName(), firstLine - 1, entry.getValue(), entry.getKey(), a); } } } } } } m.getCodeAttribute().computeMaxStack(); } catch (Exception e) { e.printStackTrace(); } } }
public boolean match(String filename, ClassFile classFile, ClassMap tempClassMap) { for (Object o : classFile.getMethods()) { MethodInfo methodInfo = (MethodInfo) o; classMod.methodInfo = methodInfo; CodeAttribute codeAttribute = methodInfo.getCodeAttribute(); if (codeAttribute == null) { continue; } if (getClassMap().hasMap(deobfMethod)) { MethodRef obfTarget = (MethodRef) getClassMap().map(deobfMethod); if (!methodInfo.getName().equals(obfTarget.getName())) { continue; } } ArrayList<String> deobfTypes = null; ArrayList<String> obfTypes = null; if (deobfMethod != null && deobfMethod.getType() != null) { deobfTypes = ConstPoolUtils.parseDescriptor(deobfMethod.getType()); obfTypes = ConstPoolUtils.parseDescriptor(methodInfo.getDescriptor()); if (!isPotentialTypeMatch(deobfTypes, obfTypes)) { continue; } } ConstPool constPool = methodInfo.getConstPool(); CodeIterator codeIterator = codeAttribute.iterator(); initMatcher(); ArrayList<JavaRef> tempMappings = new ArrayList<JavaRef>(); try { match: for (int offset = 0; offset < codeIterator.getCodeLength() && matcher.match(methodInfo, offset); offset = codeIterator.next()) { tempMappings.clear(); for (Map.Entry<Integer, JavaRef> entry : xrefs.entrySet()) { int captureGroup = entry.getKey(); JavaRef xref = entry.getValue(); byte[] code = matcher.getCaptureGroup(captureGroup); int index = Util.demarshal(code, 1, 2); ConstPoolUtils.matchOpcodeToRefType(code[0], xref); ConstPoolUtils.matchConstPoolTagToRefType(constPool.getTag(index), xref); JavaRef newRef = ConstPoolUtils.getRefForIndex(constPool, index); if (!isPotentialTypeMatch(xref.getType(), newRef.getType())) { if (deobfMethod != null) { Logger.log( Logger.LOG_METHOD, "method %s %s matches %s %s, but", methodInfo.getName(), methodInfo.getDescriptor(), deobfMethod.getName(), deobfMethod.getType()); } Logger.log( Logger.LOG_METHOD, "method %s %s failed xref #%d %s %s -> %s %s", methodInfo.getName(), methodInfo.getDescriptor(), captureGroup, xref.getName(), xref.getType(), newRef.getName(), newRef.getType()); continue match; } tempMappings.add(xref); tempMappings.add(newRef); } for (int i = 0; i + 1 < tempMappings.size(); i += 2) { tempClassMap.addMap(tempMappings.get(i), tempMappings.get(i + 1)); } if (deobfMethod != null) { String deobfName = classMod.getDeobfClass(); tempClassMap.addClassMap(deobfName, ClassMap.filenameToClassName(filename)); tempClassMap.addMethodMap( deobfName, deobfMethod.getName(), methodInfo.getName(), methodInfo.getDescriptor()); if (deobfTypes != null && obfTypes != null) { for (int i = 0; i < deobfTypes.size(); i++) { String desc = ClassMap.descriptorToClassName(deobfTypes.get(i)); String obf = ClassMap.descriptorToClassName(obfTypes.get(i)); if (!obf.equals(desc)) { tempClassMap.addClassMap(desc, obf); } } } } afterMatch(classFile, methodInfo); classMod.methodInfo = null; return true; } } catch (BadBytecode e) { Logger.log(e); } } classMod.methodInfo = null; return false; }
/** * Replace access to fields of entities (for example, entity.field) with a call to the enhanced * getter / setter (in this example, entity.$$_hibernate_read_field()). It's assumed that the * target entity is enhanced as well. * * @param managedCtClass Class to enhance */ public void enhanceFieldAccess(CtClass managedCtClass) { final ConstPool constPool = managedCtClass.getClassFile().getConstPool(); for (Object oMethod : managedCtClass.getClassFile().getMethods()) { final MethodInfo methodInfo = (MethodInfo) oMethod; final String methodName = methodInfo.getName(); // skip methods added by enhancement and abstract methods (methods without any code) if (methodName.startsWith("$$_hibernate_") || methodInfo.getCodeAttribute() == null) { continue; } try { final CodeIterator itr = methodInfo.getCodeAttribute().iterator(); while (itr.hasNext()) { int index = itr.next(); int op = itr.byteAt(index); if (op != Opcode.PUTFIELD && op != Opcode.GETFIELD) { continue; } String fieldName = constPool.getFieldrefName(itr.u16bitAt(index + 1)); String fieldClassName = constPool.getClassInfo(constPool.getFieldrefClass(itr.u16bitAt(index + 1))); CtClass targetCtClass = this.classPool.getCtClass(fieldClassName); if (!enhancementContext.isEntityClass(targetCtClass) && !enhancementContext.isCompositeClass(targetCtClass)) { continue; } if (targetCtClass == managedCtClass || !enhancementContext.isPersistentField(targetCtClass.getField(fieldName)) || "this$0".equals(fieldName)) { continue; } log.debugf("Transforming access to field [%s] from method [%s]", fieldName, methodName); if (op == Opcode.GETFIELD) { int fieldReaderMethodIndex = constPool.addMethodrefInfo( constPool.addClassInfo(fieldClassName), EnhancerConstants.PERSISTENT_FIELD_READER_PREFIX + fieldName, "()" + constPool.getFieldrefType(itr.u16bitAt(index + 1))); itr.writeByte(Opcode.INVOKEVIRTUAL, index); itr.write16bit(fieldReaderMethodIndex, index + 1); } else { int fieldWriterMethodIndex = constPool.addMethodrefInfo( constPool.addClassInfo(fieldClassName), EnhancerConstants.PERSISTENT_FIELD_WRITER_PREFIX + fieldName, "(" + constPool.getFieldrefType(itr.u16bitAt(index + 1)) + ")V"); itr.writeByte(Opcode.INVOKEVIRTUAL, index); itr.write16bit(fieldWriterMethodIndex, index + 1); } } methodInfo.getCodeAttribute().setAttribute(MapMaker.make(classPool, methodInfo)); } catch (BadBytecode bb) { final String msg = String.format( "Unable to perform field access transformation in method [%s]", methodName); throw new EnhancementException(msg, bb); } catch (NotFoundException nfe) { final String msg = String.format( "Unable to perform field access transformation in method [%s]", methodName); throw new EnhancementException(msg, nfe); } } }
public boolean transformClass(ClassFile file, ClassLoader loader, boolean modifiableClass) { Set<Integer> methodCallLocations = new HashSet<Integer>(); Integer newCallLocation = null; Integer methodReflectionLocation = null; Integer fakeCallRequiredLocation = null; // first we need to scan the constant pool looking for // CONSTANT_method_info_ref structures ConstPool pool = file.getConstPool(); for (int i = 1; i < pool.getSize(); ++i) { // we have a method call if (pool.getTag(i) == ConstPool.CONST_Methodref) { String className = pool.getMethodrefClassName(i); String methodName = pool.getMethodrefName(i); if (className.equals(Method.class.getName())) { if (methodName.equals("invoke")) { // store the location in the const pool of the method ref methodCallLocations.add(i); // we have found a method call // if we have not already stored a reference to our new // method in the const pool if (newCallLocation == null) { methodReflectionLocation = pool.addClassInfo("org.fakereplace.reflection.MethodReflection"); int nt = pool.addNameAndTypeInfo("fakeCallRequired", "(Ljava/lang/reflect/Method;)Z"); fakeCallRequiredLocation = pool.addMethodrefInfo(methodReflectionLocation, nt); newCallLocation = pool.addNameAndTypeInfo(METHOD_NAME, REPLACED_METHOD_DESCRIPTOR); } } } } } // this means we found an instance of the call, now we have to iterate // through the methods and replace instances of the call if (newCallLocation != null) { List<MethodInfo> methods = file.getMethods(); for (MethodInfo m : methods) { try { // ignore abstract methods if (m.getCodeAttribute() == null) { continue; } CodeIterator it = m.getCodeAttribute().iterator(); while (it.hasNext()) { // loop through the bytecode int index = it.next(); int op = it.byteAt(index); // if the bytecode is a method invocation if (op == CodeIterator.INVOKEVIRTUAL) { int val = it.s16bitAt(index + 1); // if the method call is one of the methods we are // replacing if (methodCallLocations.contains(val)) { Bytecode b = new Bytecode(file.getConstPool()); // our stack looks like Method, instance,params // we need Method, instance, params , Method b.add(Opcode.DUP_X2); b.add(Opcode.POP); b.add(Opcode.DUP_X2); b.add(Opcode.POP); b.add(Opcode.DUP_X2); b.addInvokestatic( methodReflectionLocation, "fakeCallRequired", "(Ljava/lang/reflect/Method;)Z"); b.add(Opcode.IFEQ); JumpMarker performRealCall = JumpUtils.addJumpInstruction(b); // now perform the fake call b.addInvokestatic(methodReflectionLocation, "invoke", REPLACED_METHOD_DESCRIPTOR); b.add(Opcode.GOTO); JumpMarker finish = JumpUtils.addJumpInstruction(b); performRealCall.mark(); b.addInvokevirtual(Method.class.getName(), METHOD_NAME, METHOD_DESCRIPTOR); finish.mark(); it.writeByte(CodeIterator.NOP, index); it.writeByte(CodeIterator.NOP, index + 1); it.writeByte(CodeIterator.NOP, index + 2); it.insert(b.get()); } } } m.getCodeAttribute().computeMaxStack(); } catch (Exception e) { log.error("Bad byte code transforming " + file.getName()); e.printStackTrace(); } } return true; } else { return false; } }