예제 #1
0
파일: Rule.java 프로젝트: damc-dev/byteman
  private void installParameters(boolean isStatic, String className) throws TypeException {
    Type type;
    // add a binding for the helper so we can call builtin static methods
    type = typeGroup.create(helperClass.getName());
    Binding ruleBinding = bindings.lookup("$$");
    if (ruleBinding != null) {
      ruleBinding.setType(type);
    } else {
      bindings.append(new Binding(this, "$$", type));
    }

    if (!isStatic) {
      Binding recipientBinding = bindings.lookup("$0");
      if (recipientBinding != null) {
        type = typeGroup.create(className);
        if (type.isUndefined()) {
          throw new TypeException(
              "Rule.installParameters : Rule " + name + " unable to load class " + className);
        }
        recipientBinding.setType(type);
      }
    }

    String returnTypeName = Type.parseMethodReturnType(triggerDescriptor);

    returnType = typeGroup.create(returnTypeName);

    Iterator<Binding> iterator = bindings.iterator();

    while (iterator.hasNext()) {
      Binding binding = iterator.next();
      // these bindings are typed via the descriptor installed during trigger injection
      // note that the return type has to be done this way because it may represent the
      // trigger method return type or the invoke method return type
      if (binding.isParam() || binding.isLocalVar() || binding.isReturn()) {
        String typeName = binding.getDescriptor();
        String[] typeAndArrayBounds = typeName.split("\\[");
        Type baseType = typeGroup.create(typeAndArrayBounds[0]);
        Type fullType = baseType;
        if (baseType.isUndefined()) {
          throw new TypeException(
              "Rule.installParameters : Rule " + name + " unable to load class " + baseType);
        }
        for (int i = 1; i < typeAndArrayBounds.length; i++) {
          fullType = typeGroup.createArray(fullType);
        }
        binding.setType(fullType);
      } else if (binding.isThrowable()) {
        // TODO -- enable a more precise specification of the throwable type
        // we need to be able to obtain the type descriptor for the throw operation
        binding.setType(typeGroup.ensureType(Throwable.class));
      } else if (binding.isParamCount()) {
        binding.setType(Type.I);
      } else if (binding.isParamArray() || binding.isInvokeParamArray()) {
        binding.setType(Type.OBJECT.arrayType());
      } else if (binding.isTriggerClass() || binding.isTriggerMethod()) {
        binding.setType(Type.STRING);
      }
    }
  }
