/** * Gets the correct return instruction for a proxy method * * @param pool * @param methodDescriptor */ public static void addReturnProxyMethod(String methodDescriptor, Bytecode b) { String ret = DescriptorUtils.getReturnType(methodDescriptor); // if the return type is larger than one then it is not a primitive // so just do an ARETURN if (ret.length() != 1) { b.addCheckcast(DescriptorUtils.getReturnTypeInJvmFormat(methodDescriptor)); b.add(Opcode.ARETURN); return; } // void methods are special if (ret.equals("V")) { b.add(Opcode.RETURN); return; } else { // unbox the primitive type char tp = ret.charAt(0); Boxing.unbox(b, tp); if (tp == 'F') { b.add(Opcode.FRETURN); } else if (tp == 'D') { b.add(Opcode.DRETURN); } else if (tp == 'J') { b.add(Opcode.LRETURN); } else { b.add(Opcode.IRETURN); } return; } }
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); } } } }
public static void pushParametersIntoArray(Bytecode bc, String methodDescriptor) { String[] params = DescriptorUtils.descriptorStringToParameterArray(methodDescriptor); // now we need an array: bc.addIconst(params.length); bc.addAnewarray("java.lang.Object"); // now we have our array sitting on top of the stack // we need to stick our parameters into it. We do this is reverse // as we can't pull them from the bottom of the stack for (int i = params.length - 1; i >= 0; --i) { if (DescriptorUtils.isWide(params[i])) { // dup the array below the wide bc.add(Opcode.DUP_X2); // now do it again so we have two copies bc.add(Opcode.DUP_X2); // now pop it, the is the equivalent of a wide swap bc.add(Opcode.POP); } else { // duplicate the array to place 3 bc.add(Opcode.DUP_X1); // now swap bc.add(Opcode.SWAP); } // now the parameter is above the array // box it if nessesary if (DescriptorUtils.isPrimitive(params[i])) { Boxing.box(bc, params[i].charAt(0)); } // add the array index bc.addIconst(i); bc.add(Opcode.SWAP); bc.add(Opcode.AASTORE); // we still have the array on the top of the stack becuase we // duplicated it earlier } }
/** * add a bogus constructor call to a bytecode sequence so a constructor can pass bytecode * validation * * @param bytecode */ public static boolean addBogusConstructorCall(ClassFile file, Bytecode code) { MethodInfo constructorToCall = null; for (Object meth : file.getMethods()) { MethodInfo m = (MethodInfo) meth; if (m.getName().equals("<init>")) { constructorToCall = m; break; } } if (constructorToCall == null) { return false; } // push this onto the stack code.add(Bytecode.ALOAD_0); String[] params = DescriptorUtils.descriptorStringToParameterArray(constructorToCall.getDescriptor()); for (String p : params) { // int char short boolean byte if (p.equals("I") || p.equals("C") || p.equals("S") || p.equals("Z") || p.equals("B")) { // push integer 0 code.add(Opcode.ICONST_0); } // long else if (p.equals("J")) { code.add(Opcode.LCONST_0); } // double else if (p.equals("D")) { code.add(Opcode.DCONST_0); } // float else if (p.equals("F")) { code.add(Opcode.FCONST_0); } // arrays and reference types else { code.add(Opcode.ACONST_NULL); } } // all our args should be pushed onto the stack, call the constructor code.addInvokespecial(file.getName(), "<init>", constructorToCall.getDescriptor()); code.add(Opcode.RETURN); return true; }
@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; }
/** Add a method to a class that simply delegates to the parent implementation of the method */ public static void addDelegatingMethod(ClassFile file, MethodData mData) throws BadBytecode, DuplicateMemberException { MethodInfo m = new MethodInfo(file.getConstPool(), mData.getMethodName(), mData.getDescriptor()); m.setAccessFlags(mData.getAccessFlags()); Bytecode code = new Bytecode(file.getConstPool()); String[] params = DescriptorUtils.descriptorStringToParameterArray(mData.getDescriptor()); code.add(Opcode.ALOAD_0); // push this int count = 1; // zero is the this pointer int maxLocals = 1; for (String p : params) { // int char short boolean byte if (p.equals("I") || p.equals("C") || p.equals("S") || p.equals("Z") || p.equals("B")) { // push integer 0 code.addIload(count); maxLocals++; } // long else if (p.equals("J")) { code.addLload(count); maxLocals += 2; count++; } // double else if (p.equals("D")) { code.addDload(count); maxLocals += 2; count++; } // float else if (p.equals("F")) { code.addFload(count); maxLocals++; } // arrays and reference types else { code.addAload(count); maxLocals++; } count++; } code.addInvokespecial(file.getSuperclass(), mData.getMethodName(), mData.getDescriptor()); String p = DescriptorUtils.getReturnTypeInJvmFormat(mData.getDescriptor()); // int char short boolean byte if (p.equals("I") || p.equals("C") || p.equals("S") || p.equals("Z") || p.equals("B")) { code.add(Opcode.IRETURN); } // long else if (p.equals("J")) { code.add(Opcode.LRETURN); } // double else if (p.equals("D")) { code.add(Opcode.DRETURN); } // float else if (p.equals("F")) { code.add(Opcode.FRETURN); } // void else if (p.equals("V")) { code.add(Opcode.RETURN); } // arrays and reference types else { code.add(Opcode.ARETURN); } CodeAttribute ca = code.toCodeAttribute(); ca.computeMaxStack(); ca.setMaxLocals(maxLocals); m.setCodeAttribute(ca); file.addMethod(m); }