8042096: Backout JDK-8042091
Reviewed-by: sla
This commit is contained in:
parent
5b2e62685a
commit
0d7c3da5fe
@ -1147,8 +1147,7 @@ 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 static method in "
|
||||
"this class type or one of its superclasses.")
|
||||
(Error INVALID_METHODID "methodID is not the ID of a method.")
|
||||
(Error INVALID_THREAD)
|
||||
(Error THREAD_NOT_SUSPENDED)
|
||||
(Error VM_DEAD)
|
||||
@ -1251,83 +1250,6 @@ 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. "
|
||||
"<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
|
||||
@ -1621,7 +1543,7 @@ JDWP "Java(tm) Debug Wire Protocol"
|
||||
"<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. "
|
||||
"suspended by an event or by 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 "
|
||||
@ -1664,9 +1586,7 @@ 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 an instance method "
|
||||
"in this object's type or one of its superclasses, "
|
||||
"superinterfaces, or implemented interfaces.")
|
||||
(Error INVALID_METHODID "methodID is not the ID of a method.")
|
||||
(Error INVALID_THREAD)
|
||||
(Error THREAD_NOT_SUSPENDED)
|
||||
(Error VM_DEAD)
|
||||
|
@ -1,39 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
};
|
@ -1,25 +0,0 @@
|
||||
/*
|
||||
* 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[];
|
@ -36,7 +36,7 @@
|
||||
|
||||
static char *versionName = "Java Debug Wire Protocol (Reference Implementation)";
|
||||
static int majorVersion = 1; /* JDWP major version */
|
||||
static int minorVersion = 8; /* JDWP minor version */
|
||||
static int minorVersion = 6; /* JDWP minor version */
|
||||
|
||||
static jboolean
|
||||
version(PacketInputStream *in, PacketOutputStream *out)
|
||||
|
@ -29,7 +29,6 @@
|
||||
#include "VirtualMachineImpl.h"
|
||||
#include "ReferenceTypeImpl.h"
|
||||
#include "ClassTypeImpl.h"
|
||||
#include "InterfaceTypeImpl.h"
|
||||
#include "ArrayTypeImpl.h"
|
||||
#include "FieldImpl.h"
|
||||
#include "MethodImpl.h"
|
||||
@ -68,7 +67,6 @@ 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;
|
||||
|
@ -591,8 +591,6 @@ 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 {
|
||||
|
@ -103,7 +103,7 @@ public interface ClassType extends ReferenceType {
|
||||
* <p>
|
||||
* Object values must be assignment compatible with the field type
|
||||
* (This implies that the field type must be loaded through the
|
||||
* enclosing class' class loader). Primitive values must be
|
||||
* enclosing class's 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 {
|
||||
* <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
|
||||
* 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.
|
||||
* 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 arguments for the method, or
|
||||
* does not match the number of declared arguemnts 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' class loader.
|
||||
* 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
|
||||
@ -267,7 +267,7 @@ public interface ClassType extends ReferenceType {
|
||||
* <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
|
||||
* 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.
|
||||
* 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' class loader.
|
||||
* 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
|
||||
|
@ -79,121 +79,4 @@ public interface InterfaceType extends ReferenceType {
|
||||
* 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;
|
||||
}
|
||||
|
@ -137,16 +137,6 @@ public interface Method extends TypeComponent, Locatable, Comparable<Method> {
|
||||
*/
|
||||
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.
|
||||
*
|
||||
|
@ -194,10 +194,10 @@ public interface ObjectReference extends Value {
|
||||
* {@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 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.
|
||||
* 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.
|
||||
* <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 @@ 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 arguments for the method,
|
||||
* does not match the number of declared arguemnts 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 a non-default interface member.
|
||||
* either abstract or an interface member.
|
||||
* @throws {@link InvalidTypeException} if any argument in the
|
||||
* argument list is not assignable to the corresponding method argument
|
||||
* type.
|
||||
|
@ -559,9 +559,6 @@ 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());
|
||||
|
@ -29,27 +29,9 @@ import com.sun.jdi.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
final public class ClassTypeImpl extends InvokableTypeImpl
|
||||
public class ClassTypeImpl extends ReferenceTypeImpl
|
||||
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;
|
||||
@ -83,7 +65,6 @@ final public class ClassTypeImpl extends InvokableTypeImpl
|
||||
return superclass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<InterfaceType> interfaces() {
|
||||
if (interfaces == null) {
|
||||
interfaces = getInterfaces();
|
||||
@ -91,9 +72,26 @@ final public class ClassTypeImpl extends InvokableTypeImpl
|
||||
return interfaces;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<InterfaceType> allInterfaces() {
|
||||
return getAllInterfaces();
|
||||
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;
|
||||
}
|
||||
|
||||
public List<ClassType> subclasses() {
|
||||
@ -161,6 +159,28 @@ final public class ClassTypeImpl extends InvokableTypeImpl
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
@ -183,6 +203,52 @@ final public class ClassTypeImpl extends InvokableTypeImpl
|
||||
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,
|
||||
@ -245,6 +311,58 @@ final public class ClassTypeImpl extends InvokableTypeImpl
|
||||
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 {
|
||||
@ -264,33 +382,51 @@ final public class ClassTypeImpl extends InvokableTypeImpl
|
||||
}
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
|
@ -29,31 +29,14 @@ 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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
public class InterfaceTypeImpl extends ReferenceTypeImpl
|
||||
implements InterfaceType {
|
||||
|
||||
private SoftReference<List<InterfaceType>> superinterfacesRef = null;
|
||||
|
||||
@ -98,6 +81,102 @@ final public class InterfaceTypeImpl extends InvokableTypeImpl
|
||||
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();
|
||||
}
|
||||
@ -105,39 +184,4 @@ final public class InterfaceTypeImpl extends InvokableTypeImpl
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,305 +0,0 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
@ -187,13 +187,6 @@ 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);
|
||||
}
|
||||
|
@ -277,6 +277,7 @@ 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
|
||||
@ -286,19 +287,6 @@ 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);
|
||||
|
||||
/*
|
||||
@ -312,7 +300,9 @@ public class ObjectReferenceImpl extends ValueImpl
|
||||
* For nonvirtual invokes, method must have a body
|
||||
*/
|
||||
if ((options & INVOKE_NONVIRTUAL) != 0) {
|
||||
if (method.isAbstract()) {
|
||||
if (method.declaringType() instanceof InterfaceType) {
|
||||
throw new IllegalArgumentException("Interface method");
|
||||
} else if (method.isAbstract()) {
|
||||
throw new IllegalArgumentException("Abstract method");
|
||||
}
|
||||
}
|
||||
@ -334,7 +324,7 @@ public class ObjectReferenceImpl extends ValueImpl
|
||||
*/
|
||||
Method invoker = clazz.concreteMethodByName(method.name(),
|
||||
method.signature());
|
||||
// invoker is supposed to be non-null under normal circumstances
|
||||
// isAssignableFrom check above guarantees non-null
|
||||
invokedClass = (ClassTypeImpl)invoker.declaringType();
|
||||
}
|
||||
/* The above code is left over from previous versions.
|
||||
@ -342,17 +332,6 @@ 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,
|
||||
@ -391,10 +370,7 @@ public class ObjectReferenceImpl extends ValueImpl
|
||||
ThreadReferenceImpl thread = (ThreadReferenceImpl)threadIntf;
|
||||
|
||||
if (method.isStatic()) {
|
||||
if (referenceType() instanceof InterfaceType) {
|
||||
InterfaceType type = (InterfaceType)referenceType();
|
||||
return type.invokeMethod(thread, method, origArguments, options);
|
||||
} else if (referenceType() instanceof ClassType) {
|
||||
if (referenceType() instanceof ClassType) {
|
||||
ClassType type = (ClassType)referenceType();
|
||||
return type.invokeMethod(thread, method, origArguments, options);
|
||||
} else {
|
||||
|
@ -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 = 8;
|
||||
private static final int minorVersion = 6;
|
||||
|
||||
private static final Object lock = new Object();
|
||||
private static VirtualMachineManagerImpl vmm;
|
||||
|
@ -1,126 +0,0 @@
|
||||
#!/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
|
@ -1,422 +0,0 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user