Package org.apache.aries.proxy.impl.common

Source Code of org.apache.aries.proxy.impl.common.AbstractWovenProxyMethodAdapter

/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership.  The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License.  You may obtain a copy of the License at
*
*   http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied.  See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.aries.proxy.impl.common;

import static org.apache.aries.proxy.impl.common.AbstractWovenProxyAdapter.DISPATCHER_FIELD;
import static org.apache.aries.proxy.impl.common.AbstractWovenProxyAdapter.DISPATCHER_TYPE;
import static org.apache.aries.proxy.impl.common.AbstractWovenProxyAdapter.LISTENER_FIELD;
import static org.apache.aries.proxy.impl.common.AbstractWovenProxyAdapter.LISTENER_TYPE;
import static org.apache.aries.proxy.impl.common.AbstractWovenProxyAdapter.METHOD_TYPE;
import static org.apache.aries.proxy.impl.common.AbstractWovenProxyAdapter.NO_ARGS;
import static org.apache.aries.proxy.impl.common.AbstractWovenProxyAdapter.OBJECT_TYPE;
import static org.apache.aries.proxy.impl.common.AbstractWovenProxyAdapter.THROWABLE_INAME;
import static org.apache.aries.proxy.impl.common.AbstractWovenProxyAdapter.WOVEN_PROXY_IFACE_TYPE;
import static org.objectweb.asm.Opcodes.ACONST_NULL;
import static org.objectweb.asm.Opcodes.IFNE;

import java.util.Arrays;

import org.apache.aries.proxy.InvocationListener;
import org.apache.aries.proxy.impl.NLS;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.GeneratorAdapter;
import org.objectweb.asm.commons.Method;
/**
* This class weaves dispatch and listener code into a method, there are two known
* subclasses {@link WovenProxyMethodAdapter} is used for weaving instance methods
* {@link InterfaceUsingWovenProxyMethodAdapter} is used to provide a delegating
* implementation of an interface method.
*
* Roughly (but not exactly because it's easier to write working bytecode
* if you don't have to exactly recreate the Java!) this is trying to
* do the following: <code>
*
*     
    if(dispatcher != null) {
      int returnValue;
      Object token = null;
      boolean inInvoke = false;
      try {
        Object toInvoke = dispatcher.call();
        if(listener != null)
          token = listener.preInvoke(toInvoke, method, args);
       
        inInvoke = true;
        returnValue = ((Template) toInvoke).doStuff(args);
        inInvoke = false;
       
        if(listener != null)
          listener.postInvoke(token, toInvoke, method, args);
       
      } catch (Throwable e){
        // whether the the exception is an error is an application decision
        // if we catch an exception we decide carefully which one to
        // throw onwards
        Throwable exceptionToRethrow = null;
        // if the exception came from a precall or postcall
        // we will rethrow it
        if (!inInvoke) {
          exceptionToRethrow = e;
        }
        // if the exception didn't come from precall or postcall then it
        // came from invoke
        // we will rethrow this exception if it is not a runtime
        // exception, but we must unwrap InvocationTargetExceptions
        else {
          if (!(e instanceof RuntimeException)) {
            exceptionToRethrow = e;
          }
        }
        try {
          if(listener != null)
            listener.postInvokeExceptionalReturn(token, method, null, e);
        } catch (Throwable f) {
          // we caught an exception from
          // postInvokeExceptionalReturn
          // if we haven't already chosen an exception to rethrow then
          // we will throw this exception
          if (exceptionToRethrow == null) {
            exceptionToRethrow = f;
          }
        }
        // if we made it this far without choosing an exception we
        // should throw e
        if (exceptionToRethrow == null) {
          exceptionToRethrow = e;
        }
        throw exceptionToRethrow;
      }
    }
   
    //...original method body
      </code>
*  
*/
public abstract class AbstractWovenProxyMethodAdapter extends GeneratorAdapter
{
  /** The type of a RuntimeException */
  private static final Type RUNTIME_EX_TYPE = Type.getType(RuntimeException.class);
  private static final Type THROWABLE_TYPE = Type.getType(Throwable.class);
 
