diff --git a/jdk/make/data/jdwp/jdwp.spec b/jdk/make/data/jdwp/jdwp.spec index 0df8837c2cb..8d05bebcc02 100644 --- a/jdk/make/data/jdwp/jdwp.spec +++ b/jdk/make/data/jdwp/jdwp.spec @@ -1147,7 +1147,8 @@ JDWP "Java(tm) Debug Wire Protocol" (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 @@ JDWP "Java(tm) Debug Wire Protocol" ) ) (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. " + "

Since JDWP version 1.8 " + "

" + "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. " + "

" + "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. " + "

" + "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. " + "

" + "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. " + "

" + "The resumption of other threads during the invoke can be prevented " + "by specifying the INVOKE_SINGLE_THREADED " + "bit flag in the options 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. " + "

" + "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 options") + ) + (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 @@ JDWP "Java(tm) Debug Wire Protocol" "

" "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 @@ JDWP "Java(tm) Debug Wire Protocol" (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) diff --git a/jdk/src/share/back/InterfaceTypeImpl.c b/jdk/src/share/back/InterfaceTypeImpl.c new file mode 100644 index 00000000000..f25d353a58e --- /dev/null +++ b/jdk/src/share/back/InterfaceTypeImpl.c @@ -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 +}; diff --git a/jdk/src/share/back/InterfaceTypeImpl.h b/jdk/src/share/back/InterfaceTypeImpl.h new file mode 100644 index 00000000000..ff290004c03 --- /dev/null +++ b/jdk/src/share/back/InterfaceTypeImpl.h @@ -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[]; diff --git a/jdk/src/share/back/VirtualMachineImpl.c b/jdk/src/share/back/VirtualMachineImpl.c index 34c4a17b32e..e8563924375 100644 --- a/jdk/src/share/back/VirtualMachineImpl.c +++ b/jdk/src/share/back/VirtualMachineImpl.c @@ -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) diff --git a/jdk/src/share/back/debugDispatch.c b/jdk/src/share/back/debugDispatch.c index 1b1c782441d..e9e8c0e7bb8 100644 --- a/jdk/src/share/back/debugDispatch.c +++ b/jdk/src/share/back/debugDispatch.c @@ -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 @@ debugDispatch_initialize(void) 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; diff --git a/jdk/src/share/back/util.c b/jdk/src/share/back/util.c index 2bbd61bedc1..70b93dc4317 100644 --- a/jdk/src/share/back/util.c +++ b/jdk/src/share/back/util.c @@ -591,6 +591,8 @@ sharedInvoke(PacketInputStream *in, PacketOutputStream *out) 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 { diff --git a/jdk/src/share/classes/com/sun/jdi/ClassType.java b/jdk/src/share/classes/com/sun/jdi/ClassType.java index 919f3ab699b..296f2e730fa 100644 --- a/jdk/src/share/classes/com/sun/jdi/ClassType.java +++ b/jdk/src/share/classes/com/sun/jdi/ClassType.java @@ -103,7 +103,7 @@ public interface ClassType extends ReferenceType { *

* 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 @@ public interface ClassType extends ReferenceType { *

* 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 @@ public interface ClassType extends ReferenceType { * @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 @@ public interface ClassType extends ReferenceType { * @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 @@ public interface ClassType extends ReferenceType { *

* 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 @@ public interface ClassType extends ReferenceType { * @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 diff --git a/jdk/src/share/classes/com/sun/jdi/InterfaceType.java b/jdk/src/share/classes/com/sun/jdi/InterfaceType.java index 3b2790a34ea..9436e487387 100644 --- a/jdk/src/share/classes/com/sun/jdi/InterfaceType.java +++ b/jdk/src/share/classes/com/sun/jdi/InterfaceType.java @@ -79,4 +79,123 @@ public interface InterfaceType extends ReferenceType { * If none exist, returns a zero length List. */ List 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. + *

+ * 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}. + *

+ * 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. + *

+ * 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 + * The Java™ Language Specification + * for more information on assignment compatibility. + *

+ * 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. + *

+ * The resumption of other threads during the invocation can be prevented + * by specifying the {@link #INVOKE_SINGLE_THREADED} + * bit flag in the options 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. + *

+ * 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 + */ + default Value invokeMethod(ThreadReference thread, Method method, + List arguments, int options) + throws InvalidTypeException, + ClassNotLoadedException, + IncompatibleThreadStateException, + InvocationException { + throw new UnsupportedOperationException(); + } } diff --git a/jdk/src/share/classes/com/sun/jdi/Method.java b/jdk/src/share/classes/com/sun/jdi/Method.java index 19b13e0d464..eb81b058c89 100644 --- a/jdk/src/share/classes/com/sun/jdi/Method.java +++ b/jdk/src/share/classes/com/sun/jdi/Method.java @@ -137,6 +137,18 @@ public interface Method extends TypeComponent, Locatable, Comparable { */ boolean isAbstract(); + /** + * Determine if this method is a default method + * + * @return true if the method is declared default; + * false otherwise + * + * @since 1.8 + */ + default boolean isDefault() { + throw new UnsupportedOperationException(); + } + /** * Determine if this method is synchronized. * diff --git a/jdk/src/share/classes/com/sun/jdi/ObjectReference.java b/jdk/src/share/classes/com/sun/jdi/ObjectReference.java index 797d5adfd2a..b6bf5a3eb19 100644 --- a/jdk/src/share/classes/com/sun/jdi/ObjectReference.java +++ b/jdk/src/share/classes/com/sun/jdi/ObjectReference.java @@ -194,10 +194,10 @@ public interface ObjectReference extends Value { * {@link #INVOKE_NONVIRTUAL} bit flag in the options * 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 super 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 super keyword in the Java programming + * language. *

* By default, all threads in the target VM are resumed while * the method is being invoked if they were previously @@ -246,10 +246,10 @@ public interface ObjectReference extends Value { * @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. diff --git a/jdk/src/share/classes/com/sun/tools/example/debug/expr/LValue.java b/jdk/src/share/classes/com/sun/tools/example/debug/expr/LValue.java index e7f5f95bfc8..4ae7d68b413 100644 --- a/jdk/src/share/classes/com/sun/tools/example/debug/expr/LValue.java +++ b/jdk/src/share/classes/com/sun/tools/example/debug/expr/LValue.java @@ -559,6 +559,9 @@ abstract class LValue { } 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()); diff --git a/jdk/src/share/classes/com/sun/tools/jdi/ClassTypeImpl.java b/jdk/src/share/classes/com/sun/tools/jdi/ClassTypeImpl.java index 16b46e6be4e..b815d1acef4 100644 --- a/jdk/src/share/classes/com/sun/tools/jdi/ClassTypeImpl.java +++ b/jdk/src/share/classes/com/sun/tools/jdi/ClassTypeImpl.java @@ -29,9 +29,27 @@ import com.sun.jdi.*; 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 @@ public class ClassTypeImpl extends ReferenceTypeImpl return superclass; } + @Override public List interfaces() { if (interfaces == null) { interfaces = getInterfaces(); @@ -72,26 +91,9 @@ public class ClassTypeImpl extends ReferenceTypeImpl return interfaces; } - void addInterfaces(List list) { - List immediate = interfaces(); - list.addAll(interfaces()); - - Iterator 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 allInterfaces() { - List all = new ArrayList(); - addInterfaces(all); - return all; + @Override + public List allInterfaces() { + return getAllInterfaces(); } public List subclasses() { @@ -159,28 +161,6 @@ public class ClassTypeImpl extends ReferenceTypeImpl } } - 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 @@ public class ClassTypeImpl extends ReferenceTypeImpl return stream; } - public Value invokeMethod(ThreadReference threadIntf, Method methodIntf, - List 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 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 origArguments, @@ -311,58 +245,6 @@ public class ClassTypeImpl extends ReferenceTypeImpl return method; } - public List allMethods() { - ArrayList 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 : allInterfaces()) { - list.addAll(interfaze.methods()); - } - - return list; - } - - List inheritedTypes() { - List inherited = new ArrayList(); - 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 @@ public class ClassTypeImpl extends ReferenceTypeImpl } } - @Override - void addVisibleMethods(Map methodMap, Set seenInterfaces) { - /* - * Add methods from - * parent types first, so that the methods in this class will - * overwrite them in the hash table - */ - - Iterator 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 interfaces = interfaces(); - Iterator 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); + } } diff --git a/jdk/src/share/classes/com/sun/tools/jdi/InterfaceTypeImpl.java b/jdk/src/share/classes/com/sun/tools/jdi/InterfaceTypeImpl.java index 8e81c56ed88..da7e55a2d9f 100644 --- a/jdk/src/share/classes/com/sun/tools/jdi/InterfaceTypeImpl.java +++ b/jdk/src/share/classes/com/sun/tools/jdi/InterfaceTypeImpl.java @@ -29,14 +29,31 @@ import com.sun.jdi.*; 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> superinterfacesRef = null; @@ -81,102 +98,6 @@ public class InterfaceTypeImpl extends ReferenceTypeImpl return implementors; } - @Override - void addVisibleMethods(Map methodMap, Set 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 allMethods() { - ArrayList list = new ArrayList(methods()); - - /* - * It's more efficient if don't do this - * recursively. - */ - for (InterfaceType interfaze : allSuperinterfaces()) { - list.addAll(interfaze.methods()); - } - - return list; - } - - List allSuperinterfaces() { - ArrayList list = new ArrayList(); - addSuperinterfaces(list); - return list; - } - - void addSuperinterfaces(List 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 immediate = new ArrayList(superinterfaces()); - Iterator 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 inheritedTypes() { - return superinterfaces(); - } - public boolean isInitialized() { return isPrepared(); } @@ -184,4 +105,39 @@ public class InterfaceTypeImpl extends ReferenceTypeImpl 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 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 diff --git a/jdk/src/share/classes/com/sun/tools/jdi/InvokableTypeImpl.java b/jdk/src/share/classes/com/sun/tools/jdi/InvokableTypeImpl.java new file mode 100644 index 00000000000..61a23301861 --- /dev/null +++ b/jdk/src/share/classes/com/sun/tools/jdi/InvokableTypeImpl.java @@ -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 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 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 interfaces = interfaces(); + Iterator iter = interfaces.iterator(); + while (iter.hasNext()) { + InterfaceTypeImpl interfaze = (InterfaceTypeImpl) iter.next(); + if (interfaze.isAssignableTo(type)) { + return true; + } + } + return false; + } + } + + @Override + final void addVisibleMethods(Map methodMap, Set seenInterfaces) { + /* + * Add methods from + * parent types first, so that the methods in this class will + * overwrite them in the hash table + */ + Iterator 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 list) { + List immediate = interfaces(); + list.addAll(interfaces()); + Iterator 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 getAllInterfaces() { + List 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 allMethods() { + ArrayList 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 inheritedTypes() { + List 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 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); +} diff --git a/jdk/src/share/classes/com/sun/tools/jdi/MethodImpl.java b/jdk/src/share/classes/com/sun/tools/jdi/MethodImpl.java index f63ad68727d..1d93f4c76ba 100644 --- a/jdk/src/share/classes/com/sun/tools/jdi/MethodImpl.java +++ b/jdk/src/share/classes/com/sun/tools/jdi/MethodImpl.java @@ -187,6 +187,13 @@ public abstract class MethodImpl extends TypeComponentImpl 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); } diff --git a/jdk/src/share/classes/com/sun/tools/jdi/ObjectReferenceImpl.java b/jdk/src/share/classes/com/sun/tools/jdi/ObjectReferenceImpl.java index 2b1b0586b94..1d5a6c909eb 100644 --- a/jdk/src/share/classes/com/sun/tools/jdi/ObjectReferenceImpl.java +++ b/jdk/src/share/classes/com/sun/tools/jdi/ObjectReferenceImpl.java @@ -277,7 +277,6 @@ public class ObjectReferenceImpl extends ValueImpl 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 @@ public class ObjectReferenceImpl extends ValueImpl 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 @@ public class ObjectReferenceImpl extends ValueImpl * 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 @@ public class ObjectReferenceImpl extends ValueImpl */ 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 @@ public class ObjectReferenceImpl extends ValueImpl */ } + 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 @@ public class ObjectReferenceImpl extends ValueImpl 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 { diff --git a/jdk/src/share/classes/com/sun/tools/jdi/VirtualMachineManagerImpl.java b/jdk/src/share/classes/com/sun/tools/jdi/VirtualMachineManagerImpl.java index e41b295e017..3704b417b60 100644 --- a/jdk/src/share/classes/com/sun/tools/jdi/VirtualMachineManagerImpl.java +++ b/jdk/src/share/classes/com/sun/tools/jdi/VirtualMachineManagerImpl.java @@ -48,7 +48,7 @@ public class VirtualMachineManagerImpl implements VirtualMachineManagerService { 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; diff --git a/jdk/test/com/sun/jdi/EvalInterfaceStatic.sh b/jdk/test/com/sun/jdi/EvalInterfaceStatic.sh new file mode 100644 index 00000000000..92340b357a8 --- /dev/null +++ b/jdk/test/com/sun/jdi/EvalInterfaceStatic.sh @@ -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 < $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 diff --git a/jdk/test/com/sun/jdi/InterfaceMethodsTest.java b/jdk/test/com/sun/jdi/InterfaceMethodsTest.java new file mode 100644 index 00000000000..e127fa5e298 --- /dev/null +++ b/jdk/test/com/sun/jdi/InterfaceMethodsTest.java @@ -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); + } + } +} + + +