예제 #2
0
  /**
   * find a method to resolve this method call expression.
   *
   * @param publicOnly true if only public methods should be considered
   * @throws TypeException
   */
  private void findMethod(boolean publicOnly) throws TypeException {
    // check all declared methods of each class in the class hierarchy using the one with
    // the most specific recipient type if we can find it

    TypeGroup typeGroup = getTypeGroup();
    Class<?> clazz = rootType.getTargetClass();
    boolean isStatic = (recipient == null);

    int arity = arguments.size();
    LinkedList<Class<?>> clazzes = new LinkedList<Class<?>>();
    if (publicOnly) {
      // we can use getDeclaredMethods on just one class to list all possible candidates
      clazzes.add(clazz);
    } else {
      // we need to iterate over the class and interface hierarchy bottom up
      while (clazz != null) {
        clazzes.add(clazz);
        // collect all direct interfaces in order
        Class[] ifaces = clazz.getInterfaces();
        LinkedList<Class<?>> toBeChecked = new LinkedList<Class<?>>();
        for (int i = 0; i < ifaces.length; i++) {
          toBeChecked.addLast(ifaces[i]);
        }
        // process each interface in turn, also collecting its parent interfaces for consideration
        while (!toBeChecked.isEmpty()) {
          Class<?> iface = toBeChecked.pop();
          // only need to process it if we have not already seen it
          if (!clazzes.contains(iface)) {
            clazzes.addLast(iface);
            Class[] ifaces2 = iface.getInterfaces();
            // don't bother to check for repeats here as we check later anyway
            for (int j = 0; j < ifaces2.length; j++) {
              toBeChecked.addLast(ifaces2[j]);
            }
          }
        }
        // move on to the next class
        clazz = clazz.getSuperclass();
      }
    }
    // now check for a matching method in each class or interface in order
    while (!clazzes.isEmpty()) {
      clazz = clazzes.pop();
      List<Method> candidates = new ArrayList<Method>();
      try {
        Method[] methods;
        if (publicOnly) {
          methods = clazz.getMethods();
        } else {
          methods = clazz.getDeclaredMethods();
        }

        argumentTypes = new ArrayList<Type>();

        for (Method method : methods) {
          int modifiers = method.getModifiers();
          // ensure we only look at static or non static methods as appropriate
          if (Modifier.isStatic(modifiers) == isStatic) {
            if (method.getName().equals(name) && method.getParameterTypes().length == arity) {
              candidates.add(method);
            }
          }
        }
        // check each argument in turn -- if all candidates have the same argument type then
        // use that as the type to check against
        for (int i = 0; i < arguments.size(); i++) {
          if (candidates.isEmpty()) {
            // no more possible matches
            break;
          }
          Class candidateClass = getCandidateArgClass(candidates, i);
          Type candidateType;
          if (candidateClass != null) {
            candidateType = typeGroup.ensureType(candidateClass);
          } else {
            candidateType = Type.UNDEFINED;
          }
          Type argType = arguments.get(i).typeCheck(candidateType);
          argumentTypes.add(argType);
          if (candidateType == Type.UNDEFINED) {
            // we had several methods to choose from
            candidates = pruneCandidates(candidates, i, argType.getTargetClass());
          }
        }

        // see if we have a unique best fit

        Method method = bestMatchCandidate(candidates);

        if (method != null) {
          if (!Modifier.isPublic(method.getModifiers())) {
            // see if we can actually access this method
            try {
              method.setAccessible(true);
            } catch (SecurityException e) {
              // hmm, maybe try the next super
              continue;
            }
            // we need to remember that this is not public
            isPublicMethod = false;
            // save the method so we can use it from the compiled code
            methodIndex = rule.addAccessibleMethod(method);
          } else {
            isPublicMethod = true;
          }
          this.method = method;
          return;
        } else if (candidates.size() > 1) {
          // ambiguous method so throw up here
          throw new TypeException(
              "MethodExpression.typeCheck : ambiguous method signature "
                  + name
                  + " for target class "
                  + rootType.getName()
                  + getPos());
        }

      } catch (SecurityException e) {
        // continue in case we can find an implementation
      }
    }

    // no more possible candidates so throw up here
    throw new TypeException(
        "MethodExpression.typeCheck : invalid method "
            + name
            + " for target class "
            + rootType.getName()
            + getPos());
  }