  /** The postInvoke method of an {@link InvocationListener} */
  private static final Method POST_INVOKE_METHOD = getAsmMethodFromClass(InvocationListener.class, "postInvoke", Object.class,
      Object.class, java.lang.reflect.Method.class, Object.class);
  /** The postInvokeExceptionalReturn method of an {@link InvocationListener} */
  private static final Method POST_INVOKE_EXCEPTIONAL_METHOD = getAsmMethodFromClass(InvocationListener.class,
      "postInvokeExceptionalReturn", Object.class, Object.class,
      java.lang.reflect.Method.class, Throwable.class);
  /** The preInvoke method of an {@link InvocationListener} */
  private static final Method PRE_INVOKE_METHOD = getAsmMethodFromClass(InvocationListener.class, "preInvoke", Object.class,
      java.lang.reflect.Method.class, Object[].class);
 
 
  /** The name of the static field that stores our {@link java.lang.reflect.Method} */
  private final String methodStaticFieldName;
  /** The current method */
  protected final Method currentTransformMethod;
  /** The type of <code>this</code> */
  protected final Type typeBeingWoven;
  /** True if this is a void method */
  private final boolean isVoid;

  //ints for local store
  /** The local we use to store the {@link InvocationListener} token */
  private int preInvokeReturnedToken;
  /** The local we use to note whether we are in the original method body or not */
  private int inNormalMethod;
  /** The local we use to store the invocation target to dispatch to */
  private int dispatchTarget;
  /** The local for storing our method's result */
  private int normalResult;

  //the Labels we need for jumping around the pre/post/postexception and current method code
  /** This marks the start of the try/catch around the pre/postInvoke*/
  private final Label beginTry = new Label();
  /** This marks the end of the try/catch around the pre/postInvoke*/
  private final Label endTry = new Label();

  /** The return type of this method */
  private final Type returnType;
 
  /**
   * Construct a new method adapter
   * @param mv - the method visitor to write to
   * @param access - the access modifiers on this method
   * @param name - the name of this method
   * @param desc - the descriptor of this method
   * @param methodStaticFieldName - the name of the static field that will hold
   *                                the {@link java.lang.reflect.Method} representing
   *                                this method.
   * @param currentTransformMethod - the ASM representation of this method
   * @param proxyType - the type being woven that contains this method
   */
  public AbstractWovenProxyMethodAdapter(MethodVisitor mv, int access, String name, String desc,
      String methodStaticFieldName, Method currentTransformMethod, Type typeBeingWoven)
  {
    super(mv, access, name, desc);
    this.methodStaticFieldName = methodStaticFieldName;
    this.currentTransformMethod = currentTransformMethod;
    returnType = currentTransformMethod.getReturnType();
    isVoid = returnType.getSort() == Type.VOID;
    this.typeBeingWoven = typeBeingWoven;
  }

  @Override
  public abstract void visitCode();

  @Override
  public abstract void visitMaxs(int stack, int locals);
 
  /**
   * Write out the bytecode instructions necessary to do the dispatch.
   * We know the dispatcher is non-null, and we need a try/catch around the
   * invocation and listener calls.
   */
  protected final void writeDispatcher() {
    // Setup locals we will use in the dispatch
    setupLocals();
   
    //Write the try catch block
    visitTryCatchBlock(beginTry, endTry, endTry, THROWABLE_INAME);
    mark(beginTry);
   
    //Start dispatching, get the target object and store it
    loadThis();
    getField(typeBeingWoven, DISPATCHER_FIELD, DISPATCHER_TYPE);
    invokeInterface(DISPATCHER_TYPE, new Method("call", OBJECT_TYPE, NO_ARGS));
    storeLocal(dispatchTarget);
   
    //Pre-invoke, invoke, post-invoke, return
    writePreInvoke();
    //Start the real method
    push(true);
    storeLocal(inNormalMethod);
   
    //Dispatch the method and store the result (null for void)
    loadLocal(dispatchTarget);
    checkCast(getTypeToCastTo());
    loadArgs();
    if(isTypeToCastToInterface()) {
      invokeInterface(getTypeToCastTo(), currentTransformMethod);
    } else {
      invokeVirtual(getTypeToCastTo(), currentTransformMethod);
    }
    if(isVoid) {
      visitInsn(ACONST_NULL);
    }
    storeLocal(normalResult);
   
    // finish the real method and post-invoke
    push(false);
    storeLocal(inNormalMethod);
    writePostInvoke();
   
    //Return, with the return value if necessary
    if(!!!isVoid) {
      loadLocal(normalResult);
    }
    returnValue();
   
    //End of our try, start of our catch
    mark(endTry);
    writeMethodCatchHandler();
  }
 
