/** Invokes a given method via {@link Method#invoke(Object, Object...)}. */
  default Object invoke(ResolvedJavaMethod method, Object receiver, Object... args) {
    try {
      JavaType[] parameterTypes = method.toParameterTypes();
      Class<?>[] parameterClasses = new Class<?>[parameterTypes.length];
      for (int i = 0; i < parameterClasses.length; ++i) {
        JavaType type = parameterTypes[i];
        if (type.getJavaKind() != JavaKind.Object) {
          parameterClasses[i] = type.getJavaKind().toJavaClass();
        } else {
          parameterClasses[i] = resolveClassForSnippet(parameterTypes[i]);
        }
      }

      Class<?> c = resolveClassForSnippet(method.getDeclaringClass());
      if (method.isConstructor()) {
        Constructor<?> javaConstructor = c.getDeclaredConstructor(parameterClasses);
        javaConstructor.setAccessible(true);
        return javaConstructor.newInstance(args);
      } else {
        Method javaMethod = c.getDeclaredMethod(method.getName(), parameterClasses);
        javaMethod.setAccessible(true);
        return javaMethod.invoke(receiver, args);
      }
    } catch (IllegalAccessException
        | InstantiationException
        | InvocationTargetException
        | ClassNotFoundException
        | NoSuchMethodException ex) {
      throw new IllegalArgumentException(ex);
    }
  }
 /**
  * Gets the method bridged to by a {@linkplain ResolvedJavaMethod#isBridge() bridge} method. The
  * value returned is the method called by {@code method} that has the same name as {@code bridge}.
  *
  * @param bridge a bridge method
  * @return the method called by {@code bridge} whose name is the same as {@code bridge.getName()}
  */
 public static ResolvedJavaMethod getBridgedMethod(ResolvedJavaMethod bridge) {
   assert bridge.isBridge();
   Bytecode code = new ResolvedJavaMethodBytecode(bridge);
   BytecodeStream stream = new BytecodeStream(code.getCode());
   int opcode = stream.currentBC();
   ResolvedJavaMethod bridged = null;
   while (opcode != Bytecodes.END) {
     switch (opcode) {
       case INVOKEVIRTUAL:
       case INVOKESPECIAL:
       case INVOKESTATIC:
       case INVOKEINTERFACE:
         {
           int cpi = stream.readCPI();
           ConstantPool cp = code.getConstantPool();
           cp.loadReferencedType(cpi, opcode);
           ResolvedJavaMethod method = (ResolvedJavaMethod) cp.lookupMethod(cpi, opcode);
           if (method.getName().equals(bridge.getName())) {
             if (!assertionsEnabled()) {
               return method;
             }
             assert bridged == null || bridged.equals(method)
                 : String.format(
                     "Found calls to different methods named %s in bridge method %s%n  callee 1: %s%n  callee 2: %s",
                     bridge.getName(),
                     bridge.format("%R %H.%n(%P)"),
                     bridged.format("%R %H.%n(%P)"),
                     method.format("%R %H.%n(%P)"));
             bridged = method;
           }
           break;
         }
     }
     stream.next();
     opcode = stream.currentBC();
   }
   if (bridged == null) {
     throw new InternalError("Couldn't find method bridged by " + bridge.format("%R %H.%n(%P)"));
   }
   return bridged;
 }
 static ResolvedJavaMethod findMethod(
     ResolvedJavaType type, String name, String descriptor, boolean isStatic) {
   if (isStatic && name.equals("<clinit>")) {
     ResolvedJavaMethod method = type.getClassInitializer();
     if (method != null) {
       return method;
     }
   }
   ResolvedJavaMethod[] methodsToSearch =
       name.equals("<init>") ? type.getDeclaredConstructors() : type.getDeclaredMethods();
   for (ResolvedJavaMethod method : methodsToSearch) {
     if (method.isStatic() == isStatic
         && method.getName().equals(name)
         && method.getSignature().toMethodDescriptor().equals(descriptor)) {
       return method;
     }
   }
   return null;
 }
 @Override
 public Bytecode getBytecode(ResolvedJavaMethod method) {
   Classfile classfile = getClassfile(resolveToClass(method.getDeclaringClass().getName()));
   return classfile.getCode(method.getName(), method.getSignature().toMethodDescriptor());
 }