8031195: Support default and static interface methods in JDI, JDWP and JDB
Reviewed-by: sla, sspitsyn, dcubed
--- 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™ 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);
+ }
+ }
+}
+
+
+