  /**
   * Get the type to which the class should be cast for delegation
   * @return
   */
  protected abstract Type getTypeToCastTo();
 
  protected abstract boolean isTypeToCastToInterface();
 
  /**
   * Setup the normalResult, inNormalMethod, preInvokeReturnedToken and
   * dispatch target locals.
   */
  private final void setupLocals() {
    if (isVoid){
      normalResult = newLocal(OBJECT_TYPE);
    } else{
      normalResult = newLocal(returnType);
    }
   
    preInvokeReturnedToken = newLocal(OBJECT_TYPE);
    visitInsn(ACONST_NULL);
    storeLocal(preInvokeReturnedToken);
   
    inNormalMethod = newLocal(Type.BOOLEAN_TYPE);
    push(false);
    storeLocal(inNormalMethod);
   
    dispatchTarget = newLocal(OBJECT_TYPE);
    visitInsn(ACONST_NULL);
    storeLocal(dispatchTarget);
  }

  /**
   * Begin trying to invoke the listener, if the listener is
   * null the bytecode will branch to the supplied label, other
   * otherwise it will load the listener onto the stack.
   * @param l The label to branch to
   */
  private final void beginListenerInvocation(Label l) {
    //If there's no listener then skip invocation
    loadThis();
    getField(typeBeingWoven, LISTENER_FIELD, LISTENER_TYPE);
    ifNull(l);
    loadThis();
    getField(typeBeingWoven, LISTENER_FIELD, LISTENER_TYPE);
  }

  /**
   * Write out the preInvoke. This copes with the listener being null
   */
  private final void writePreInvoke() {
    //The place to go if the listener is null
    Label nullListener = newLabel();
    beginListenerInvocation(nullListener);

    // The listener is on the stack, we need (target, method, args)
   
    loadLocal(dispatchTarget);
    getStatic(typeBeingWoven, methodStaticFieldName, METHOD_TYPE);
    loadArgArray();
   
    //invoke it and store the token returned
    invokeInterface(LISTENER_TYPE, PRE_INVOKE_METHOD);
    storeLocal(preInvokeReturnedToken);
   
    mark(nullListener);
  }
 
  /**
   * Write out the postInvoke. This copes with the listener being null
   */
  private final void writePostInvoke() {
    //The place to go if the listener is null
    Label nullListener = newLabel();
    beginListenerInvocation(nullListener);
   
    // The listener is on the stack, we need (token, target, method, result)
   
    loadLocal(preInvokeReturnedToken);
    loadLocal(dispatchTarget);
    getStatic(typeBeingWoven, methodStaticFieldName, METHOD_TYPE);
    loadLocal(normalResult);
   
    //If the result a primitive then we need to box it
    if (!!!isVoid && returnType.getSort() != Type.OBJECT && returnType.getSort() != Type.ARRAY){
      box(returnType);
    }
   
    //invoke the listener
    invokeInterface(LISTENER_TYPE, POST_INVOKE_METHOD);
   
    mark(nullListener);
  }
 
