/*
* Copyright (c) 2007, 2011, 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.
*/
package com.sun.max.jdwp.handlers;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.logging.Logger;
import com.sun.max.jdwp.constants.Error;
import com.sun.max.jdwp.constants.SuspendStatus;
import com.sun.max.jdwp.constants.Tag;
import com.sun.max.jdwp.constants.ThreadStatus;
import com.sun.max.jdwp.constants.TypeTag;
import com.sun.max.jdwp.data.ID;
import com.sun.max.jdwp.data.ID.ArrayID;
import com.sun.max.jdwp.data.ID.ArrayTypeID;
import com.sun.max.jdwp.data.ID.ClassID;
import com.sun.max.jdwp.data.ID.ClassLoaderID;
import com.sun.max.jdwp.data.ID.ClassObjectID;
import com.sun.max.jdwp.data.ID.FieldID;
import com.sun.max.jdwp.data.ID.FrameID;
import com.sun.max.jdwp.data.ID.InterfaceID;
import com.sun.max.jdwp.data.ID.MethodID;
import com.sun.max.jdwp.data.ID.ObjectID;
import com.sun.max.jdwp.data.ID.ReferenceTypeID;
import com.sun.max.jdwp.data.ID.StringID;
import com.sun.max.jdwp.data.ID.ThreadGroupID;
import com.sun.max.jdwp.data.ID.ThreadID;
import com.sun.max.jdwp.data.JDWPException;
import com.sun.max.jdwp.data.JDWPLocation;
import com.sun.max.jdwp.data.JDWPNotImplementedException;
import com.sun.max.jdwp.data.JDWPValue;
import com.sun.max.jdwp.vm.core.Provider;
import com.sun.max.jdwp.vm.proxy.ArrayProvider;
import com.sun.max.jdwp.vm.proxy.ArrayTypeProvider;
import com.sun.max.jdwp.vm.proxy.ClassLoaderProvider;
import com.sun.max.jdwp.vm.proxy.ClassObjectProvider;
import com.sun.max.jdwp.vm.proxy.ClassProvider;
import com.sun.max.jdwp.vm.proxy.FieldProvider;
import com.sun.max.jdwp.vm.proxy.FrameProvider;
import com.sun.max.jdwp.vm.proxy.InterfaceProvider;
import com.sun.max.jdwp.vm.proxy.JdwpCodeLocation;
import com.sun.max.jdwp.vm.proxy.MethodProvider;
import com.sun.max.jdwp.vm.proxy.ObjectProvider;
import com.sun.max.jdwp.vm.proxy.ReferenceTypeProvider;
import com.sun.max.jdwp.vm.proxy.StringProvider;
import com.sun.max.jdwp.vm.proxy.ThreadGroupProvider;
import com.sun.max.jdwp.vm.proxy.ThreadProvider;
import com.sun.max.jdwp.vm.proxy.VMAccess;
import com.sun.max.jdwp.vm.proxy.VMValue;
/**
* This class is respondible for handling a JDWP session in terms of managing the JDWP object space.
* It contains utility functions for looking up the identifier of an object or looking up an object based on the identifier.
*
*/
public class JDWPSession {
private static final Logger LOGGER = Logger.getLogger(Handlers.class.getName());
private VMAccess vm;
private Map<ID, Provider> idToProvider;
private Map<Provider, ID> providerToID;
private Map<MethodProvider, ReferenceTypeProvider> methodToReferenceType;
private Map<FieldProvider, ReferenceTypeProvider> fieldToReferenceType;
private Map<FrameProvider, ThreadProvider> frameToThread;
private long lastID;
public JDWPSession(VMAccess vm) {
assert vm != null : "Virtual machine abstraction must not be null";
this.vm = vm;
idToProvider = new HashMap<ID, Provider>();
providerToID = new IdentityHashMap<Provider, ID>();
methodToReferenceType = new IdentityHashMap<MethodProvider, ReferenceTypeProvider>();
fieldToReferenceType = new IdentityHashMap<FieldProvider, ReferenceTypeProvider>();
frameToThread = new IdentityHashMap<FrameProvider, ThreadProvider>();
}
public static int getValueTypeTag(VMValue.Type type) {
switch (type) {
case BOOLEAN:
return Tag.BOOLEAN;
case BYTE:
return Tag.BYTE;
case CHAR:
return Tag.CHAR;
case DOUBLE:
return Tag.DOUBLE;
case FLOAT:
return Tag.FLOAT;
case INT:
return Tag.INT;
case LONG:
return Tag.LONG;
case PROVIDER:
return Tag.OBJECT;
case SHORT:
return Tag.SHORT;
case VOID:
return Tag.VOID;
}
throw new IllegalArgumentException("Unknown virtual machine type: " + type);
}
public VMAccess vm() {
return vm;
}
/**
* All events should be hold back until {@link releaseEvents()} is called.
* @throws JDWPException
*/
public void holdEvents() throws JDWPException {
// TODO: Add implementation.
throw new JDWPNotImplementedException();
}
/**
* All events that were hold back because of a call to {@link holdEvents()} should be set free.
* @throws JDWPException
*/
public void releaseEvents() throws JDWPException {
// TODO: Add implementation.
throw new JDWPNotImplementedException();
}
/**
* Looks up an ID object of a Provider object or creates a new ID object if none is found.
* @param provider the provider for which the ID is looked up
* @param idKlass the class of the ID object
* @return the ID object representing the given Provider object
*/
private <Provider_Type extends Provider, ID_Type extends ID> ID_Type makeID(Provider_Type provider, Class<ID_Type> idKlass) {
if (provider == null) {
return ID.create(0, idKlass);
}
if (!providerToID.containsKey(provider)) {
lastID++;
final ID_Type newID = ID.create(lastID, idKlass);
idToProvider.put(newID, provider);
providerToID.put(provider, newID);
assert providerToID.containsKey(provider);
assert idToProvider.containsKey(newID);
assert idToProvider.containsKey(ID.convert(newID, ID.class));
LOGGER.fine("Created new ID " + lastID + " for provider " + provider);
return newID;
}
final ID result = providerToID.get(provider);
// TODO: Make a better check-system!
/*if (!idKlass.isAssignableFrom(result.getClass())) {
throw new IllegalArgumentException("The ID " + result + " found for provider " + provider + " is not assignable to type " + idKlass.getName() + " but of type " + result.getClass());
}*/
return ID.convert(result, idKlass);
//return StaticLoophole.cast(idKlass, result);
}
/**
* Looks up a Provider object based on the ID and throws an exception if no Provider object is found.
* @param errorCode the error code of the exception that should be thrown in case no Provider object is found
* @param klass the klass of the returned Provider object
* @param id identifier of the object to look for
* @return the object that has the given identifier
* @throws JDWPException this exception is thrown, when no provider object was found
*/
@SuppressWarnings("unchecked")
private <Provider_Type extends Provider, ID_Type extends ID> Provider_Type lookup(int errorCode, Class<Provider_Type> klass, ID_Type id) throws JDWPException {
if (id.value() == 0) {
return null;
}
if (!idToProvider.containsKey(id)) {
throw new JDWPException((short) Error.INVALID_OBJECT, "The id " + id + " is unknown!");
}
final Provider result = idToProvider.get(id);
// TODO: Check if there can be a better assertion.
// if (klass.isAssignableFrom(result.getClass())) {
// throw new JDWPException(errorCode, "The object found at id " + id + " is not a valid instance of " +
// klass.getName() + " but: " + result);
// }
return (Provider_Type) result;
}
public ClassObjectProvider getClassObject(ClassObjectID id) throws JDWPException {
return lookup(Error.INVALID_OBJECT, ClassObjectProvider.class, id);
}
public ArrayProvider getArray(ArrayID id) throws JDWPException {
return lookup(Error.INVALID_ARRAY, ArrayProvider.class, id);
}
public ArrayTypeProvider getArrayType(ArrayTypeID id) throws JDWPException {
return lookup(Error.INVALID_CLASS, ArrayTypeProvider.class, id);
}
public ClassLoaderProvider getClassLoader(ClassLoaderID id) throws JDWPException {
return lookup(Error.INVALID_CLASS_LOADER, ClassLoaderProvider.class, id);
}
public ClassProvider getClass(ClassID id) throws JDWPException {
return lookup(Error.INVALID_CLASS, ClassProvider.class, id);
}
/**
* Checks whether the field really belongs to the given reference type.
* @param refType
* @param f
* @throws JDWPException
*/
private void checkField(ReferenceTypeProvider referenceTypeProvider, FieldProvider fieldProvider) throws JDWPException {
if (!this.fieldToReferenceType.containsKey(fieldProvider)) {
fieldToReferenceType.put(fieldProvider, referenceTypeProvider);
}
// TODO: Check subclass / class relationship.
// if (_fieldToReferenceType.get(fieldProvider) != referenceTypeProvider) {
// throw new JDWPException("Field has wrong parent reference type!");
// }
}
private void checkMethod(ReferenceTypeProvider refType, MethodProvider m) throws JDWPException {
if (!this.methodToReferenceType.containsKey(m)) {
methodToReferenceType.put(m, refType);
}
// TODO: Check subclass / class relationship.
// if (_methodToReferenceType.get(m) != refType) {
// throw new JDWPException("Method has wrong parent reference type! Cached type: " +
// _methodToReferenceType.get(m) + ", actual type: " + refType);
// }
}
private void checkFrame(ThreadProvider thread, FrameProvider frameProvider) throws JDWPException {
if (!this.frameToThread.containsKey(frameProvider)) {
frameToThread.put(frameProvider, thread);
}
if (this.frameToThread.get(frameProvider) != thread) {
throw new JDWPException("Frame has wrong parent thread!");
}
}
public ThreadProvider frameToThread(FrameProvider f) {
if (f == null) {
return null;
}
return f.getThread();
}
public ReferenceTypeProvider methodToReferenceType(MethodProvider m) {
if (m == null) {
return null;
}
return m.getReferenceTypeHolder(); // _methodToReferenceType.get(m);
}
public ReferenceTypeProvider fieldToReferenceType(FieldProvider f) {
if (f == null) {
return null;
}
return f.getReferenceTypeHolder(); // _fieldToReferenceType.get(f);
}
public FieldProvider getField(ReferenceTypeID id, FieldID fid) throws JDWPException {
final ReferenceTypeProvider refType = getReferenceType(id);
final FieldProvider f = lookup(Error.INVALID_FIELDID, FieldProvider.class, fid);
checkField(refType, f);
return f;
}
public ThreadProvider getThread(ThreadID id) throws JDWPException {
return lookup(Error.INVALID_THREAD, ThreadProvider.class, id);
}
public MethodProvider getMethod(ReferenceTypeID id, MethodID mid) throws JDWPException {
final ReferenceTypeProvider refType = getReferenceType(id);
final MethodProvider m = lookup(Error.INVALID_METHODID, MethodProvider.class, mid);
checkMethod(refType, m);
return m;
}
public ObjectProvider getObject(ObjectID id) throws JDWPException {
return lookup(Error.INVALID_OBJECT, ObjectProvider.class, id);
}
public FrameProvider getFrame(ThreadID id, FrameID fid) throws JDWPException {
final ThreadProvider thread = getThread(id);
final FrameProvider f = lookup(Error.INVALID_FRAMEID, FrameProvider.class, fid);
checkFrame(thread, f);
return f;
}
public StringProvider getString(StringID id) throws JDWPException {
return lookup(Error.INVALID_STRING, StringProvider.class, id);
}
public ThreadGroupProvider getThreadGroup(ThreadGroupID id) throws JDWPException {
return lookup(Error.INVALID_THREAD_GROUP, ThreadGroupProvider.class, id);
}
public ReferenceTypeProvider getReferenceType(ReferenceTypeID id) throws JDWPException {
return lookup(Error.INVALID_CLASS, ReferenceTypeProvider.class, id);
}
public ID.ArrayID toID(ArrayProvider p) {
return makeID(p, ID.ArrayID.class);
}
public ID.FieldID toID(FieldProvider p) {
return makeID(p, ID.FieldID.class);
}
public ID.FrameID toID(FrameProvider p) {
return makeID(p, ID.FrameID.class);
}
public InterfaceID toID(InterfaceProvider p) {
return makeID(p, ID.InterfaceID.class);
}
public ReferenceTypeID toID(ReferenceTypeProvider p) {
if (p == null) {
return ID.ReferenceTypeID.create(0, ID.ReferenceTypeID.class);
} else if (p instanceof ClassProvider) {
return makeID(p, ID.ClassID.class);
} else if (p instanceof ArrayProvider) {
return makeID(p, ID.ArrayTypeID.class);
} else if (p instanceof InterfaceProvider) {
return makeID(p, ID.InterfaceID.class);
} else {
assert false : "Unknown subclass of ReferenceTypeProvider " + p + "!";
return null;
}
}
public ClassID toID(ClassProvider p) {
return makeID(p, ID.ClassID.class);
}
public ObjectID toID(ObjectProvider p) {
return makeID(p, ID.ObjectID.class);
}
public ThreadGroupID toID(ThreadGroupProvider p) {
return makeID(p, ID.ThreadGroupID.class);
}
public StringID toID(StringProvider p) {
return makeID(p, ID.StringID.class);
}
public ThreadID toID(ThreadProvider p) {
return makeID(p, ID.ThreadID.class);
}
public ClassObjectID toID(ClassObjectProvider p) {
return makeID(p, ID.ClassObjectID.class);
}
public ClassLoaderID toID(ClassLoaderProvider p) {
return makeID(p, ID.ClassLoaderID.class);
}
public FieldID toID(ReferenceTypeProvider refType, FieldProvider p) throws JDWPException {
checkField(refType, p);
return makeID(p, ID.FieldID.class);
}
public MethodID toID(ReferenceTypeProvider refType, MethodProvider p) throws JDWPException {
checkMethod(refType, p);
return makeID(p, ID.MethodID.class);
}
public MethodID toID(MethodProvider p) {
return makeID(p, ID.MethodID.class);
}
public FrameID toID(ThreadProvider thread, FrameProvider p) throws JDWPException {
checkFrame(thread, p);
return makeID(p, ID.FrameID.class);
}
public JDWPLocation fromCodeLocation(JdwpCodeLocation location) {
final ReferenceTypeProvider refType = methodToReferenceType(location.method());
return new JDWPLocation(getTypeTag(refType), ID.convert(toID(refType), ID.ClassID.class), toID(location.method()), location.position());
}
/**
* Returns the JDWP type tag based on the class type of a ReferenceTypeProvider object.
* @param refType the reference type object whose JDWP type tag should be returned
* @return the JDWP type tag
*/
public byte getTypeTag(ReferenceTypeProvider refType) {
if (refType == null) {
return 0;
}
if (ArrayTypeProvider.class.isAssignableFrom(refType.getClass())) {
return TypeTag.ARRAY;
} else if (ClassProvider.class.isAssignableFrom(refType.getClass())) {
return TypeTag.CLASS;
} else if (InterfaceProvider.class.isAssignableFrom(refType.getClass())) {
return TypeTag.INTERFACE;
}
assert false : "Must not reach here, no other type of Provider.ReferenceType objects allowed! Type: " + refType.getClass();
return 0;
}
public JdwpCodeLocation toCodeLocation(JDWPLocation location) throws JDWPException {
final MethodProvider curMethod = getMethod(location.getClassID(), location.getMethodID());
final int curPosition = (int) location.getIndex();
return vm().createCodeLocation(curMethod, curPosition, false);
}
/**
* Creates a JDWPValue object based on a virtual machine value object.
* @param v the value that should be converted to a JDWPValue object
* @return the newly created JDWPValue object representing the given Maxine Value object
* @throws JDWPException this exception is thrown, when there occurred an error during the conversion
*/
public JDWPValue toJDWPValue(VMValue v) throws JDWPException {
if (v.isVoid()) {
return new JDWPValue();
} else if (v.asBoolean() != null) {
return new JDWPValue(v.asBoolean());
} else if (v.asByte() != null) {
return new JDWPValue(v.asByte());
} else if (v.asChar() != null) {
return new JDWPValue(v.asChar());
} else if (v.asDouble() != null) {
return new JDWPValue(v.asDouble());
} else if (v.asFloat() != null) {
return new JDWPValue(v.asFloat());
} else if (v.asInt() != null) {
return new JDWPValue(v.asInt());
} else if (v.asLong() != null) {
return new JDWPValue(v.asLong());
} else if (v.asShort() != null) {
return new JDWPValue(v.asShort());
} else {
final Provider p = v.asProvider();
if (p == null) {
return new JDWPValue(ID.create(0, ObjectID.class));
}
if (p instanceof FieldProvider) {
return new JDWPValue(toID((FieldProvider) p));
} else if (p instanceof FrameProvider) {
throw new IllegalArgumentException("Cannot convert FrameProvider " + p + " + to JDWP value!");
} else if (p instanceof StringProvider) {
return new JDWPValue(toID((StringProvider) p));
} else if (p instanceof ArrayProvider) {
return new JDWPValue(toID((ArrayProvider) p));
} else if (p instanceof ObjectProvider) {
return new JDWPValue(toID((ObjectProvider) p));
}
}
throw new IllegalArgumentException("Could not convert value " + v + " to a JDWP value!");
}
/**
* Creates a Maxine Value object based on the given JDWPValue object.
* @param value the value to be converted to a Maxine Value object
* @return a newly created Maxine Value object representing the given parameter
* @throws JDWPException this exception is thrown when an error occurred during the conversion
*/
public VMValue toValue(JDWPValue value) throws JDWPException {
switch (value.tag()) {
case Tag.BOOLEAN:
return vm().createBooleanValue(value.asBoolean());
case Tag.BYTE:
return vm().createByteValue(value.asByte());
case Tag.CHAR:
return vm().createCharValue(value.asCharacter());
case Tag.DOUBLE:
return vm().createDoubleValue(value.asByte());
case Tag.FLOAT:
return vm().createFloatValue(value.asFloat());
case Tag.INT:
return vm().createIntValue(value.asInteger());
case Tag.LONG:
return vm().createLongValue(value.asLong());
case Tag.SHORT:
return vm().createShortValue(value.asShort());
case Tag.ARRAY:
return vm().createObjectProviderValue(this.getArray(value.asArray()));
case Tag.CLASS_LOADER:
return vm().createObjectProviderValue(this.getClassLoader(value.asClassLoader()));
case Tag.CLASS_OBJECT:
return vm().createObjectProviderValue(this.getClassObject(value.asClassObject()));
case Tag.OBJECT:
return vm().createObjectProviderValue(this.getObject(value.asObject()));
case Tag.STRING:
return vm().createObjectProviderValue(this.getString(value.asString()));
case Tag.THREAD:
return vm().createObjectProviderValue(this.getThread(value.asThread()));
case Tag.THREAD_GROUP:
return vm().createObjectProviderValue(this.getThreadGroup(value.asThreadGroup()));
case Tag.VOID:
return vm().getVoidValue();
}
throw new IllegalArgumentException("Argument " + value + " could not be converted into a VirtualMachineValue object!");
}
/**
* Retrieves the status of a thread.
* @param thread the thread whose status should be queried
* @return the status of the given thread
*/
public int getThreadStatus(ThreadProvider thread) {
// TODO: Correct implementation
return ThreadStatus.RUNNING;
}
/**
* Retrieves the suspend status of a thread.
* @param thread the thread whose suspend status should be queried
* @return the suspend status of the given thread
*/
public int getSuspendStatus(ThreadProvider thread) {
// TODO: Correct implementation
return SuspendStatus.SUSPEND_STATUS_SUSPENDED;
}
}