blob: 8644957e0ff31e71a9ef13625833510f516d70e2 [file] [log] [blame]
/*
* Copyright (c) 1998, 2008, 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.*;
import java.util.*;
public class ClassTypeImpl extends ReferenceTypeImpl
implements ClassType
{
private boolean cachedSuperclass = false;
private ClassType superclass = null;
private int lastLine = -1;
private List<InterfaceType> interfaces = null;
protected ClassTypeImpl(VirtualMachine aVm,long aRef) {
super(aVm, aRef);
}
public ClassType superclass() {
if(!cachedSuperclass) {
ClassTypeImpl sup = null;
try {
sup = JDWP.ClassType.Superclass.
process(vm, this).superclass;
} catch (JDWPException exc) {
throw exc.toJDIException();
}
/*
* If there is a superclass, cache its
* ClassType here. Otherwise,
* leave the cache reference null.
*/
if (sup != null) {
superclass = sup;
}
cachedSuperclass = true;
}
return superclass;
}
public List<InterfaceType> interfaces() {
if (interfaces == null) {
interfaces = getInterfaces();
}
return interfaces;
}
void addInterfaces(List<InterfaceType> list) {
List<InterfaceType> 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<InterfaceType> allInterfaces() {
List<InterfaceType> all = new ArrayList<InterfaceType>();
addInterfaces(all);
return all;
}
public List<ClassType> subclasses() {
List<ClassType> subs = new ArrayList<ClassType>();
for (ReferenceType refType : vm.allClasses()) {
if (refType instanceof ClassType) {
ClassType clazz = (ClassType)refType;
ClassType superclass = clazz.superclass();
if ((superclass != null) && superclass.equals(this)) {
subs.add((ClassType)refType);
}
}
}
return subs;
}
public boolean isEnum() {
ClassType superclass = superclass();
if (superclass != null &&
superclass.name().equals("java.lang.Enum")) {
return true;
}
return false;
}
public void setValue(Field field, Value value)
throws InvalidTypeException, ClassNotLoadedException {
validateMirror(field);
validateMirrorOrNull(value);
validateFieldSet(field);
// More validation specific to setting from a ClassType
if(!field.isStatic()) {
throw new IllegalArgumentException(
"Must set non-static field through an instance");
}
try {
JDWP.ClassType.SetValues.FieldValue[] values =
new JDWP.ClassType.SetValues.FieldValue[1];
values[0] = new JDWP.ClassType.SetValues.FieldValue(
((FieldImpl)field).ref(),
// validate and convert if necessary
ValueImpl.prepareForAssignment(value, (FieldImpl)field));
try {
JDWP.ClassType.SetValues.process(vm, this, values);
} catch (JDWPException exc) {
throw exc.toJDIException();
}
} catch (ClassNotLoadedException e) {
/*
* Since we got this exception,
* the field type must be a reference type. The value
* we're trying to set is null, but if the field's
* class has not yet been loaded through the enclosing
* class loader, then setting to null is essentially a
* no-op, and we should allow it without an exception.
*/
if (value != null) {
throw e;
}
}
}
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,
final int options) {
CommandSender sender =
new CommandSender() {
public PacketStream send() {
return JDWP.ClassType.NewInstance.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;
}
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,
int options)
throws InvalidTypeException,
ClassNotLoadedException,
IncompatibleThreadStateException,
InvocationException {
validateMirror(threadIntf);
validateMirror(methodIntf);
validateMirrorsOrNulls(origArguments);
MethodImpl method = (MethodImpl)methodIntf;
ThreadReferenceImpl thread = (ThreadReferenceImpl)threadIntf;
validateConstructorInvocation(method);
List<Value> arguments = method.validateAndPrepareArgumentsForInvoke(
origArguments);
ValueImpl[] args = arguments.toArray(new ValueImpl[0]);
JDWP.ClassType.NewInstance ret = null;
try {
PacketStream stream =
sendNewInstanceCommand(thread, method, args, options);
ret = JDWP.ClassType.NewInstance.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.newObject;
}
}
public Method concreteMethodByName(String name, String signature) {
Method method = null;
for (Method candidate : visibleMethods()) {
if (candidate.name().equals(name) &&
candidate.signature().equals(signature) &&
!candidate.isAbstract()) {
method = candidate;
break;
}
}
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 {
/*
* Method must be in this class.
*/
ReferenceTypeImpl declType = (ReferenceTypeImpl)method.declaringType();
if (!declType.equals(this)) {
throw new IllegalArgumentException("Invalid constructor");
}
/*
* Method must be a constructor
*/
if (!method.isConstructor()) {
throw new IllegalArgumentException("Cannot create instance with non-constructor");
}
}
void addVisibleMethods(Map<String, Method> methodMap) {
/*
* 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();
interfaze.addVisibleMethods(methodMap);
}
ClassTypeImpl clazz = (ClassTypeImpl)superclass();
if (clazz != null) {
clazz.addVisibleMethods(methodMap);
}
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 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() + ")";
}
}