  /**
   * Write the catch handler for our method level catch, this runs the exceptional
   * post-invoke if there is a listener, and throws the correct exception at the
   * end
   */
  private final void writeMethodCatchHandler() {
   
    //Store the original exception
    int originalException = newLocal(THROWABLE_TYPE);
    storeLocal(originalException);
   
    //Start by initialising exceptionToRethrow
    int exceptionToRethrow = newLocal(THROWABLE_TYPE);
    visitInsn(ACONST_NULL);
    storeLocal(exceptionToRethrow);
   
    //We need another try catch around the postInvokeExceptionalReturn, so here
    //are some labels and the declaration for it
    Label beforeInvoke = newLabel();
    Label afterInvoke = newLabel();
    visitTryCatchBlock(beforeInvoke, afterInvoke, afterInvoke, THROWABLE_INAME);
   
    //If we aren't in normal flow then set exceptionToRethrow = originalException
    loadLocal(inNormalMethod);
    Label inNormalMethodLabel = newLabel();
    // Jump if not zero (false)
    visitJumpInsn(IFNE, inNormalMethodLabel);
    loadLocal(originalException);
    storeLocal(exceptionToRethrow);
    goTo(beforeInvoke);
   
    mark(inNormalMethodLabel);
    //We are in normal method flow so set exceptionToRethrow = originalException
    //if originalException is not a runtime exception
    loadLocal(originalException);
    instanceOf(RUNTIME_EX_TYPE);
    //If false then store original in toThrow, otherwise go to beforeInvoke
    visitJumpInsn(IFNE, beforeInvoke);
    loadLocal(originalException);
    storeLocal(exceptionToRethrow);
    goTo(beforeInvoke);
    //Setup of variables finished, begin try/catch
      
    //Mark the start of our try
    mark(beforeInvoke);
    //Begin invocation of the listener, jump to throw if null
    Label throwSelectedException = newLabel();
    beginListenerInvocation(throwSelectedException);
   
    //We have a listener, so call it (token, target, method, exception)
    loadLocal(preInvokeReturnedToken);
    loadLocal(dispatchTarget);
    getStatic(typeBeingWoven, methodStaticFieldName, METHOD_TYPE);
    loadLocal(originalException);
    invokeInterface(LISTENER_TYPE, POST_INVOKE_EXCEPTIONAL_METHOD);
    goTo(throwSelectedException);
   
    mark(afterInvoke);
    //catching another exception replaces the original
    storeLocal(originalException);

    //Throw exceptionToRethrow if it isn't null, or the original if it is
    Label throwException = newLabel();
    mark(throwSelectedException);
    loadLocal(exceptionToRethrow);
    ifNonNull(throwException);
    loadLocal(originalException);
    storeLocal(exceptionToRethrow);
   
    mark(throwException);
    loadLocal(exceptionToRethrow);
    throwException();
  }
 
  /**
   * This method unwraps woven proxy instances for use in the right-hand side
   * of equals methods
   */
  protected final void unwrapEqualsArgument() {
   
    //Create and initialize a local for our work
    int unwrapLocal = newLocal(OBJECT_TYPE);
    visitInsn(ACONST_NULL);
    storeLocal(unwrapLocal);
   
    Label startUnwrap = newLabel();
    mark(startUnwrap);
    //Load arg and check if it is a WovenProxy instances
    loadArg(0);
    instanceOf(WOVEN_PROXY_IFACE_TYPE);
    Label unwrapFinished = newLabel();
    //Jump if zero (false)
    visitJumpInsn(Opcodes.IFEQ, unwrapFinished);
    //Arg is a wovenProxy, if it is the same as last time then we're done
    loadLocal(unwrapLocal);
    loadArg(0);
    ifCmp(OBJECT_TYPE, EQ, unwrapFinished);
    //Not the same, store current arg in unwrapLocal for next loop
    loadArg(0);
    storeLocal(unwrapLocal);
   
    //So arg is a WovenProxy, but not the same as last time, cast it and store
    //the result of unwrap.call in the arg
    loadArg(0);
    checkCast(WOVEN_PROXY_IFACE_TYPE);
    //Now unwrap
    invokeInterface(WOVEN_PROXY_IFACE_TYPE, new Method("org_apache_aries_proxy_weaving_WovenProxy_unwrap",
        DISPATCHER_TYPE, NO_ARGS));
   
    //Now we may have a Callable to invoke
    int callable = newLocal(DISPATCHER_TYPE);
    storeLocal(callable);
    loadLocal(callable);
    ifNull(unwrapFinished);
    loadLocal(callable);
    invokeInterface(DISPATCHER_TYPE, new Method("call",
        OBJECT_TYPE, NO_ARGS));
    //Store the result and we're done (for this iteration)
    storeArg(0);
    goTo(startUnwrap);
   
    mark(unwrapFinished);
  }

  /**
   * A utility method for getting an ASM method from a Class
   * @param clazz the class to search
   * @param name The method name
   * @param argTypes The method args
   * @return
   */
  private static final Method getAsmMethodFromClass(Class<?> clazz, String name, Class<?>... argTypes)
  {
    //get the java.lang.reflect.Method to get the types
    java.lang.reflect.Method ilMethod = null;
    try {
      ilMethod = clazz.getMethod(name, argTypes);
    } catch (Exception e) {
      //Should be impossible!
      throw new RuntimeException(NLS.MESSAGES.getMessage("error.finding.invocation.listener.method", name, Arrays.toString(argTypes)), e);
    }
    //get the ASM method
    return new Method(name, Type.getReturnType(ilMethod), Type.getArgumentTypes(ilMethod));
  }
}
TOP

Related Classes of org.apache.aries.proxy.impl.common.AbstractWovenProxyMethodAdapter

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.