private void processMethod()

in proxy/proxy-impl/src/main/java/org/apache/aries/proxy/impl/gen/ProxySubclassAdapter.java [425:658]


  private void processMethod(int access, String name, String desc, String signature,
      String[] exceptions)
  {
    LOGGER.debug(Constants.LOG_ENTRY, "processMethod", new Object[] { access, name, desc,
        signature, exceptions });

    LOGGER.debug("Processing method: {} with descriptor {}", name, desc);

    // identify the target method parameters and return type
    Method currentTransformMethod = new Method(name, desc);
    Type[] targetMethodParameters = currentTransformMethod.getArgumentTypes();
    Type returnType = currentTransformMethod.getReturnType();

    // we create a static field for each method we encounter with a name
    // like method_parm1_parm2...
    StringBuilder methodStaticFieldNameBuilder = new StringBuilder(name);
    // for each a parameter get the name and add it to the field removing
    // the dots first
    for (Type t : targetMethodParameters) {
      methodStaticFieldNameBuilder.append("_");
      methodStaticFieldNameBuilder.append(t.getClassName().replaceAll("\\[\\]", "Array")
          .replaceAll("\\.", ""));
    }
    String methodStaticFieldName = methodStaticFieldNameBuilder.toString();

    // add a private static field for the method
    cv.visitField(ACC_PRIVATE | ACC_STATIC, methodStaticFieldName, METHOD_TYPE.getDescriptor(),
        null, null);

    // visit the method using the class writer, delegated through the method
    // visitor and generator
    // modify the method access so that any native methods aren't
    // described as native
    // since they won't be native in proxy form
    // also stop methods being marked synchronized on the proxy as they will
    // be sync
    // on the real object
    int newAccess = access & (~ACC_NATIVE) & (~ACC_SYNCHRONIZED);
    MethodVisitor mv = cv.visitMethod(newAccess, name, desc, signature, exceptions);
    // use a GeneratorAdapter to build the invoke call directly in byte code
    GeneratorAdapter methodAdapter = new GeneratorAdapter(mv, newAccess, name, desc);

    /*
     * Stage 1 creates the bytecode for adding the reflected method of the
     * superclass to a static field in the subclass: private static Method
     * methodName_parm1_parm2... = null; static{ methodName_parm1_parm2... =
     * superClass.getDeclaredMethod(methodName,new Class[]{method args}; }
     * 
     * Stage 2 is to call the ih.invoke(this,methodName_parm1_parm2,args) in
     * the new subclass methods Stage 3 is to cast the return value to the
     * correct type
     */

    /*
     * Stage 1 use superClass.getMethod(methodName,new Class[]{method args}
     * from the Class object on the stack
     */

    // load the static superclass Class onto the stack
    staticAdapter.getStatic(newClassType, currentClassFieldName, CLASS_TYPE);

    // push the method name string arg onto the stack
    staticAdapter.push(name);

    // create an array of the method parm class[] arg
    staticAdapter.push(targetMethodParameters.length);
    staticAdapter.newArray(CLASS_TYPE);
    int index = 0;
    for (Type t : targetMethodParameters) {
      staticAdapter.dup();
      staticAdapter.push(index);
      switch (t.getSort())
      {
        case Type.BOOLEAN:
          staticAdapter.getStatic(Type.getType(java.lang.Boolean.class), "TYPE", CLASS_TYPE);
          break;
        case Type.BYTE:
          staticAdapter.getStatic(Type.getType(java.lang.Byte.class), "TYPE", CLASS_TYPE);
          break;
        case Type.CHAR:
          staticAdapter.getStatic(Type.getType(java.lang.Character.class), "TYPE", CLASS_TYPE);
          break;
        case Type.DOUBLE:
          staticAdapter.getStatic(Type.getType(java.lang.Double.class), "TYPE", CLASS_TYPE);
          break;
        case Type.FLOAT:
          staticAdapter.getStatic(Type.getType(java.lang.Float.class), "TYPE", CLASS_TYPE);
          break;
        case Type.INT:
          staticAdapter.getStatic(Type.getType(java.lang.Integer.class), "TYPE", CLASS_TYPE);
          break;
        case Type.LONG:
          staticAdapter.getStatic(Type.getType(java.lang.Long.class), "TYPE", CLASS_TYPE);
          break;
        case Type.SHORT:
          staticAdapter.getStatic(Type.getType(java.lang.Short.class), "TYPE", CLASS_TYPE);
          break;
        default:
        case Type.OBJECT:
          staticAdapter.push(t);
          break;
      }
      staticAdapter.arrayStore(CLASS_TYPE);
      index++;
    }

    // invoke the getMethod
    staticAdapter.invokeVirtual(CLASS_TYPE, new Method("getDeclaredMethod", METHOD_TYPE,
        new Type[] { STRING_TYPE, Type.getType(java.lang.Class[].class) }));

    // store the reflected method in the static field
    staticAdapter.putStatic(newClassType, methodStaticFieldName, METHOD_TYPE);

    /*
     * Stage 2 call the ih.invoke(this,supermethod,parms)
     */

    // load this to get the ih field
    methodAdapter.loadThis();
    // load the invocation handler from the field (the location of the
    // InvocationHandler.invoke)
    methodAdapter.getField(newClassType, IH_FIELD, IH_TYPE);
    // loadThis (the first arg of the InvocationHandler.invoke)
    methodAdapter.loadThis();
    // load the method to invoke (the second arg of the
    // InvocationHandler.invoke)
    methodAdapter.getStatic(newClassType, methodStaticFieldName, METHOD_TYPE);
    // load all the method arguments onto the stack as an object array (the
    // third arg of the InvocationHandler.invoke)
    methodAdapter.loadArgArray();
    // generate the invoke method
    Method invocationHandlerInvokeMethod = new Method("invoke", OBJECT_TYPE, new Type[] {
        OBJECT_TYPE, METHOD_TYPE, Type.getType(java.lang.Object[].class) });
    // call the invoke method of the invocation handler
    methodAdapter.invokeInterface(IH_TYPE, invocationHandlerInvokeMethod);

    /*
     * Stage 3 the returned object is now on the top of the stack We need to
     * check the type and cast as necessary
     */
    switch (returnType.getSort())
    {
      case Type.BOOLEAN:
        try {
          methodAdapter.cast(OBJECT_TYPE, Type.getType(Boolean.class));
        } catch (IllegalArgumentException ex) {
          LOGGER.debug("Ignore cast exception caused by ASM 6.1 and try further", ex);
        }
        methodAdapter.unbox(Type.BOOLEAN_TYPE);
        break;
      case Type.BYTE:
        try {
          methodAdapter.cast(OBJECT_TYPE, Type.getType(Byte.class));
        } catch (IllegalArgumentException ex) {
          LOGGER.debug("Ignore cast exception caused by ASM 6.1 and try further", ex);
        }
        methodAdapter.unbox(Type.BYTE_TYPE);
        break;
      case Type.CHAR:
        try {
          methodAdapter.cast(OBJECT_TYPE, Type.getType(Character.class));
        } catch (IllegalArgumentException ex) {
          LOGGER.debug("Ignore cast exception caused by ASM 6.1 and try further", ex);
        }
        methodAdapter.unbox(Type.CHAR_TYPE);
        break;
      case Type.DOUBLE:
        try {
          methodAdapter.cast(OBJECT_TYPE, Type.getType(Double.class));
        } catch (IllegalArgumentException ex) {
          LOGGER.debug("Ignore cast exception caused by ASM 6.1 and try further", ex);
        }
        methodAdapter.unbox(Type.DOUBLE_TYPE);
        break;
      case Type.FLOAT:
        try {
          methodAdapter.cast(OBJECT_TYPE, Type.getType(Float.class));
        } catch (IllegalArgumentException ex) {
          LOGGER.debug("Ignore cast exception caused by ASM 6.1 and try further", ex);
        }
        methodAdapter.unbox(Type.FLOAT_TYPE);
        break;
      case Type.INT:
        try {
          methodAdapter.cast(OBJECT_TYPE, Type.getType(Integer.class));
        } catch (IllegalArgumentException ex) {
          LOGGER.debug("Ignore cast exception caused by ASM 6.1 and try further", ex);
        }
        methodAdapter.unbox(Type.INT_TYPE);
        break;
      case Type.LONG:
        try {
          methodAdapter.cast(OBJECT_TYPE, Type.getType(Long.class));
        } catch (IllegalArgumentException ex) {
          LOGGER.debug("Ignore cast exception caused by ASM 6.1 and try further", ex);
        }
        methodAdapter.unbox(Type.LONG_TYPE);
        break;
      case Type.SHORT:
        try {
          methodAdapter.cast(OBJECT_TYPE, Type.getType(Short.class));
        } catch (IllegalArgumentException ex) {
          LOGGER.debug("Ignore cast exception caused by ASM 6.1 and try further", ex);
        }
        methodAdapter.unbox(Type.SHORT_TYPE);
        break;
      case Type.VOID:
        try {
          methodAdapter.cast(OBJECT_TYPE, Type.getType(Void.class));
        } catch (IllegalArgumentException ex) {
          LOGGER.debug("Ignore cast exception caused by ASM 6.1 and try further", ex);
        }
        methodAdapter.unbox(Type.VOID_TYPE);
        break;
      default:
      case Type.OBJECT:
        // in this case check the cast and cast the object to the return
        // type
        methodAdapter.checkCast(returnType);
        try {
          methodAdapter.cast(OBJECT_TYPE, returnType);
        } catch (IllegalArgumentException ex) {
          LOGGER.debug("Ignore cast exception caused by ASM 6.1 and try further", ex);
        }
        break;
    }
    // return the (appropriately cast) result of the invocation from the
    // stack
    methodAdapter.returnValue();
    // end the method
    methodAdapter.endMethod();

    LOGGER.debug(Constants.LOG_EXIT, "processMethod");
  }