private Bytecode createAbstractMethodCode(ClassFile file, MethodInformation method) throws NotFoundException { if ((delegateField != null) && (!Modifier.isPrivate(delegateField.getModifiers()))) { // Call the corresponding method directly on the delegate Bytecode b = new Bytecode(file.getConstPool()); int localVariables = MethodUtils.calculateMaxLocals(method.getMethod()); b.setMaxLocals(localVariables); // load the delegate field b.addAload(0); b.addGetfield( file.getName(), delegateField.getName(), DescriptorUtils.classToStringRepresentation(delegateField.getType())); // load the parameters BytecodeUtils.loadParameters(b, method.getDescriptor()); // invoke the delegate method b.addInvokeinterface( delegateField.getType().getName(), method.getName(), method.getDescriptor(), localVariables); // return the value if applicable BytecodeUtils.addReturnInstruction(b, method.getReturnType()); return b; } else { if (!Modifier.isPrivate(method.getMethod().getModifiers())) { // if it is a parameter injection point we need to initalize the // injection point then handle the method with the method handler return createAbstractMethodHandler(file, method); } else { // if the delegate is private we need to use the method handler return createInterceptorBody(file, method); } } }
/** * calls _initMH on the method handler and then stores the result in the methodHandler field as * then new methodHandler */ private Bytecode createMethodHandlerInitializerBody(ClassFile proxyClassType) { Bytecode b = new Bytecode(proxyClassType.getConstPool(), 1, 2); b.add(Opcode.ALOAD_0); StaticMethodInformation methodInfo = new StaticMethodInformation( "_initMH", new Class[] {Object.class}, void.class, proxyClassType.getName()); invokeMethodHandler(proxyClassType, b, methodInfo, false, DEFAULT_METHOD_RESOLVER); b.addCheckcast("javassist/util/proxy/MethodHandler"); b.addPutfield( proxyClassType.getName(), "methodHandler", DescriptorUtils.classToStringRepresentation(MethodHandler.class)); b.add(Opcode.RETURN); log.trace("Created MH initializer body for decorator proxy: " + getBeanType()); return b; }
/** * When creates the delegate initializer code when the delegate is injected into a method. * * <p>super initializer method is called first, and then _initMH is called * * @param file * @param intializerMethodInfo * @param delegateParameterPosition * @return */ private Bytecode createDelegateInitializerCode( ClassFile file, MethodInformation intializerMethodInfo, int delegateParameterPosition) { Bytecode b = new Bytecode(file.getConstPool()); // we need to push all the pareters on the stack to call the corresponding // superclass arguments b.addAload(0); // load this int localVariables = 1; int actualDelegateParamterPosition = 0; for (int i = 0; i < intializerMethodInfo.getMethod().getParameterTypes().length; ++i) { if (i == delegateParameterPosition) { // figure out the actual position of the delegate in the local // variables actualDelegateParamterPosition = localVariables; } Class<?> type = intializerMethodInfo.getMethod().getParameterTypes()[i]; BytecodeUtils.addLoadInstruction( b, DescriptorUtils.classToStringRepresentation(type), localVariables); if (type == long.class || type == double.class) { localVariables = localVariables + 2; } else { localVariables++; } } b.addInvokespecial( file.getSuperclass(), intializerMethodInfo.getName(), intializerMethodInfo.getDescriptor()); // if this method returns a value it is now sitting on top of the stack // we will leave it there are return it later // now we need to call _initMH b.addAload(0); // load this b.addAload(actualDelegateParamterPosition); // load the delegate b.addInvokevirtual(file.getName(), "_initMH", "(Ljava/lang/Object;)V"); // return the object from the top of the stack that we got from calling // the superclass method earlier BytecodeUtils.addReturnInstruction(b, intializerMethodInfo.getReturnType()); b.setMaxLocals(localVariables); return b; }
/** * 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; }