예제 #3
0
  public void compile(MethodVisitor mv, CompileContext compileContext) throws CompileException {
    // make sure we are at the right source line
    compileContext.notifySourceLine(line);

    int currentStack = compileContext.getStackCount();
    int extraParams = 0; // space used by stacked args after conversion
    int expected = 0;

    // no need for type conversion as return type was derived from method
    if (type.getNBytes() > 4) {
      expected = 2;
    } else if (type != Type.VOID) {
      expected = 1;
    } else {
      expected = 0;
    }

    int argCount = arguments.size();

    if (isPublicMethod) {
      // we can just do this as a direct call
      // stack the recipient if necessary then stack the args and then invoke the method
      if (recipient != null) {
        // compile code for recipient
        recipient.compile(mv, compileContext);

        extraParams += 1;
      }

      for (int i = 0; i < argCount; i++) {
        Expression argument = arguments.get(i);
        Type argType = argumentTypes.get(i);
        Type paramType = paramTypes.get(i);
        // compile code to stack argument and type convert if necessary
        argument.compile(mv, compileContext);
        compileTypeConversion(argType, paramType, mv, compileContext);
        // allow for stacked paramType value
        extraParams += (paramType.getNBytes() > 4 ? 2 : 1);
      }

      // enable triggering before we call the method
      // this adds an extra value to the stack so modify the compile context to ensure
      // we increase the maximum height if necessary
      mv.visitMethodInsn(
          Opcodes.INVOKESTATIC, "org/jboss/byteman/rule/Rule", "enableTriggersInternal", "()Z");
      compileContext.addStackCount(1);
      mv.visitInsn(Opcodes.POP);
      compileContext.addStackCount(-1);

      // ok, now just call the method -- removes extraParams words

      String ownerName = Type.internalName(method.getDeclaringClass());

      if (recipient == null) {
        mv.visitMethodInsn(Opcodes.INVOKESTATIC, ownerName, method.getName(), getDescriptor());
      } else if (method.getDeclaringClass().isInterface()) {
        mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, ownerName, method.getName(), getDescriptor());
      } else {
        mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, ownerName, method.getName(), getDescriptor());
      }
      // decrement the stack height to account for stacked param values (removed) and return value
      // (added)
      compileContext.addStackCount(expected - extraParams);

      // now disable triggering again
      // this temporarily adds an extra value to the stack -- n.b. we *must* increment and
      // then decrement the stack height even though we bumped the max before the call. in
      // some cases the stack may be larger after the method call than before e.g. calling
      // a static which returns a value or calling a non-static which returns a long/double

      mv.visitMethodInsn(
          Opcodes.INVOKESTATIC, "org/jboss/byteman/rule/Rule", "disableTriggersInternal", "()Z");
      compileContext.addStackCount(1);
      mv.visitInsn(Opcodes.POP);
      compileContext.addStackCount(-1);
    } else {
      // if we are calling a method by reflection then we need to stack the current helper then
      // the recipient or null if there is none and then build an object array on the stack
      mv.visitVarInsn(Opcodes.ALOAD, 0);
      compileContext.addStackCount(1);
      if (recipient != null) {
        // compile code for recipient
        recipient.compile(mv, compileContext);
      } else {
        mv.visitInsn(Opcodes.ACONST_NULL);
        compileContext.addStackCount(1);
      }

      // stack arg count then create a new array
      mv.visitLdcInsn(argCount);
      compileContext.addStackCount(1);
      // this just swaps one word for another
      mv.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object");

      // duplicate the array, stack the index, compile code to generate the arg and the do an array
      // put
      for (int i = 0; i < argCount; i++) {
        mv.visitInsn(Opcodes.DUP);
        mv.visitLdcInsn(i);
        // that was two extra words
        compileContext.addStackCount(2);
        Expression argument = arguments.get(i);
        Type argType = argumentTypes.get(i);
        Type paramType = paramTypes.get(i);
        // compile code to stack argument and type convert/box if necessary
        argument.compile(mv, compileContext);
        compileTypeConversion(argType, paramType, mv, compileContext);
        compileBox(paramType, mv, compileContext);
        // that's 3 extra words which now get removed
        mv.visitInsn(Opcodes.AASTORE);
        compileContext.addStackCount(-3);
      }
      // now stack the method object index
      mv.visitLdcInsn(methodIndex);
      compileContext.addStackCount(1);

      // enable triggering before we call the method
      // this adds an extra value to the stack so modify the compile context to ensure
      // we increase the maximum height if necessary
      mv.visitMethodInsn(
          Opcodes.INVOKESTATIC, "org/jboss/byteman/rule/Rule", "enableTriggersInternal", "()Z");
      compileContext.addStackCount(1);
      mv.visitInsn(Opcodes.POP);
      compileContext.addStackCount(-1);

      // ok, we  now have the recipient, args array and method index on the stack
      // so we can call the HelperAdapter method  to do the actual reflective invocation
      mv.visitMethodInsn(
          Opcodes.INVOKEINTERFACE,
          Type.internalName(HelperAdapter.class),
          "invokeAccessibleMethod",
          "(Ljava/lang/Object;[Ljava/lang/Object;I)Ljava/lang/Object;");
      // we popped 4 words and left one in its place
      compileContext.addStackCount(-3);
      if (type == Type.VOID) {
        mv.visitInsn(Opcodes.POP);
        compileContext.addStackCount(-1);
      } else {
        // do any necessary casting and/or unboxing
        compileTypeConversion(Type.OBJECT, type, mv, compileContext);
      }

      // now disable triggering again
      // this temporarily adds an extra value to the stack -- n.b. no need to increment and
      // then decrement the stack height here because the previous enable call will already have
      // bumped the max when we had 4 slots on the stack and any return value on the stack will
      // occupy at most 2 slots

      mv.visitMethodInsn(
          Opcodes.INVOKESTATIC, "org/jboss/byteman/rule/Rule", "disableTriggersInternal", "()Z");
      compileContext.addStackCount(1);
      mv.visitInsn(Opcodes.POP);
      compileContext.addStackCount(-1);
    }

    // ensure we have only increased the stack by the return value size
    if (compileContext.getStackCount() != currentStack + expected) {
      throw new CompileException(
          "MethodExpression.compile : invalid stack height "
              + compileContext.getStackCount()
              + " expecting "
              + (currentStack + expected));
    }

    // no need to update max stack since compiling the  recipient or arguments will
    // have done so (and there will be no change if there was no such compile call)
  }
