private static CtMethod delegator0(CtMethod delegate, CtClass declaring) throws CannotCompileException, NotFoundException { MethodInfo deleInfo = delegate.getMethodInfo2(); String methodName = deleInfo.getName(); String desc = deleInfo.getDescriptor(); ConstPool cp = declaring.getClassFile2().getConstPool(); MethodInfo minfo = new MethodInfo(cp, methodName, desc); minfo.setAccessFlags(deleInfo.getAccessFlags()); ExceptionsAttribute eattr = deleInfo.getExceptionsAttribute(); if (eattr != null) minfo.setExceptionsAttribute((ExceptionsAttribute) eattr.copy(cp, null)); Bytecode code = new Bytecode(cp, 0, 0); boolean isStatic = Modifier.isStatic(delegate.getModifiers()); CtClass deleClass = delegate.getDeclaringClass(); CtClass[] params = delegate.getParameterTypes(); int s; if (isStatic) { s = code.addLoadParameters(params, 0); code.addInvokestatic(deleClass, methodName, desc); } else { code.addLoad(0, deleClass); s = code.addLoadParameters(params, 1); code.addInvokespecial(deleClass, methodName, desc); } code.addReturn(delegate.getReturnType()); code.setMaxLocals(++s); code.setMaxStack(s < 2 ? 2 : s); // for a 2-word return value minfo.setCodeAttribute(code.toCodeAttribute()); return new CtMethod(minfo, declaring); }
/* MemberCodeGen overrides this method. */ protected void atClassObject2(String cname) throws CompileError { int start = bytecode.currentPc(); bytecode.addLdc(cname); bytecode.addInvokestatic("java.lang.Class", "forName", "(Ljava/lang/String;)Ljava/lang/Class;"); int end = bytecode.currentPc(); bytecode.addOpcode(Opcode.GOTO); int pc = bytecode.currentPc(); bytecode.addIndex(0); // correct later bytecode.addExceptionHandler( start, end, bytecode.currentPc(), "java.lang.ClassNotFoundException"); /* -- the following code is for inlining a call to DotClass.fail(). int var = getMaxLocals(); incMaxLocals(1); bytecode.growStack(1); bytecode.addAstore(var); bytecode.addNew("java.lang.NoClassDefFoundError"); bytecode.addOpcode(DUP); bytecode.addAload(var); bytecode.addInvokevirtual("java.lang.ClassNotFoundException", "getMessage", "()Ljava/lang/String;"); bytecode.addInvokespecial("java.lang.NoClassDefFoundError", "<init>", "(Ljava/lang/String;)V"); */ bytecode.growStack(1); bytecode.addInvokestatic( "javassist.runtime.DotClass", "fail", "(Ljava/lang/ClassNotFoundException;)" + "Ljava/lang/NoClassDefFoundError;"); bytecode.addOpcode(ATHROW); bytecode.write16bit(pc, bytecode.currentPc() - pc + 1); }
private void convToString(int type, int dim) throws CompileError { final String method = "valueOf"; if (isRefType(type) || dim > 0) bytecode.addInvokestatic(javaLangString, method, "(Ljava/lang/Object;)Ljava/lang/String;"); else if (type == DOUBLE) bytecode.addInvokestatic(javaLangString, method, "(D)Ljava/lang/String;"); else if (type == FLOAT) bytecode.addInvokestatic(javaLangString, method, "(F)Ljava/lang/String;"); else if (type == LONG) bytecode.addInvokestatic(javaLangString, method, "(J)Ljava/lang/String;"); else if (type == BOOLEAN) bytecode.addInvokestatic(javaLangString, method, "(Z)Ljava/lang/String;"); else if (type == CHAR) bytecode.addInvokestatic(javaLangString, method, "(C)Ljava/lang/String;"); else if (type == VOID) throw new CompileError("void type expression"); else /* INT, BYTE, SHORT */ bytecode.addInvokestatic(javaLangString, method, "(I)Ljava/lang/String;"); }
@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; }
/** * Calls methodHandler.invoke with a null method parameter in order to get the underlying * instance. The invocation is then forwarded to this instance with generated bytecode. */ protected Bytecode createForwardingMethodBody(ClassFile file, MethodInformation methodInfo) throws NotFoundException { Method method = methodInfo.getMethod(); // we can only use bytecode based invocation for some methods // at the moment we restrict it solely to public methods with public // return and parameter types boolean bytecodeInvocationAllowed = Modifier.isPublic(method.getModifiers()) && Modifier.isPublic(method.getReturnType().getModifiers()); for (Class<?> paramType : method.getParameterTypes()) { if (!Modifier.isPublic(paramType.getModifiers())) { bytecodeInvocationAllowed = false; break; } } if (!bytecodeInvocationAllowed) { return createInterceptorBody(file, methodInfo); } Bytecode b = new Bytecode(file.getConstPool()); int localCount = MethodUtils.calculateMaxLocals(method) + 1; // create a new interceptor invocation context whenever we invoke a method on a client proxy // we use a try-catch block in order to make sure that endInterceptorContext() is invoked // regardless whether // the method has succeeded or not int start = b.currentPc(); b.addInvokestatic( "org.jboss.weld.bean.proxy.InterceptionDecorationContext", "startInterceptorContext", "()V"); b.add(Opcode.ALOAD_0); b.addGetfield( file.getName(), "methodHandler", DescriptorUtils.classToStringRepresentation(MethodHandler.class)); // pass null arguments to methodHandler.invoke b.add(Opcode.ALOAD_0); b.add(Opcode.ACONST_NULL); b.add(Opcode.ACONST_NULL); b.add(Opcode.ACONST_NULL); // now we have all our arguments on the stack // lets invoke the method b.addInvokeinterface( MethodHandler.class.getName(), "invoke", "(Ljava/lang/Object;Ljava/lang/reflect/Method;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;", 5); b.addCheckcast(methodInfo.getDeclaringClass()); // now we should have the target bean instance on top of the stack // we need to dup it so we still have it to compare to the return value b.add(Opcode.DUP); // lets create the method invocation String methodDescriptor = methodInfo.getDescriptor(); BytecodeUtils.loadParameters(b, methodDescriptor); if (method.getDeclaringClass().isInterface()) { b.addInvokeinterface( methodInfo.getDeclaringClass(), methodInfo.getName(), methodDescriptor, method.getParameterTypes().length + 1); } else { b.addInvokevirtual(methodInfo.getDeclaringClass(), methodInfo.getName(), methodDescriptor); } // end the interceptor context, everything was fine b.addInvokestatic( "org.jboss.weld.bean.proxy.InterceptionDecorationContext", "endInterceptorContext", "()V"); // jump over the catch block b.addOpcode(Opcode.GOTO); JumpMarker gotoEnd = JumpUtils.addJumpInstruction(b); // create catch block b.addExceptionHandler(start, b.currentPc(), b.currentPc(), 0); b.addInvokestatic( "org.jboss.weld.bean.proxy.InterceptionDecorationContext", "endInterceptorContext", "()V"); b.add(Opcode.ATHROW); // update the correct address to jump over the catch block gotoEnd.mark(); // if this method returns a primitive we just return if (method.getReturnType().isPrimitive()) { BytecodeUtils.addReturnInstruction(b, methodInfo.getReturnType()); } else { // otherwise we have to check that the proxy is not returning 'this; // now we need to check if the proxy has return 'this' and if so return // an // instance of the proxy. // currently we have result, beanInstance on the stack. b.add(Opcode.DUP_X1); // now we have result, beanInstance, result // we need to compare result and beanInstance // first we need to build up the inner conditional that just returns // the // result b.add(Opcode.IF_ACMPEQ); JumpMarker returnInstruction = JumpUtils.addJumpInstruction(b); BytecodeUtils.addReturnInstruction(b, methodInfo.getReturnType()); returnInstruction.mark(); // now add the case where the proxy returns 'this'; b.add(Opcode.ALOAD_0); b.addCheckcast(methodInfo.getMethod().getReturnType().getName()); BytecodeUtils.addReturnInstruction(b, methodInfo.getReturnType()); } if (b.getMaxLocals() < localCount) { b.setMaxLocals(localCount); } return b; }
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; } }