8031195: Support default and static interface methods in JDI, JDWP and JDB
authorjbachorik
Tue, 29 Apr 2014 11:15:21 +0200
changeset 24125 b85eeaae56c7
parent 24124 5f5f7f7a4328
child 24126 dda4c98ed5e9
8031195: Support default and static interface methods in JDI, JDWP and JDB Reviewed-by: sla, sspitsyn, dcubed
jdk/make/data/jdwp/jdwp.spec
jdk/src/share/back/InterfaceTypeImpl.c
jdk/src/share/back/InterfaceTypeImpl.h
jdk/src/share/back/VirtualMachineImpl.c
jdk/src/share/back/debugDispatch.c
jdk/src/share/back/util.c
jdk/src/share/classes/com/sun/jdi/ClassType.java
jdk/src/share/classes/com/sun/jdi/InterfaceType.java
jdk/src/share/classes/com/sun/jdi/Method.java
jdk/src/share/classes/com/sun/jdi/ObjectReference.java
jdk/src/share/classes/com/sun/tools/example/debug/expr/LValue.java
jdk/src/share/classes/com/sun/tools/jdi/ClassTypeImpl.java
jdk/src/share/classes/com/sun/tools/jdi/InterfaceTypeImpl.java
jdk/src/share/classes/com/sun/tools/jdi/InvokableTypeImpl.java
jdk/src/share/classes/com/sun/tools/jdi/MethodImpl.java
jdk/src/share/classes/com/sun/tools/jdi/ObjectReferenceImpl.java
jdk/src/share/classes/com/sun/tools/jdi/VirtualMachineManagerImpl.java
jdk/test/com/sun/jdi/EvalInterfaceStatic.sh
jdk/test/com/sun/jdi/InterfaceMethodsTest.java
--- a/jdk/make/data/jdwp/jdwp.spec	Mon Apr 28 13:49:49 2014 +0100
+++ b/jdk/make/data/jdwp/jdwp.spec	Tue Apr 29 11:15:21 2014 +0200
@@ -1147,7 +1147,8 @@
         (ErrorSet
             (Error INVALID_CLASS     "clazz is not the ID of a class.")
             (Error INVALID_OBJECT    "clazz is not a known ID.")
-            (Error INVALID_METHODID  "methodID is not the ID of a method.")
+            (Error INVALID_METHODID  "methodID is not the ID of a static method in "
+                                     "this class type or one of its superclasses.")
             (Error INVALID_THREAD)
             (Error THREAD_NOT_SUSPENDED)
             (Error VM_DEAD)
@@ -1250,6 +1251,83 @@
     )
 )
 (CommandSet InterfaceType=5
+    (Command InvokeMethod=1
+        "Invokes a static method. "
+        "The method must not be a static initializer. "
+        "The method must be a member of the interface type. "
+        "<p>Since JDWP version 1.8 "
+        "<p>"
+        "The method invocation will occur in the specified thread. "
+        "Method invocation can occur only if the specified thread "
+        "has been suspended by an event. "
+        "Method invocation is not supported "
+        "when the target VM has been suspended by the front-end. "
+        "<p>"
+        "The specified method is invoked with the arguments in the specified "
+        "argument list. "
+        "The method invocation is synchronous; the reply packet is not "
+        "sent until the invoked method returns in the target VM. "
+        "The return value (possibly the void value) is "
+        "included in the reply packet. "
+        "If the invoked method throws an exception, the "
+        "exception object ID is set in the reply packet; otherwise, the "
+        "exception object ID is null. "
+        "<p>"
+        "For primitive arguments, the argument value's type must match the "
+        "argument's type exactly. For object arguments, there must exist a "
+        "widening reference conversion from the argument value's type to the "
+        "argument's type and the argument's type must be loaded. "
+        "<p>"
+        "By default, all threads in the target VM are resumed while "
+        "the method is being invoked if they were previously "
+        "suspended by an event or by a command. "
+        "This is done to prevent the deadlocks "
+        "that will occur if any of the threads own monitors "
+        "that will be needed by the invoked method. It is possible that "
+        "breakpoints or other events might occur during the invocation. "
+        "Note, however, that this implicit resume acts exactly like "
+        "the ThreadReference resume command, so if the thread's suspend "
+        "count is greater than 1, it will remain in a suspended state "
+        "during the invocation. By default, when the invocation completes, "
+        "all threads in the target VM are suspended, regardless their state "
+        "before the invocation. "
+        "<p>"
+        "The resumption of other threads during the invoke can be prevented "
+        "by specifying the INVOKE_SINGLE_THREADED "
+        "bit flag in the <code>options</code> field; however, "
+        "there is no protection against or recovery from the deadlocks "
+        "described above, so this option should be used with great caution. "
+        "Only the specified thread will be resumed (as described for all "
+        "threads above). Upon completion of a single threaded invoke, the invoking thread "
+        "will be suspended once again. Note that any threads started during "
+        "the single threaded invocation will not be suspended when the "
+        "invocation completes. "
+        "<p>"
+        "If the target VM is disconnected during the invoke (for example, through "
+        "the VirtualMachine dispose command) the method invocation continues. "
+        (Out
+            (interfaceType clazz "The interface type ID.")
+            (threadObject thread "The thread in which to invoke.")
+            (method methodID "The method to invoke.")
+            (Repeat arguments
+                (value arg "The argument value.")
+            )
+            (int options "Invocation <a href=\"#JDWP_InvokeOptions\">options</a>")
+        )
+        (Reply
+            (value returnValue "The returned value.")
+            (tagged-object exception "The thrown exception.")
+        )
+        (ErrorSet
+            (Error INVALID_CLASS     "clazz is not the ID of an interface.")
+            (Error INVALID_OBJECT    "clazz is not a known ID.")
+            (Error INVALID_METHODID  "methodID is not the ID of a static method in this "
+                                     "interface type or is the ID of a static initializer.")
+            (Error INVALID_THREAD)
+            (Error THREAD_NOT_SUSPENDED)
+            (Error VM_DEAD)
+        )
+    )
 )
 (CommandSet Method=6
     (Command LineTable=1
@@ -1543,7 +1621,7 @@
         "<p>"
         "By default, all threads in the target VM are resumed while "
         "the method is being invoked if they were previously "
-        "suspended by an event or by command. "
+        "suspended by an event or by a command. "
         "This is done to prevent the deadlocks "
         "that will occur if any of the threads own monitors "
         "that will be needed by the invoked method. It is possible that "
@@ -1586,7 +1664,9 @@
             (Error INVALID_OBJECT)
             (Error INVALID_CLASS     "clazz is not the ID of a reference "
                                      "type.")
-            (Error INVALID_METHODID  "methodID is not the ID of a method.")
+            (Error INVALID_METHODID  "methodID is not the ID of an instance method "
+                                     "in this object's type or one of its superclasses, "
+                                     "superinterfaces, or implemented interfaces.")
             (Error INVALID_THREAD)
             (Error THREAD_NOT_SUSPENDED)
             (Error VM_DEAD)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/back/InterfaceTypeImpl.c	Tue Apr 29 11:15:21 2014 +0200
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 1998, 2005, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "util.h"
+#include "InterfaceTypeImpl.h"
+#include "inStream.h"
+#include "outStream.h"
+
+static jboolean
+invokeStatic(PacketInputStream *in, PacketOutputStream *out)
+{
+    return sharedInvoke(in, out);
+}
+
+void *InterfaceType_Cmds[] = { (void *)0x1
+    , (void *)invokeStatic
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/back/InterfaceTypeImpl.h	Tue Apr 29 11:15:21 2014 +0200
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 1998, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+extern void *InterfaceType_Cmds[];
--- a/jdk/src/share/back/VirtualMachineImpl.c	Mon Apr 28 13:49:49 2014 +0100
+++ b/jdk/src/share/back/VirtualMachineImpl.c	Tue Apr 29 11:15:21 2014 +0200
@@ -36,7 +36,7 @@
 
 static char *versionName = "Java Debug Wire Protocol (Reference Implementation)";
 static int majorVersion = 1;  /* JDWP major version */
-static int minorVersion = 6;  /* JDWP minor version */
+static int minorVersion = 8;  /* JDWP minor version */
 
 static jboolean
 version(PacketInputStream *in, PacketOutputStream *out)
--- a/jdk/src/share/back/debugDispatch.c	Mon Apr 28 13:49:49 2014 +0100
+++ b/jdk/src/share/back/debugDispatch.c	Tue Apr 29 11:15:21 2014 +0200
@@ -29,6 +29,7 @@
 #include "VirtualMachineImpl.h"
 #include "ReferenceTypeImpl.h"
 #include "ClassTypeImpl.h"
+#include "InterfaceTypeImpl.h"
 #include "ArrayTypeImpl.h"
 #include "FieldImpl.h"
 #include "MethodImpl.h"
@@ -67,6 +68,7 @@
     l1Array[JDWP_COMMAND_SET(VirtualMachine)] = (void *)VirtualMachine_Cmds;
     l1Array[JDWP_COMMAND_SET(ReferenceType)] = (void *)ReferenceType_Cmds;
     l1Array[JDWP_COMMAND_SET(ClassType)] = (void *)ClassType_Cmds;
+    l1Array[JDWP_COMMAND_SET(InterfaceType)] = (void *)InterfaceType_Cmds;
     l1Array[JDWP_COMMAND_SET(ArrayType)] = (void *)ArrayType_Cmds;
 
     l1Array[JDWP_COMMAND_SET(Field)] = (void *)Field_Cmds;
--- a/jdk/src/share/back/util.c	Mon Apr 28 13:49:49 2014 +0100
+++ b/jdk/src/share/back/util.c	Tue Apr 29 11:15:21 2014 +0200
@@ -591,6 +591,8 @@
         invokeType = INVOKE_CONSTRUCTOR;
     } else if (inStream_command(in) == JDWP_COMMAND(ClassType, InvokeMethod)) {
         invokeType = INVOKE_STATIC;
+    } else if (inStream_command(in) == JDWP_COMMAND(InterfaceType, InvokeMethod)) {
+        invokeType = INVOKE_STATIC;
     } else if (inStream_command(in) == JDWP_COMMAND(ObjectReference, InvokeMethod)) {
         invokeType = INVOKE_INSTANCE;
     } else {
--- a/jdk/src/share/classes/com/sun/jdi/ClassType.java	Mon Apr 28 13:49:49 2014 +0100
+++ b/jdk/src/share/classes/com/sun/jdi/ClassType.java	Tue Apr 29 11:15:21 2014 +0200
@@ -103,7 +103,7 @@
      * <p>
      * Object values must be assignment compatible with the field type
      * (This implies that the field type must be loaded through the
-     * enclosing class's class loader). Primitive values must be
+     * enclosing class' class loader). Primitive values must be
      * either assignment compatible with the field type or must be
      * convertible to the field type without loss of information.
      * See JLS section 5.2 for more information on assignment
@@ -153,7 +153,7 @@
      * <p>
      * Object arguments must be assignment compatible with the argument type
      * (This implies that the argument type must be loaded through the
-     * enclosing class's class loader). Primitive arguments must be
+     * enclosing class' class loader). Primitive arguments must be
      * either assignment compatible with the argument type or must be
      * convertible to the argument type without loss of information.
      * If the method being called accepts a variable number of arguments,
@@ -216,7 +216,7 @@
      * @return a {@link Value} mirror of the invoked method's return value.
      * @throws java.lang.IllegalArgumentException if the method is not
      * a member of this class or a superclass, if the size of the argument list
-     * does not match the number of declared arguemnts for the method, or
+     * does not match the number of declared arguments for the method, or
      * if the method is an initializer, constructor or static intializer.
      * @throws {@link InvalidTypeException} if any argument in the
      * argument list is not assignable to the corresponding method argument
@@ -230,7 +230,7 @@
      * @throws InvalidTypeException If the arguments do not meet this requirement --
      *         Object arguments must be assignment compatible with the argument
      *         type.  This implies that the argument type must be
-     *         loaded through the enclosing class's class loader.
+     *         loaded through the enclosing class' class loader.
      *         Primitive arguments must be either assignment compatible with the
      *         argument type or must be convertible to the argument type without loss
      *         of information. See JLS section 5.2 for more information on assignment
@@ -267,7 +267,7 @@
      * <p>
      * Object arguments must be assignment compatible with the argument type
      * (This implies that the argument type must be loaded through the
-     * enclosing class's class loader). Primitive arguments must be
+     * enclosing class' class loader). Primitive arguments must be
      * either assignment compatible with the argument type or must be
      * convertible to the argument type without loss of information.
      * If the method being called accepts a variable number of arguments,
@@ -335,7 +335,7 @@
      * @throws InvalidTypeException If the arguments do not meet this requirement --
      *         Object arguments must be assignment compatible with the argument
      *         type.  This implies that the argument type must be
-     *         loaded through the enclosing class's class loader.
+     *         loaded through the enclosing class' class loader.
      *         Primitive arguments must be either assignment compatible with the
      *         argument type or must be convertible to the argument type without loss
      *         of information. See JLS section 5.2 for more information on assignment
--- a/jdk/src/share/classes/com/sun/jdi/InterfaceType.java	Mon Apr 28 13:49:49 2014 +0100
+++ b/jdk/src/share/classes/com/sun/jdi/InterfaceType.java	Tue Apr 29 11:15:21 2014 +0200
@@ -79,4 +79,121 @@
      * If none exist, returns a zero length List.
      */
     List<ClassType> implementors();
+
+    /**
+     * Invokes the specified static {@link Method} in the
+     * target VM. The
+     * specified method must be defined in this interface.
+     * The method must be a static method
+     * but not a static initializer.
+     * <p>
+     * The method invocation will occur in the specified thread.
+     * Method invocation can occur only if the specified thread
+     * has been suspended by an event which occurred in that thread.
+     * Method invocation is not supported
+     * when the target VM has been suspended through
+     * {@link VirtualMachine#suspend} or when the specified thread
+     * is suspended through {@link ThreadReference#suspend}.
+     * <p>
+     * The specified method is invoked with the arguments in the specified
+     * argument list.  The method invocation is synchronous; this method
+     * does not return until the invoked method returns in the target VM.
+     * If the invoked method throws an exception, this method will throw
+     * an {@link InvocationException} which contains a mirror to the exception
+     * object thrown.
+     * <p>
+     * Object arguments must be assignment compatible with the argument type
+     * (This implies that the argument type must be loaded through the
+     * enclosing class' class loader). Primitive arguments must be
+     * either assignment compatible with the argument type or must be
+     * convertible to the argument type without loss of information.
+     * If the method being called accepts a variable number of arguments,
+     * then the last argument type is an array of some component type.
+     * The argument in the matching position can be omitted, or can be null,
+     * an array of the same component type, or an argument of the
+     * component type followed by any number of other arguments of the same
+     * type. If the argument is omitted, then a 0 length array of the
+     * component type is passed.  The component type can be a primitive type.
+     * Autoboxing is not supported.
+     *
+     * See Section 5.2 of
+     * <cite>The Java&trade; Language Specification</cite>
+     * for more information on assignment compatibility.
+     * <p>
+     * By default, all threads in the target VM are resumed while
+     * the method is being invoked if they were previously
+     * suspended by an event or by {@link VirtualMachine#suspend} or
+     * {@link ThreadReference#suspend}. This is done to prevent the deadlocks
+     * that will occur if any of the threads own monitors
+     * that will be needed by the invoked method.
+     * Note, however, that this implicit resume acts exactly like
+     * {@link ThreadReference#resume}, so if the thread's suspend
+     * count is greater than 1, it will remain in a suspended state
+     * during the invocation and thus a deadlock could still occur.
+     * By default, when the invocation completes,
+     * all threads in the target VM are suspended, regardless their state
+     * before the invocation.
+     * It is possible that
+     * breakpoints or other events might occur during the invocation.
+     * This can cause deadlocks as described above. It can also cause a deadlock
+     * if invokeMethod is called from the client's event handler thread.  In this
+     * case, this thread will be waiting for the invokeMethod to complete and
+     * won't read the EventSet that comes in for the new event.  If this
+     * new EventSet is SUSPEND_ALL, then a deadlock will occur because no
+     * one will resume the EventSet.  To avoid this, all EventRequests should
+     * be disabled before doing the invokeMethod, or the invokeMethod should
+     * not be done from the client's event handler thread.
+     * <p>
+     * The resumption of other threads during the invocation can be prevented
+     * by specifying the {@link #INVOKE_SINGLE_THREADED}
+     * bit flag in the <code>options</code> argument; however,
+     * there is no protection against or recovery from the deadlocks
+     * described above, so this option should be used with great caution.
+     * Only the specified thread will be resumed (as described for all
+     * threads above). Upon completion of a single threaded invoke, the invoking thread
+     * will be suspended once again. Note that any threads started during
+     * the single threaded invocation will not be suspended when the
+     * invocation completes.
+     * <p>
+     * If the target VM is disconnected during the invoke (for example, through
+     * {@link VirtualMachine#dispose}) the method invocation continues.
+     *
+     * @param thread the thread in which to invoke.
+     * @param method the {@link Method} to invoke.
+     * @param arguments the list of {@link Value} arguments bound to the
+     * invoked method. Values from the list are assigned to arguments
+     * in the order they appear in the method signature.
+     * @param options the integer bit flag options.
+     * @return a {@link Value} mirror of the invoked method's return value.
+     * @throws java.lang.IllegalArgumentException if the method is not
+     * a member of this interface, if the size of the argument list
+     * does not match the number of declared arguments for the method, or
+     * if the method is not static or is a static initializer.
+     * @throws {@link InvalidTypeException} if any argument in the
+     * argument list is not assignable to the corresponding method argument
+     * type.
+     * @throws ClassNotLoadedException if any argument type has not yet been loaded
+     * through the appropriate class loader.
+     * @throws IncompatibleThreadStateException if the specified thread has not
+     * been suspended by an event.
+     * @throws InvocationException if the method invocation resulted in
+     * an exception in the target VM.
+     * @throws InvalidTypeException If the arguments do not meet this requirement --
+     *         Object arguments must be assignment compatible with the argument
+     *         type.  This implies that the argument type must be
+     *         loaded through the enclosing class' class loader.
+     *         Primitive arguments must be either assignment compatible with the
+     *         argument type or must be convertible to the argument type without loss
+     *         of information. See JLS section 5.2 for more information on assignment
+     *         compatibility.
+     * @throws VMCannotBeModifiedException if the VirtualMachine is read-only - see {@link VirtualMachine#canBeModified()}.
+     *
+     * @since 1.8
+     */
+    Value invokeMethod(ThreadReference thread, Method method,
+                       List<? extends Value> arguments, int options)
+            throws InvalidTypeException,
+            ClassNotLoadedException,
+            IncompatibleThreadStateException,
+            InvocationException;
 }
--- a/jdk/src/share/classes/com/sun/jdi/Method.java	Mon Apr 28 13:49:49 2014 +0100
+++ b/jdk/src/share/classes/com/sun/jdi/Method.java	Tue Apr 29 11:15:21 2014 +0200
@@ -138,6 +138,16 @@
     boolean isAbstract();
 
     /**
+     * Determine if this method is a default method
+     *
+     * @return <code>true</code> if the method is declared default;
+     * false otherwise
+     *
+     * @since 1.8
+     */
+    boolean isDefault();
+
+    /**
      * Determine if this method is synchronized.
      *
      * @return <code>true</code> if the method is declared synchronized;
--- a/jdk/src/share/classes/com/sun/jdi/ObjectReference.java	Mon Apr 28 13:49:49 2014 +0100
+++ b/jdk/src/share/classes/com/sun/jdi/ObjectReference.java	Tue Apr 29 11:15:21 2014 +0200
@@ -194,10 +194,10 @@
      * {@link #INVOKE_NONVIRTUAL} bit flag in the <code>options</code>
      * argument. If this flag is set, the specified method is invoked
      * whether or not it is overridden for this object's runtime type.
-     * The method, in this case, must not belong to an interface and
-     * must not be abstract. This option is useful for performing method
-     * invocations like those done with the <code>super</code> keyword in
-     * the Java programming language.
+     * The method, in this case, must have an implementation, either in a class
+     * or an interface. This option is useful for performing method invocations
+     * like those done with the <code>super</code> keyword in the Java programming
+     * language.
      * <p>
      * By default, all threads in the target VM are resumed while
      * the method is being invoked if they were previously
@@ -246,10 +246,10 @@
      * @return a {@link Value} mirror of the invoked method's return value.
      * @throws java.lang.IllegalArgumentException if the method is not
      * a member of this object's class, if the size of the argument list
-     * does not match the number of declared arguemnts for the method,
+     * does not match the number of declared arguments for the method,
      * if the method is a constructor or static intializer, or
      * if {@link #INVOKE_NONVIRTUAL} is specified and the method is
-     * either abstract or an interface member.
+     * either abstract or a non-default interface member.
      * @throws {@link InvalidTypeException} if any argument in the
      * argument list is not assignable to the corresponding method argument
      * type.
--- a/jdk/src/share/classes/com/sun/tools/example/debug/expr/LValue.java	Mon Apr 28 13:49:49 2014 +0100
+++ b/jdk/src/share/classes/com/sun/tools/example/debug/expr/LValue.java	Tue Apr 29 11:15:21 2014 +0200
@@ -559,6 +559,9 @@
             } else if (refType instanceof ClassType) {
                 ClassType clazz = (ClassType)refType;
                 return jdiValue = clazz.invokeMethod(thread, matchingMethod, methodArguments, 0);
+            } else if (refType instanceof InterfaceType) {
+                InterfaceType iface = (InterfaceType)refType;
+                return jdiValue = iface.invokeMethod(thread, matchingMethod, methodArguments, 0);
             } else {
                 throw new InvalidTypeException("Cannot invoke static method on " +
                                          refType.name());
--- a/jdk/src/share/classes/com/sun/tools/jdi/ClassTypeImpl.java	Mon Apr 28 13:49:49 2014 +0100
+++ b/jdk/src/share/classes/com/sun/tools/jdi/ClassTypeImpl.java	Tue Apr 29 11:15:21 2014 +0200
@@ -29,9 +29,27 @@
 
 import java.util.*;
 
-public class ClassTypeImpl extends ReferenceTypeImpl
+final public class ClassTypeImpl extends InvokableTypeImpl
     implements ClassType
 {
+    private static class IResult implements InvocationResult {
+        final private JDWP.ClassType.InvokeMethod rslt;
+
+        public IResult(JDWP.ClassType.InvokeMethod rslt) {
+            this.rslt = rslt;
+        }
+
+        @Override
+        public ObjectReferenceImpl getException() {
+            return rslt.exception;
+        }
+
+        @Override
+        public ValueImpl getResult() {
+            return rslt.returnValue;
+        }
+    }
+
     private boolean cachedSuperclass = false;
     private ClassType superclass = null;
     private int lastLine = -1;
@@ -65,6 +83,7 @@
         return superclass;
     }
 
+    @Override
     public List<InterfaceType> interfaces()  {
         if (interfaces == null) {
             interfaces = getInterfaces();
@@ -72,26 +91,9 @@
         return interfaces;
     }
 
-    void addInterfaces(List<InterfaceType> list) {
-        List<InterfaceType> immediate = interfaces();
-        list.addAll(interfaces());
-
-        Iterator<InterfaceType> iter = immediate.iterator();
-        while (iter.hasNext()) {
-            InterfaceTypeImpl interfaze = (InterfaceTypeImpl)iter.next();
-            interfaze.addSuperinterfaces(list);
-        }
-
-        ClassTypeImpl superclass = (ClassTypeImpl)superclass();
-        if (superclass != null) {
-            superclass.addInterfaces(list);
-        }
-    }
-
-    public List<InterfaceType> allInterfaces()  {
-        List<InterfaceType> all = new ArrayList<InterfaceType>();
-        addInterfaces(all);
-        return all;
+    @Override
+    public List<InterfaceType> allInterfaces() {
+        return getAllInterfaces();
     }
 
     public List<ClassType> subclasses() {
@@ -159,28 +161,6 @@
         }
     }
 
-    PacketStream sendInvokeCommand(final ThreadReferenceImpl thread,
-                                   final MethodImpl method,
-                                   final ValueImpl[] args,
-                                   final int options) {
-        CommandSender sender =
-            new CommandSender() {
-                public PacketStream send() {
-                    return JDWP.ClassType.InvokeMethod.enqueueCommand(
-                                          vm, ClassTypeImpl.this, thread,
-                                          method.ref(), args, options);
-                }
-        };
-
-        PacketStream stream;
-        if ((options & INVOKE_SINGLE_THREADED) != 0) {
-            stream = thread.sendResumingCommand(sender);
-        } else {
-            stream = vm.sendResumingCommand(sender);
-        }
-        return stream;
-    }
-
     PacketStream sendNewInstanceCommand(final ThreadReferenceImpl thread,
                                    final MethodImpl method,
                                    final ValueImpl[] args,
@@ -203,52 +183,6 @@
         return stream;
     }
 
-    public Value invokeMethod(ThreadReference threadIntf, Method methodIntf,
-                              List<? extends Value> origArguments, int options)
-                                   throws InvalidTypeException,
-                                          ClassNotLoadedException,
-                                          IncompatibleThreadStateException,
-                                          InvocationException {
-        validateMirror(threadIntf);
-        validateMirror(methodIntf);
-        validateMirrorsOrNulls(origArguments);
-
-        MethodImpl method = (MethodImpl)methodIntf;
-        ThreadReferenceImpl thread = (ThreadReferenceImpl)threadIntf;
-
-        validateMethodInvocation(method);
-
-        List<? extends Value> arguments = method.validateAndPrepareArgumentsForInvoke(origArguments);
-
-        ValueImpl[] args = arguments.toArray(new ValueImpl[0]);
-        JDWP.ClassType.InvokeMethod ret;
-        try {
-            PacketStream stream =
-                sendInvokeCommand(thread, method, args, options);
-            ret = JDWP.ClassType.InvokeMethod.waitForReply(vm, stream);
-        } catch (JDWPException exc) {
-            if (exc.errorCode() == JDWP.Error.INVALID_THREAD) {
-                throw new IncompatibleThreadStateException();
-            } else {
-                throw exc.toJDIException();
-            }
-        }
-
-        /*
-         * There is an implict VM-wide suspend at the conclusion
-         * of a normal (non-single-threaded) method invoke
-         */
-        if ((options & INVOKE_SINGLE_THREADED) == 0) {
-            vm.notifySuspend();
-        }
-
-        if (ret.exception != null) {
-            throw new InvocationException(ret.exception);
-        } else {
-            return ret.returnValue;
-        }
-    }
-
     public ObjectReference newInstance(ThreadReference threadIntf,
                                        Method methodIntf,
                                        List<? extends Value> origArguments,
@@ -311,58 +245,6 @@
        return method;
    }
 
-   public List<Method> allMethods() {
-        ArrayList<Method> list = new ArrayList<Method>(methods());
-
-        ClassType clazz = superclass();
-        while (clazz != null) {
-            list.addAll(clazz.methods());
-            clazz = clazz.superclass();
-        }
-
-        /*
-         * Avoid duplicate checking on each method by iterating through
-         * duplicate-free allInterfaces() rather than recursing
-         */
-        for (InterfaceType interfaze : allInterfaces()) {
-            list.addAll(interfaze.methods());
-        }
-
-        return list;
-    }
-
-    List<ReferenceType> inheritedTypes() {
-        List<ReferenceType> inherited = new ArrayList<ReferenceType>();
-        if (superclass() != null) {
-            inherited.add(0, (ReferenceType)superclass()); /* insert at front */
-        }
-        for (ReferenceType rt : interfaces()) {
-            inherited.add(rt);
-        }
-        return inherited;
-    }
-
-    void validateMethodInvocation(Method method)
-                                   throws InvalidTypeException,
-                                          InvocationException {
-        /*
-         * Method must be in this class or a superclass.
-         */
-        ReferenceTypeImpl declType = (ReferenceTypeImpl)method.declaringType();
-        if (!declType.isAssignableFrom(this)) {
-            throw new IllegalArgumentException("Invalid method");
-        }
-
-        /*
-         * Method must be a static and not a static initializer
-         */
-        if (!method.isStatic()) {
-            throw new IllegalArgumentException("Cannot invoke instance method on a class type");
-        } else if (method.isStaticInitializer()) {
-            throw new IllegalArgumentException("Cannot invoke static initializer");
-        }
-    }
-
     void validateConstructorInvocation(Method method)
                                    throws InvalidTypeException,
                                           InvocationException {
@@ -382,51 +264,33 @@
         }
     }
 
-    @Override
-    void addVisibleMethods(Map<String, Method> methodMap, Set<InterfaceType> seenInterfaces) {
-        /*
-         * Add methods from
-         * parent types first, so that the methods in this class will
-         * overwrite them in the hash table
-         */
-
-        Iterator<InterfaceType> iter = interfaces().iterator();
-        while (iter.hasNext()) {
-            InterfaceTypeImpl interfaze = (InterfaceTypeImpl)iter.next();
-            if (!seenInterfaces.contains(interfaze)) {
-                interfaze.addVisibleMethods(methodMap, seenInterfaces);
-                seenInterfaces.add(interfaze);
-            }
-        }
-
-        ClassTypeImpl clazz = (ClassTypeImpl)superclass();
-        if (clazz != null) {
-            clazz.addVisibleMethods(methodMap, seenInterfaces);
-        }
-
-        addToMethodMap(methodMap, methods());
-    }
-
-    boolean isAssignableTo(ReferenceType type) {
-        ClassTypeImpl superclazz = (ClassTypeImpl)superclass();
-        if (this.equals(type)) {
-            return true;
-        } else if ((superclazz != null) && superclazz.isAssignableTo(type)) {
-            return true;
-        } else {
-            List<InterfaceType> interfaces = interfaces();
-            Iterator<InterfaceType> iter = interfaces.iterator();
-            while (iter.hasNext()) {
-                InterfaceTypeImpl interfaze = (InterfaceTypeImpl)iter.next();
-                if (interfaze.isAssignableTo(type)) {
-                    return true;
-                }
-            }
-            return false;
-        }
-    }
 
     public String toString() {
        return "class " + name() + " (" + loaderString() + ")";
     }
+
+    @Override
+    CommandSender getInvokeMethodSender(ThreadReferenceImpl thread,
+                                        MethodImpl method,
+                                        ValueImpl[] args,
+                                        int options) {
+        return () ->
+            JDWP.ClassType.InvokeMethod.enqueueCommand(vm,
+                                                       ClassTypeImpl.this,
+                                                       thread,
+                                                       method.ref(),
+                                                       args,
+                                                       options);
+    }
+
+    @Override
+    InvocationResult waitForReply(PacketStream stream) throws JDWPException {
+        return new IResult(JDWP.ClassType.InvokeMethod.waitForReply(vm, stream));
+    }
+
+    @Override
+    boolean canInvoke(Method method) {
+        // Method must be in this class or a superclass.
+        return ((ReferenceTypeImpl)method.declaringType()).isAssignableFrom(this);
+    }
 }
--- a/jdk/src/share/classes/com/sun/tools/jdi/InterfaceTypeImpl.java	Mon Apr 28 13:49:49 2014 +0100
+++ b/jdk/src/share/classes/com/sun/tools/jdi/InterfaceTypeImpl.java	Tue Apr 29 11:15:21 2014 +0200
@@ -29,14 +29,31 @@
 
 import java.util.List;
 import java.util.ArrayList;
-import java.util.Map;
-import java.util.Iterator;
 import java.util.Collections;
 import java.util.Set;
 import java.lang.ref.SoftReference;
 
-public class InterfaceTypeImpl extends ReferenceTypeImpl
-                               implements InterfaceType {
+final public class InterfaceTypeImpl extends InvokableTypeImpl
+                                     implements InterfaceType {
+
+    private static class IResult implements InvocationResult {
+        final private JDWP.InterfaceType.InvokeMethod rslt;
+
+        public IResult(JDWP.InterfaceType.InvokeMethod rslt) {
+            this.rslt = rslt;
+        }
+
+        @Override
+        public ObjectReferenceImpl getException() {
+            return rslt.exception;
+        }
+
+        @Override
+        public ValueImpl getResult() {
+            return rslt.returnValue;
+        }
+
+    }
 
     private SoftReference<List<InterfaceType>> superinterfacesRef = null;
 
@@ -81,102 +98,6 @@
         return implementors;
     }
 
-    @Override
-    void addVisibleMethods(Map<String, Method> methodMap, Set<InterfaceType> seenInterfaces) {
-        /*
-         * Add methods from
-         * parent types first, so that the methods in this class will
-         * overwrite them in the hash table
-         */
-
-        for (InterfaceType interfaze : superinterfaces()) {
-            if (!seenInterfaces.contains(interfaze)) {
-                ((InterfaceTypeImpl)interfaze).addVisibleMethods(methodMap, seenInterfaces);
-                seenInterfaces.add(interfaze);
-            }
-        }
-
-        addToMethodMap(methodMap, methods());
-    }
-
-    public List<Method> allMethods() {
-        ArrayList<Method> list = new ArrayList<Method>(methods());
-
-        /*
-         * It's more efficient if don't do this
-         * recursively.
-         */
-        for (InterfaceType interfaze : allSuperinterfaces()) {
-            list.addAll(interfaze.methods());
-        }
-
-        return list;
-    }
-
-    List<InterfaceType> allSuperinterfaces() {
-        ArrayList<InterfaceType> list = new ArrayList<InterfaceType>();
-        addSuperinterfaces(list);
-        return list;
-    }
-
-    void addSuperinterfaces(List<InterfaceType> list) {
-        /*
-         * This code is a little strange because it
-         * builds the list with a more suitable order than the
-         * depth-first approach a normal recursive solution would
-         * take. Instead, all direct superinterfaces precede all
-         * indirect ones.
-         */
-
-        /*
-         * Get a list of direct superinterfaces that's not already in the
-         * list being built.
-         */
-        List<InterfaceType> immediate = new ArrayList<InterfaceType>(superinterfaces());
-        Iterator<InterfaceType> iter = immediate.iterator();
-        while (iter.hasNext()) {
-            InterfaceType interfaze = iter.next();
-            if (list.contains(interfaze)) {
-                iter.remove();
-            }
-        }
-
-        /*
-         * Add all new direct superinterfaces
-         */
-        list.addAll(immediate);
-
-        /*
-         * Recurse for all new direct superinterfaces.
-         */
-        iter = immediate.iterator();
-        while (iter.hasNext()) {
-            InterfaceTypeImpl interfaze = (InterfaceTypeImpl)iter.next();
-            interfaze.addSuperinterfaces(list);
-        }
-    }
-
-    boolean isAssignableTo(ReferenceType type) {
-
-        // Exact match?
-        if (this.equals(type)) {
-            return true;
-        } else {
-            // Try superinterfaces.
-            for (InterfaceType interfaze : superinterfaces()) {
-                if (((InterfaceTypeImpl)interfaze).isAssignableTo(type)) {
-                    return true;
-                }
-            }
-
-            return false;
-        }
-    }
-
-    List<InterfaceType> inheritedTypes() {
-        return superinterfaces();
-    }
-
     public boolean isInitialized() {
         return isPrepared();
     }
@@ -184,4 +105,39 @@
     public String toString() {
        return "interface " + name() + " (" + loaderString() + ")";
     }
-}
+
+    @Override
+    InvocationResult waitForReply(PacketStream stream) throws JDWPException {
+        return new IResult(JDWP.InterfaceType.InvokeMethod.waitForReply(vm, stream));
+    }
+
+    @Override
+    CommandSender getInvokeMethodSender(final ThreadReferenceImpl thread,
+                                        final MethodImpl method,
+                                        final ValueImpl[] args,
+                                        final int options) {
+        return () ->
+            JDWP.InterfaceType.InvokeMethod.enqueueCommand(vm,
+                                                           InterfaceTypeImpl.this,
+                                                           thread,
+                                                           method.ref(),
+                                                           args,
+                                                           options);
+    }
+
+    @Override
+    ClassType superclass() {
+        return null;
+    }
+
+    @Override
+    List<InterfaceType> interfaces() {
+        return superinterfaces();
+    }
+
+    @Override
+    boolean canInvoke(Method method) {
+        // method must be directly in this interface
+        return this.equals(method.declaringType());
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/com/sun/tools/jdi/InvokableTypeImpl.java	Tue Apr 29 11:15:21 2014 +0200
@@ -0,0 +1,305 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.tools.jdi;
+
+import com.sun.jdi.ClassNotLoadedException;
+import com.sun.jdi.ClassType;
+import com.sun.jdi.IncompatibleThreadStateException;
+import com.sun.jdi.InterfaceType;
+import com.sun.jdi.InvalidTypeException;
+import com.sun.jdi.InvocationException;
+import com.sun.jdi.Method;
+import com.sun.jdi.ReferenceType;
+import com.sun.jdi.ThreadReference;
+import com.sun.jdi.Value;
+import com.sun.jdi.VirtualMachine;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * A supertype for ReferenceTypes allowing method invocations
+ */
+abstract class InvokableTypeImpl extends ReferenceTypeImpl {
+    /**
+     * The invocation result wrapper
+     * It is necessary because both ClassType and InterfaceType
+     * use their own type to represent the invocation result
+     */
+    static interface InvocationResult {
+        ObjectReferenceImpl getException();
+        ValueImpl getResult();
+    }
+
+    InvokableTypeImpl(VirtualMachine aVm, long aRef) {
+        super(aVm, aRef);
+    }
+
+    /**
+     * Method invocation support.
+     * Shared by ClassType and InterfaceType
+     * @param threadIntf the thread in which to invoke.
+     * @param methodIntf method the {@link Method} to invoke.
+     * @param origArguments the list of {@link Value} arguments bound to the
+     * invoked method. Values from the list are assigned to arguments
+     * in the order they appear in the method signature.
+     * @param options the integer bit flag options.
+     * @return a {@link Value} mirror of the invoked method's return value.
+     * @throws java.lang.IllegalArgumentException if the method is not
+     * a member of this type, if the size of the argument list
+     * does not match the number of declared arguments for the method, or
+     * if the method is not static or is a static initializer.
+     * @throws {@link InvalidTypeException} if any argument in the
+     * argument list is not assignable to the corresponding method argument
+     * type.
+     * @throws ClassNotLoadedException if any argument type has not yet been loaded
+     * through the appropriate class loader.
+     * @throws IncompatibleThreadStateException if the specified thread has not
+     * been suspended by an event.
+     * @throws InvocationException if the method invocation resulted in
+     * an exception in the target VM.
+     * @throws InvalidTypeException If the arguments do not meet this requirement --
+     *         Object arguments must be assignment compatible with the argument
+     *         type.  This implies that the argument type must be
+     *         loaded through the enclosing class's class loader.
+     *         Primitive arguments must be either assignment compatible with the
+     *         argument type or must be convertible to the argument type without loss
+     *         of information. See JLS section 5.2 for more information on assignment
+     *         compatibility.
+     * @throws VMCannotBeModifiedException if the VirtualMachine is read-only - see {@link VirtualMachine#canBeModified()}.
+     */
+    final public Value invokeMethod(ThreadReference threadIntf, Method methodIntf,
+                                    List<? extends Value> origArguments, int options)
+                                        throws InvalidTypeException,
+                                               ClassNotLoadedException,
+                                               IncompatibleThreadStateException,
+                                               InvocationException {
+        validateMirror(threadIntf);
+        validateMirror(methodIntf);
+        validateMirrorsOrNulls(origArguments);
+        MethodImpl method = (MethodImpl) methodIntf;
+        ThreadReferenceImpl thread = (ThreadReferenceImpl) threadIntf;
+        validateMethodInvocation(method);
+        List<? extends Value> arguments = method.validateAndPrepareArgumentsForInvoke(origArguments);
+        ValueImpl[] args = arguments.toArray(new ValueImpl[0]);
+        InvocationResult ret;
+        try {
+            PacketStream stream = sendInvokeCommand(thread, method, args, options);
+            ret = waitForReply(stream);
+        } catch (JDWPException exc) {
+            if (exc.errorCode() == JDWP.Error.INVALID_THREAD) {
+                throw new IncompatibleThreadStateException();
+            } else {
+                throw exc.toJDIException();
+            }
+        }
+        /*
+         * There is an implict VM-wide suspend at the conclusion
+         * of a normal (non-single-threaded) method invoke
+         */
+        if ((options & ClassType.INVOKE_SINGLE_THREADED) == 0) {
+            vm.notifySuspend();
+        }
+        if (ret.getException() != null) {
+            throw new InvocationException(ret.getException());
+        } else {
+            return ret.getResult();
+        }
+    }
+
+    @Override
+    boolean isAssignableTo(ReferenceType type) {
+        ClassTypeImpl superclazz = (ClassTypeImpl) superclass();
+        if (this.equals(type)) {
+            return true;
+        } else if ((superclazz != null) && superclazz.isAssignableTo(type)) {
+            return true;
+        } else {
+            List<InterfaceType> interfaces = interfaces();
+            Iterator<InterfaceType> iter = interfaces.iterator();
+            while (iter.hasNext()) {
+                InterfaceTypeImpl interfaze = (InterfaceTypeImpl) iter.next();
+                if (interfaze.isAssignableTo(type)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+    }
+
+    @Override
+    final void addVisibleMethods(Map<String, Method> methodMap, Set<InterfaceType> seenInterfaces) {
+        /*
+         * Add methods from
+         * parent types first, so that the methods in this class will
+         * overwrite them in the hash table
+         */
+        Iterator<InterfaceType> iter = interfaces().iterator();
+        while (iter.hasNext()) {
+            InterfaceTypeImpl interfaze = (InterfaceTypeImpl) iter.next();
+            if (!seenInterfaces.contains(interfaze)) {
+                interfaze.addVisibleMethods(methodMap, seenInterfaces);
+                seenInterfaces.add(interfaze);
+            }
+        }
+        ClassTypeImpl clazz = (ClassTypeImpl) superclass();
+        if (clazz != null) {
+            clazz.addVisibleMethods(methodMap, seenInterfaces);
+        }
+        addToMethodMap(methodMap, methods());
+    }
+
+    final void addInterfaces(List<InterfaceType> list) {
+        List<InterfaceType> immediate = interfaces();
+        list.addAll(interfaces());
+        Iterator<InterfaceType> iter = immediate.iterator();
+        while (iter.hasNext()) {
+            InterfaceTypeImpl interfaze = (InterfaceTypeImpl) iter.next();
+            interfaze.addInterfaces(list);
+        }
+        ClassTypeImpl superclass = (ClassTypeImpl) superclass();
+        if (superclass != null) {
+            superclass.addInterfaces(list);
+        }
+    }
+
+    /**
+     * Returns all the implemented interfaces recursively
+     * @return A list of all the implemented interfaces (recursively)
+     */
+    final List<InterfaceType> getAllInterfaces() {
+        List<InterfaceType> all = new ArrayList<>();
+        addInterfaces(all);
+        return all;
+    }
+
+    /**
+     * Shared implementation of {@linkplain ClassType#allMethods()} and
+     * {@linkplain InterfaceType#allMethods()}
+     * @return A list of all methods (recursively)
+     */
+    public final List<Method> allMethods() {
+        ArrayList<Method> list = new ArrayList<>(methods());
+        ClassType clazz = superclass();
+        while (clazz != null) {
+            list.addAll(clazz.methods());
+            clazz = clazz.superclass();
+        }
+        /*
+         * Avoid duplicate checking on each method by iterating through
+         * duplicate-free allInterfaces() rather than recursing
+         */
+        for (InterfaceType interfaze : getAllInterfaces()) {
+            list.addAll(interfaze.methods());
+        }
+        return list;
+    }
+
+    @Override
+    final List<ReferenceType> inheritedTypes() {
+        List<ReferenceType> inherited = new ArrayList<>();
+        if (superclass() != null) {
+            inherited.add(0, superclass()); /* insert at front */
+        }
+        for (ReferenceType rt : interfaces()) {
+            inherited.add(rt);
+        }
+        return inherited;
+    }
+
+    private PacketStream sendInvokeCommand(final ThreadReferenceImpl thread,
+                                           final MethodImpl method,
+                                           final ValueImpl[] args,
+                                           final int options) {
+        CommandSender sender = getInvokeMethodSender(thread, method, args, options);
+        PacketStream stream;
+        if ((options & ClassType.INVOKE_SINGLE_THREADED) != 0) {
+            stream = thread.sendResumingCommand(sender);
+        } else {
+            stream = vm.sendResumingCommand(sender);
+        }
+        return stream;
+    }
+
+    private void validateMethodInvocation(Method method)
+                                            throws InvalidTypeException,
+                                                   InvocationException {
+        if (!canInvoke(method)) {
+            throw new IllegalArgumentException("Invalid method");
+        }
+        /*
+         * Method must be a static and not a static initializer
+         */
+        if (!method.isStatic()) {
+            throw new IllegalArgumentException("Cannot invoke instance method on a class/interface type");
+        } else if (method.isStaticInitializer()) {
+            throw new IllegalArgumentException("Cannot invoke static initializer");
+        }
+    }
+
+    /**
+     * A subclass will provide specific {@linkplain CommandSender}
+     * @param thread the current invocation thread
+     * @param method the method to invoke
+     * @param args the arguments to pass to the method
+     * @param options the integer bit flag options
+     * @return the specific {@literal CommandSender} instance
+     */
+    abstract CommandSender getInvokeMethodSender(ThreadReferenceImpl thread,
+                                                 MethodImpl method,
+                                                 ValueImpl[] args,
+                                                 int options);
+
+    /**
+     * Waits for the reply to the last sent command
+     * @param stream the stream to listen for the reply on
+     * @return the {@linkplain InvocationResult} instance
+     * @throws JDWPException when something goes wrong in JDWP
+     */
+    abstract InvocationResult waitForReply(PacketStream stream) throws JDWPException;
+
+    /**
+     * Get the {@linkplain ReferenceType} superclass
+     * @return the superclass or null
+     */
+    abstract ClassType superclass();
+
+    /**
+     * Get the implemented/extended interfaces
+     * @return the list of implemented/extended interfaces
+     */
+    abstract List<InterfaceType> interfaces();
+
+    /**
+     * Checks the provided method whether it can be invoked
+     * @param method the method to check
+     * @return {@code TRUE} if the implementation knows how to invoke the method,
+     *         {@code FALSE} otherwise
+     */
+    abstract boolean canInvoke(Method method);
+}
--- a/jdk/src/share/classes/com/sun/tools/jdi/MethodImpl.java	Mon Apr 28 13:49:49 2014 +0100
+++ b/jdk/src/share/classes/com/sun/tools/jdi/MethodImpl.java	Tue Apr 29 11:15:21 2014 +0200
@@ -187,6 +187,13 @@
         return isModifierSet(VMModifiers.ABSTRACT);
     }
 
+    public boolean isDefault() {
+        return !isModifierSet(VMModifiers.ABSTRACT) &&
+               !isModifierSet(VMModifiers.STATIC) &&
+               !isModifierSet(VMModifiers.PRIVATE) &&
+               declaringType() instanceof InterfaceType;
+    }
+
     public boolean isSynchronized() {
         return isModifierSet(VMModifiers.SYNCHRONIZED);
     }
--- a/jdk/src/share/classes/com/sun/tools/jdi/ObjectReferenceImpl.java	Mon Apr 28 13:49:49 2014 +0100
+++ b/jdk/src/share/classes/com/sun/tools/jdi/ObjectReferenceImpl.java	Tue Apr 29 11:15:21 2014 +0200
@@ -277,7 +277,6 @@
     void validateMethodInvocation(Method method, int options)
                                          throws InvalidTypeException,
                                          InvocationException {
-
         /*
          * Method must be in this object's class, a superclass, or
          * implemented interface
@@ -287,6 +286,19 @@
             throw new IllegalArgumentException("Invalid method");
         }
 
+        if (declType instanceof ClassTypeImpl) {
+            validateClassMethodInvocation(method, options);
+        } else if (declType instanceof InterfaceTypeImpl) {
+            validateIfaceMethodInvocation(method, options);
+        } else {
+            throw new InvalidTypeException();
+        }
+    }
+
+    void validateClassMethodInvocation(Method method, int options)
+                                         throws InvalidTypeException,
+                                         InvocationException {
+
         ClassTypeImpl clazz = invokableReferenceType(method);
 
         /*
@@ -300,9 +312,7 @@
          * For nonvirtual invokes, method must have a body
          */
         if ((options & INVOKE_NONVIRTUAL) != 0) {
-            if (method.declaringType() instanceof InterfaceType) {
-                throw new IllegalArgumentException("Interface method");
-            } else if (method.isAbstract()) {
+            if (method.isAbstract()) {
                 throw new IllegalArgumentException("Abstract method");
             }
         }
@@ -324,7 +334,7 @@
              */
             Method invoker = clazz.concreteMethodByName(method.name(),
                                                         method.signature());
-            //  isAssignableFrom check above guarantees non-null
+            //  invoker is supposed to be non-null under normal circumstances
             invokedClass = (ClassTypeImpl)invoker.declaringType();
         }
         /* The above code is left over from previous versions.
@@ -332,6 +342,17 @@
          */
     }
 
+    void validateIfaceMethodInvocation(Method method, int options)
+                                         throws InvalidTypeException,
+                                         InvocationException {
+        /*
+         * Only default methods allowed for nonvirtual invokes
+         */
+        if (!method.isDefault()) {
+            throw new IllegalArgumentException("Not a default method");
+        }
+    }
+
     PacketStream sendInvokeCommand(final ThreadReferenceImpl thread,
                                    final ClassTypeImpl refType,
                                    final MethodImpl method,
@@ -370,7 +391,10 @@
         ThreadReferenceImpl thread = (ThreadReferenceImpl)threadIntf;
 
         if (method.isStatic()) {
-            if (referenceType() instanceof ClassType) {
+            if (referenceType() instanceof InterfaceType) {
+                InterfaceType type = (InterfaceType)referenceType();
+                return type.invokeMethod(thread, method, origArguments, options);
+            } else if (referenceType() instanceof ClassType) {
                 ClassType type = (ClassType)referenceType();
                 return type.invokeMethod(thread, method, origArguments, options);
             } else {
--- a/jdk/src/share/classes/com/sun/tools/jdi/VirtualMachineManagerImpl.java	Mon Apr 28 13:49:49 2014 +0100
+++ b/jdk/src/share/classes/com/sun/tools/jdi/VirtualMachineManagerImpl.java	Tue Apr 29 11:15:21 2014 +0200
@@ -48,7 +48,7 @@
     private ResourceBundle messages = null;
     private int vmSequenceNumber = 0;
     private static final int majorVersion = 1;
-    private static final int minorVersion = 6;
+    private static final int minorVersion = 8;
 
     private static final Object lock = new Object();
     private static VirtualMachineManagerImpl vmm;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/com/sun/jdi/EvalInterfaceStatic.sh	Tue Apr 29 11:15:21 2014 +0200
@@ -0,0 +1,126 @@
+#!/bin/sh
+
+#
+# Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# This code is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License version 2 only, as
+# published by the Free Software Foundation.
+#
+# This code is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+# version 2 for more details (a copy is included in the LICENSE file that
+# accompanied this code).
+#
+# You should have received a copy of the GNU General Public License version
+# 2 along with this work; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+# or visit www.oracle.com if you need additional information or have any
+# questions.
+#
+
+#  @test
+#  @bug 8031195
+#  @summary JDB allows evaluation of calls to static interface methods
+#  @author Jaroslav Bachorik
+#
+#  @run shell/timeout=300 EvalInterfaceStatic.sh
+
+#  The test exercises the ability to invoke static methods on interfaces.
+#  Static interface methods are a new feature added in JDK8.
+#
+#  The test makes sure that it is, at all, possible to invoke an interface
+#  static method and that the static methods are not inherited by extending
+#  interfaces.
+
+classname=EvalStaticInterfaces
+
+createJavaFile()
+{
+    cat <<EOF > $classname.java.1
+public interface $classname {
+    static String staticMethod1() {
+        return "base:staticMethod1";
+    }
+
+    static String staticMethod2() {
+        return "base:staticMethod2";
+    }
+
+    public static void main(String[] args) {
+        // prove that these work
+        System.out.println("base staticMethod1(): " + $classname.staticMethod1());
+        System.out.println("base staticMethod2(): " + $classname.staticMethod2());
+        System.out.println("overridden staticMethod2(): " + Extended$classname.staticMethod2());
+        System.out.println("base staticMethod3(): " + Extended$classname.staticMethod3());
+
+        gus();
+    }
+
+    static void gus() {
+        int x = 0;             // @1 breakpoint
+    }
+}
+
+interface Extended$classname extends $classname {
+    static String staticMethod2() {
+        return "extended:staticMethod2";
+    }
+
+    static String staticMethod3() {
+        return "extended:staticMethod3";
+    }
+}
+
+
+
+EOF
+}
+
+# drive jdb by sending cmds to it and examining its output
+dojdbCmds()
+{
+    setBkpts @1
+    runToBkpt @1
+
+    cmd eval "$classname.staticMethod1()"
+    jdbFailIfNotPresent "base:staticMethod1" 2
+
+    cmd eval "$classname.staticMethod2()"
+    jdbFailIfNotPresent "base:staticMethod2" 2
+
+    cmd eval "Extended$classname.staticMethod1()"
+    jdbFailIfPresent "base:staticMethod1" 2
+
+    cmd eval "Extended$classname.staticMethod2()"
+    jdbFailIfNotPresent "extended:staticMethod2" 2
+
+    cmd eval "Extended$classname.staticMethod3()"
+    jdbFailIfNotPresent "extended:staticMethod3" 2
+}
+
+
+mysetup()
+{
+    if [ -z "$TESTSRC" ] ; then
+        TESTSRC=.
+    fi
+
+    for ii in . $TESTSRC $TESTSRC/.. ; do
+        if [ -r "$ii/ShellScaffold.sh" ] ; then
+            . $ii/ShellScaffold.sh
+            break
+        fi
+    done
+}
+
+# You could replace this next line with the contents
+# of ShellScaffold.sh and this script will run just the same.
+mysetup
+
+runit
+pass
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/com/sun/jdi/InterfaceMethodsTest.java	Tue Apr 29 11:15:21 2014 +0200
@@ -0,0 +1,422 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ *  @test
+ *  @bug 8031195
+ *  @summary  JDI: Add support for static and default methods in interfaces
+ *
+ *  @run build TestScaffold VMConnection TargetListener TargetAdapter
+ *  @run build InterfaceMethodsTest
+ *  @run main InterfaceMethodsTest
+ */
+import com.sun.jdi.*;
+import com.sun.jdi.event.*;
+import java.util.Collections;
+
+public class InterfaceMethodsTest extends TestScaffold {
+    private static final int RESULT_A = 1;
+    private static final int RESULT_B = 1;
+    private static final int RESULT_TARGET = 1;
+    static interface InterfaceA {
+        static int staticMethodA() {
+            System.out.println("-InterfaceA: static interface method A-");
+            return RESULT_A;
+        }
+        static int staticMethodB() {
+            System.out.println("-InterfaceA: static interface method B-");
+            return RESULT_A;
+        }
+        default int defaultMethodA() {
+            System.out.println("-InterfaceA: default interface method A-");
+            return RESULT_A;
+        }
+        default int defaultMethodB() {
+            System.out.println("-InterfaceA: default interface method B-");
+            return RESULT_A;
+        }
+        default int defaultMethodC() {
+            System.out.println("-InterfaceA: default interface method C-");
+            return RESULT_A;
+        }
+
+        int implementedMethod();
+    }
+
+    static interface InterfaceB extends InterfaceA {
+        @Override
+        default int defaultMethodC() {
+            System.out.println("-InterfaceB: overridden default interface method C-");
+            return RESULT_B;
+        }
+        default int defaultMethodD() {
+            System.out.println("-InterfaceB: default interface method D-");
+            return RESULT_B;
+        }
+
+        static int staticMethodB() {
+            System.out.println("-InterfaceB: overridden static interface method B-");
+            return RESULT_B;
+        }
+
+        static int staticMethodC() {
+            System.out.println("-InterfaceB: static interface method C-");
+            return RESULT_B;
+        }
+    }
+
+    final static class TargetClass implements InterfaceB {
+        public int classMethod() {
+            System.out.println("-TargetClass: class only method-");
+            return RESULT_TARGET;
+        }
+
+        @Override
+        public int implementedMethod() {
+            System.out.println("-TargetClass: implemented non-default interface method-");
+            return RESULT_TARGET;
+        }
+
+        @Override
+        public int defaultMethodB() {
+            System.out.println("-TargetClass: overridden default interface method D");
+
+            return RESULT_TARGET;
+        }
+
+        public static void main(String[] args) {
+            TargetClass tc = new TargetClass();
+            tc.doTests(tc);
+        }
+
+        private void doTests(TargetClass ref) {
+            // break
+        }
+    }
+
+    public InterfaceMethodsTest(String[] args) {
+        super(args);
+    }
+
+    public static void main(String[] args) throws Exception {
+        new InterfaceMethodsTest(args).startTests();
+    }
+
+    private static final String TEST_CLASS_NAME = InterfaceMethodsTest.class.getName().replace('.', '/');
+    private static final String TARGET_CLASS_NAME = TargetClass.class.getName().replace('.', '/');
+    private static final String INTERFACEA_NAME = InterfaceA.class.getName().replace('.', '/');
+    private static final String INTERFACEB_NAME = InterfaceB.class.getName().replace('.', '/');
+
+    protected void runTests() throws Exception {
+        /*
+         * Get to the top of main()
+         * to determine targetClass and mainThread
+         */
+        BreakpointEvent bpe = startToMain(TARGET_CLASS_NAME);
+
+        bpe = resumeTo(TARGET_CLASS_NAME, "doTests", "(L" + TARGET_CLASS_NAME +";)V");
+
+        mainThread = bpe.thread();
+
+        StackFrame frame = mainThread.frame(0);
+        ObjectReference thisObject = frame.thisObject();
+        ObjectReference ref = (ObjectReference)frame.getArgumentValues().get(0);
+
+        ReferenceType targetClass = bpe.location().declaringType();
+        testImplementationClass(targetClass, thisObject);
+
+        testInterfaceA(ref);
+
+        testInterfaceB(ref);
+
+        /*
+         * resume the target listening for events
+         */
+        listenUntilVMDisconnect();
+
+        /*
+         * deal with results of test
+         * if anything has called failure("foo") testFailed will be true
+         */
+        if (!testFailed) {
+            println("InterfaceMethodsTest: passed");
+        } else {
+            throw new Exception("InterfaceMethodsTest: failed");
+        }
+    }
+
+    private void testInterfaceA(ObjectReference ref) {
+        // Test non-virtual calls on InterfaceA
+
+        ReferenceType ifaceClass = (ReferenceType)vm().classesByName(INTERFACEA_NAME).get(0);
+        /* Default method calls */
+
+        // invoke the InterfaceA's "defaultMethodA"
+        testInvokePos(ifaceClass, ref, "defaultMethodA", "()I", vm().mirrorOf(RESULT_A));
+
+        // invoke the InterfaceA's "defaultMethodB"
+        testInvokePos(ifaceClass, ref, "defaultMethodB", "()I", vm().mirrorOf(RESULT_A));
+
+        // invoke the InterfaceA's "defaultMethodC"
+        testInvokePos(ifaceClass, ref, "defaultMethodC", "()I", vm().mirrorOf(RESULT_A));
+
+        // "defaultMethodD" from InterfaceB is not accessible from here
+        testInvokeNeg(ifaceClass, ref, "defaultMethodD", "()I", vm().mirrorOf(RESULT_B),
+                "Attempted to invoke non-existing method");
+
+        // trying to invoke the asbtract method "implementedMethod"
+        testInvokeNeg(ifaceClass, ref, "implementedMethod", "()I", vm().mirrorOf(TARGET_CLASS_NAME),
+                "Invocation of non-default methods is not supported");
+
+
+        /* Static method calls */
+
+        // invoke interface static method A
+        testInvokePos(ifaceClass, null, "staticMethodA", "()I", vm().mirrorOf(RESULT_A));
+
+        // try to invoke static method A on the instance
+        testInvokePos(ifaceClass, ref, "staticMethodA", "()I", vm().mirrorOf(RESULT_A));
+
+        // invoke interface static method B
+        testInvokePos(ifaceClass, null, "staticMethodB", "()I", vm().mirrorOf(RESULT_A));
+
+        // try to invoke static method B on the instance
+        testInvokePos(ifaceClass, ref, "staticMethodB", "()I", vm().mirrorOf(RESULT_A));
+    }
+
+    private void testInterfaceB(ObjectReference ref) {
+        // Test non-virtual calls on InterfaceB
+        ReferenceType ifaceClass = (ReferenceType)vm().classesByName(INTERFACEB_NAME).get(0);
+
+        /* Default method calls */
+
+        // invoke the inherited "defaultMethodA"
+        testInvokePos(ifaceClass, ref, "defaultMethodA", "()I", vm().mirrorOf(RESULT_A));
+
+        // invoke the inherited "defaultMethodB"
+        testInvokePos(ifaceClass, ref, "defaultMethodB", "()I", vm().mirrorOf(RESULT_A));
+
+        // invoke the inherited and overridden "defaultMethodC"
+        testInvokePos(ifaceClass, ref, "defaultMethodC", "()I", vm().mirrorOf(RESULT_B));
+
+        // invoke InterfaceB only "defaultMethodD"
+        testInvokePos(ifaceClass, ref, "defaultMethodD", "()I", vm().mirrorOf(RESULT_B));
+
+        // "implementedMethod" is not present in InterfaceB
+        testInvokeNeg(ifaceClass, ref, "implementedMethod", "()I", vm().mirrorOf(RESULT_TARGET),
+                "Invocation of non-default methods is not supported");
+
+
+        /* Static method calls*/
+
+        // "staticMethodA" must not be inherited by InterfaceB
+        testInvokeNeg(ifaceClass, null, "staticMethodA", "()I", vm().mirrorOf(RESULT_A),
+                "Static interface methods are not inheritable");
+
+        // however it is possible to call "staticMethodA" on the actual instance
+        testInvokeNeg(ifaceClass, ref, "staticMethodA", "()I", vm().mirrorOf(RESULT_A),
+                "Static interface methods are not inheritable");
+
+        // "staticMethodB" is overridden in InterfaceB
+        testInvokePos(ifaceClass, null, "staticMethodB", "()I", vm().mirrorOf(RESULT_B));
+
+        // the instance invokes the overriden form of "staticMethodB" from InterfaceB
+        testInvokePos(ifaceClass, ref, "staticMethodB", "()I", vm().mirrorOf(RESULT_B));
+
+        // "staticMethodC" is present only in InterfaceB
+        testInvokePos(ifaceClass, null, "staticMethodC", "()I", vm().mirrorOf(RESULT_B));
+
+        // "staticMethodC" should be reachable from the instance too
+        testInvokePos(ifaceClass, ref, "staticMethodC", "()I", vm().mirrorOf(RESULT_B));
+    }
+
+    private void testImplementationClass(ReferenceType targetClass, ObjectReference thisObject) {
+        // Test invocations on the implementation object
+
+        /* Default method calls */
+
+        // "defaultMethodA" is accessible and not overridden
+        testInvokePos(targetClass, thisObject, "defaultMethodA", "()I", vm().mirrorOf(RESULT_TARGET));
+
+        // "defaultMethodB" is accessible and overridden in TargetClass
+        testInvokePos(targetClass, thisObject, "defaultMethodB", "()I", vm().mirrorOf(RESULT_TARGET));
+
+        // "defaultMethodC" is accessible and overridden in InterfaceB
+        testInvokePos(targetClass, thisObject, "defaultMethodC", "()I", vm().mirrorOf(RESULT_TARGET));
+
+        // "defaultMethodD" is accessible
+        testInvokePos(targetClass, thisObject, "defaultMethodD", "()I", vm().mirrorOf(RESULT_TARGET));
+
+
+        /* Non-default instance method calls */
+
+        // "classMethod" declared in TargetClass is accessible
+        testInvokePos(targetClass, thisObject, "classMethod", "()I", vm().mirrorOf(RESULT_TARGET));
+
+        // the abstract "implementedMethod" has been implemented in TargetClass
+        testInvokePos(targetClass, thisObject, "implementedMethod", "()I", vm().mirrorOf(RESULT_TARGET));
+
+
+        /* Static method calls */
+
+        // All the static methods declared by the interfaces are not reachable from the instance of the implementor class
+        testInvokeNeg(targetClass, thisObject, "staticMethodA", "()I", vm().mirrorOf(RESULT_A),
+                "Static interface methods are not inheritable");
+
+        testInvokeNeg(targetClass, thisObject, "staticMethodB", "()I", vm().mirrorOf(RESULT_B),
+                "Static interface methods are not inheritable");
+
+        testInvokeNeg(targetClass, thisObject, "staticMethodC", "()I", vm().mirrorOf(RESULT_B),
+                "Static interface methods are not inheritable");
+
+        // All the static methods declared by the interfaces are not reachable through the implementor class
+        testInvokeNeg(targetClass, null, "staticMethodA", "()I", vm().mirrorOf(RESULT_A),
+                "Static interface methods are not inheritable");
+
+        testInvokeNeg(targetClass, null, "staticMethodB", "()I", vm().mirrorOf(RESULT_B),
+                "Static interface methods are not inheritable");
+
+        testInvokeNeg(targetClass, null, "staticMethodC", "()I", vm().mirrorOf(RESULT_B),
+                "Static interface methods are not inheritable");
+    }
+
+    private void testInvokePos(ReferenceType targetClass, ObjectReference ref, String methodName,
+                               String methodSig, Value value) {
+        logInvocation(ref, methodName, methodSig, targetClass);
+        try {
+            invoke(targetClass, ref, methodName, methodSig, value);
+            System.err.println("--- PASSED");
+        } catch (Exception e) {
+            System.err.println("--- FAILED");
+            failure("FAILED: Invocation failed with error message " + e.getLocalizedMessage());
+        }
+    }
+
+    private void testInvokeNeg(ReferenceType targetClass, ObjectReference ref, String methodName,
+                               String methodSig, Value value, String msg) {
+        logInvocation(ref, methodName, methodSig, targetClass);
+        try {
+            invoke(targetClass, ref, methodName, methodSig, value);
+            System.err.println("--- FAILED");
+            failure("FAILED: " + msg);
+        } catch (Exception e) {
+            System.err.println("--- PASSED");
+
+        }
+    }
+
+    private void invoke(ReferenceType targetClass, ObjectReference ref, String methodName,
+                        String methodSig, Value value)
+    throws Exception {
+        Method method = getMethod(targetClass, methodName, methodSig);
+        if (method == null) {
+            throw new Exception("Can't find method: " + methodName  + " for class = " + targetClass);
+        }
+
+        println("Invoking " + (method.isAbstract() ? "abstract " : " ") + "method: " + method);
+
+        Value returnValue = null;
+        if (ref != null) {
+            returnValue = invokeInstance(ref, method);
+        } else {
+            returnValue = invokeStatic(targetClass, method);
+        }
+
+        println("        return val = " + returnValue);
+        // It has to be the same value as what we passed in!
+        if (returnValue.equals(value)) {
+            println("         " + method.name() + " return value matches: "
+                    + value);
+        } else {
+            if (value != null) {
+                throw new Exception(method.name() + " returned: " + returnValue +
+                                    " expected: " + value );
+            } else {
+                println("         " + method.name() + " return value : " + returnValue);
+            }
+
+        }
+    }
+
+    private Value invokeInstance(ObjectReference ref, Method method) throws Exception {
+        return ref.invokeMethod(mainThread, method, Collections.emptyList(), ObjectReference.INVOKE_NONVIRTUAL);
+    }
+
+    private Value invokeStatic(ReferenceType refType, Method method) throws Exception {
+        if (refType instanceof ClassType) {
+            return ((ClassType)refType).invokeMethod(mainThread, method, Collections.emptyList(), ObjectReference.INVOKE_NONVIRTUAL);
+        } else {
+            return ((InterfaceType)refType).invokeMethod(mainThread, method, Collections.emptyList(), ObjectReference.INVOKE_NONVIRTUAL);
+        }
+    }
+
+    private Method getMethod(ReferenceType rt, String name, String signature) {
+        if (rt == null) return null;
+        Method m = findMethod(rt, name, signature);
+        if (m == null) {
+            if (rt instanceof ClassType) {
+                for (Object ifc : ((ClassType)rt).interfaces()) {
+                    m = getMethod((ReferenceType)ifc, name, signature);
+                    if (m != null) {
+                        break;
+                    }
+                }
+                if (m == null) {
+                    m = getMethod(((ClassType)rt).superclass(), name, signature);
+                } else {
+                    if (m.isStatic()) {
+                        // interface static methods are not inherited
+                        m = null;
+                    }
+                }
+            } else if (rt instanceof InterfaceType) {
+                for(Object ifc : ((InterfaceType)rt).superinterfaces()) {
+                    m = getMethod((ReferenceType)ifc, name, signature);
+                    if (m != null) {
+                        if (m.isStatic()) {
+                            // interface static methods are not inherited
+                            m = null;
+                        }
+                        break;
+                    }
+                }
+            }
+        }
+
+        return m;
+    }
+
+    private void logInvocation(ObjectReference ref, String methodName, String methodSig, ReferenceType targetClass) {
+        if (ref != null) {
+            System.err.println("Invoking: " + ref.referenceType().name() + "." +
+                    methodName + methodSig + " with target of type " +
+                    targetClass.name());
+        } else {
+            System.err.println("Invoking static : " + targetClass.name() + "." +
+                    methodName + methodSig);
+        }
+    }
+}
+
+
+