예제 #4
0
  public Type typeCheck(Type expected) throws TypeException {
    // if we have no recipient then we use the rule's helper as a target via a binding
    // to $-1. this means  we can type check the call against methods of class Helper
    // without having to do any special case processing.

    TypeGroup typeGroup = getTypeGroup();

    if (recipient == null && pathList != null) {
      // treat the pathlist as a typename or a static field dereference possibly combined with
      // further field dereferences

      // factor off a typename from the path
      Type rootType = typeGroup.match(pathList);
      if (rootType == null) {
        throw new TypeException(
            "MethodExpression.typeCheck : invalid path "
                + getPath(pathList.length)
                + " to static method "
                + name
                + getPos());
      }

      // find out how many of the path elements are included in the type name

      String rootTypeName = rootType.getName();

      int idx = getPathCount(rootTypeName);

      if (idx < pathList.length) {
        // create a static field reference using the type name and the first field name and wrap it
        // with
        // enough field references to use up all the path

        String fieldName = pathList[idx++];
        Expression recipient =
            new StaticExpression(rule, Type.UNDEFINED, token, fieldName, rootTypeName);
        while (idx < pathList.length) {
          recipient =
              new FieldExpression(rule, Type.UNDEFINED, token, pathList[idx++], recipient, null);
        }
        this.recipient = recipient;
      } else {
        // ok, this method reference is actually a static method call -- record the root type for
        // later
        this.recipient = null;
        this.rootType = rootType;
      }
      // get rid of the path list now
      this.pathList = null;
      // not strictly necessary?
      if (this.recipient != null) {
        this.recipient.bind();
      }
    }

    // if we don't have a recipient and we didn't find a static class for the method then this is
    // a builtin

    boolean isBuiltIn = false;

    if (recipient == null) {
      if (rootType == null) {
        isBuiltIn = true;
        Type ruleType = typeGroup.create(rule.getHelperClass().getCanonicalName());
        recipient = new DollarExpression(rule, ruleType, token, DollarExpression.HELPER_IDX);
        recipient.bind();

        rootType = recipient.typeCheck(Type.UNDEFINED);
      }
    } else {
      rootType = recipient.typeCheck(Type.UNDEFINED);
    }

    // see if we can find a method for this call

    findMethod(isBuiltIn);

    // now go back and identify the parameter types

    this.paramTypes = new ArrayList<Type>();
    Class<?>[] paramClasses = method.getParameterTypes();

    for (int i = 0; i < arguments.size(); i++) {
      Class<?> paramClass = paramClasses[i];
      paramTypes.add(typeGroup.ensureType(paramClass));
    }

    type = typeGroup.ensureType(method.getReturnType());

    if (Type.dereference(expected).isDefined() && !expected.isAssignableFrom(type)) {
      throw new TypeException(
          "MethodExpression.typeCheck : invalid expected type " + expected.getName() + getPos());
    }

    return type;
  }