/*
* Copyright (c) 2009, 2012, 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.ins.object;
import java.lang.reflect.*;
import java.util.*;
import com.sun.max.ins.*;
import com.sun.max.ins.gui.*;
import com.sun.max.ins.util.*;
import com.sun.max.ins.view.*;
import com.sun.max.ins.view.InspectionViews.ViewKind;
import com.sun.max.lang.*;
import com.sun.max.program.*;
import com.sun.max.tele.*;
import com.sun.max.tele.object.*;
import com.sun.max.unsafe.*;
import com.sun.max.vm.actor.holder.*;
/**
* Creates and manages canonical instances of {@link ObjectView} for
* objects in the heap of the VM.
* <p>
* This view manager does not have a public face for creating object views. Rather,
* the manager listens for the user to set focus on a particular object, and which point
* an {@link ObjectView} is created (or merely highlighted if it already exists).
*/
public final class ObjectViewManager extends AbstractMultiViewManager<ObjectView> implements ObjectViewFactory {
private static final ViewKind VIEW_KIND = ViewKind.OBJECT;
private static final String SHORT_NAME = "Object";
private static final String LONG_NAME = "Object View";
/**
* Map: {@link MaxObject} -- > the {@link ObjectView}, if it exists, for the corresponding
* object in the VM. Relies on {@link ObjectView}s being canonical.
*/
private final Map<MaxObject, ObjectView> objectToView = new HashMap<MaxObject, ObjectView>();
/**
* Object view constructors for specific tuple-implemented subclasses of {@link MaxObject}s.
* The most specific class that matches a particular {@link MaxObject} will
* be used, in an emulation of virtual method dispatch.
*/
private final Map<Class, Constructor> teleTupleObjectClassToObjectViewConstructor = new HashMap<Class, Constructor>();
/**
* Object view constructors for specific array-implemented subclasses of {@link MaxObject}s.
* The most specific class that matches a particular array component type will
* be used, in an emulation of virtual method dispatch.
*/
private final Map<Class, Constructor> arrayComponentClassToObjectViewConstructor = new HashMap<Class, Constructor>();
private final Constructor defaultArrayViewConstructor;
private final Constructor defaultTupleViewConstructor;
private final InspectorAction interactiveMakeViewByAddressAction;
private final InspectorAction interactiveMakeViewByIDAction;
private final InspectorAction closeForwarderViewsAction;
private final InspectorAction closeFreeSpaceViewsAction;
private final InspectorAction closeDarkMatterViewsAction;
private final InspectorAction closeDeadViewsAction;
private final List<InspectorAction> makeViewActions;
private final List<InspectorAction> closeViewActions;
ObjectViewManager(final Inspection inspection) {
super(inspection, VIEW_KIND, SHORT_NAME, LONG_NAME);
Trace.begin(1, tracePrefix() + "initializing");
// Use this if there is no subclass of array component type is matched, or if the component type is an interface.
defaultArrayViewConstructor = getConstructor(ArrayView.class);
// Array views for specific subclasses of component type
//arrayComponentClassToObjectViewConstructor.put(<some component class>, getConstructor(<some ArrayView class>));
// Use this if there is no object type subclass matched
defaultTupleViewConstructor = getConstructor(TupleView.class);
// Tuple views for specific subclasses
teleTupleObjectClassToObjectViewConstructor.put(TeleHeapFreeChunk.class, getConstructor(HeapFreeChunkTupleView.class));
teleTupleObjectClassToObjectViewConstructor.put(TeleHeapRegionInfo.class, getConstructor(HeapRegionInfoView.class));
focus().addListener(new InspectionFocusAdapter() {
@Override
public void heapObjectFocusChanged(MaxObject oldObject, MaxObject newObject) {
if (newObject != null) {
ObjectViewManager.this.makeObjectView(inspection, newObject);
}
}
});
interactiveMakeViewByAddressAction = new InteractiveViewObjectByAddressAction();
interactiveMakeViewByIDAction = new InteractiveViewObjectByIDAction();
closeForwarderViewsAction = new CloseViewsByStatusAction(ObjectStatus.FORWARDER);
closeFreeSpaceViewsAction = new CloseViewsByStatusAction(ObjectStatus.FREE);
closeDarkMatterViewsAction = new CloseViewsByStatusAction(ObjectStatus.DARK);
closeDeadViewsAction = new CloseViewsByStatusAction(ObjectStatus.DEAD);
makeViewActions = new ArrayList<InspectorAction>(2);
makeViewActions.add(interactiveMakeViewByAddressAction);
makeViewActions.add(interactiveMakeViewByIDAction);
closeViewActions = new ArrayList<InspectorAction>(1);
closeViewActions.add(closeDeadViewsAction);
closeViewActions.add(closeForwarderViewsAction);
closeViewActions.add(closeFreeSpaceViewsAction);
closeViewActions.add(closeDarkMatterViewsAction);
Trace.end(1, tracePrefix() + "initializing");
}
public ObjectView makeView(MaxObject object) {
focus().setHeapObject(object);
return objectToView.get(object);
}
public InspectorAction makeViewByAddressAction() {
return interactiveMakeViewByAddressAction;
}
public InspectorAction makeViewByIDAction() {
return interactiveMakeViewByIDAction;
}
public InspectorAction makeCloseDeadViewsAction() {
return closeDeadViewsAction;
}
public InspectorAction makeViewAction(MaxObject object, String actionTitle) {
return new ViewSpecifiedObjectAction(object, actionTitle);
}
@Override
public void vmStateChanged(boolean force) {
refreshActions();
}
@Override
public void vmProcessTerminated() {
for (ObjectView objectView : new ArrayList<ObjectView>(objectViews())) {
objectView.dispose();
}
}
@Override
protected List<InspectorAction> makeViewActions() {
return makeViewActions;
}
@Override
protected List<InspectorAction> closeViewActions() {
return closeViewActions;
}
private ObjectView makeObjectView(Inspection inspection, MaxObject object) {
ObjectView objectView = objectToView.get(object);
if (objectView == null) {
switch (object.kind()) {
case HYBRID: {
objectView = new HubView(inspection, object);
break;
}
case TUPLE: {
Constructor constructor = lookupViewConstructor(teleTupleObjectClassToObjectViewConstructor, object.getClass());
if (constructor == null) {
constructor = defaultTupleViewConstructor;
}
try {
objectView = (ObjectView) constructor.newInstance(inspection, object);
} catch (InstantiationException e) {
throw InspectorError.unexpected(e);
} catch (IllegalAccessException e) {
throw InspectorError.unexpected(e);
} catch (InvocationTargetException e) {
throw InspectorError.unexpected(e.getTargetException());
}
break;
}
case ARRAY: {
ClassActor componentClassActor = object.classActorForObjectType().componentClassActor();
if (componentClassActor.isPrimitiveClassActor()) {
final PrimitiveClassActor primitiveClassActor = (PrimitiveClassActor) componentClassActor;
componentClassActor = primitiveClassActor.toWrapperClassActor();
}
Constructor constructor = lookupViewConstructor(arrayComponentClassToObjectViewConstructor, componentClassActor.toJava());
if (constructor == null) {
constructor = defaultArrayViewConstructor;
}
try {
objectView = (ObjectView) constructor.newInstance(inspection, object);
} catch (InstantiationException e) {
throw InspectorError.unexpected();
} catch (IllegalAccessException e) {
throw InspectorError.unexpected();
} catch (InvocationTargetException e) {
e.printStackTrace();
throw InspectorError.unexpected();
}
break;
}
}
if (objectView != null) {
//objectToView.put(object, objectView);
addView(object, objectView);
objectView.addViewEventListener(new ViewEventListener() {
@Override
public void viewClosing(InspectorView view) {
final ObjectView objectView = (ObjectView) view;
removeView(objectView.object(), objectView);
//assert objectToView.remove(objectView.object()) != null;
}
});
super.notifyAddingView(objectView);
}
}
if (objectView != null) {
objectView.highlight();
}
return objectView;
}
private Constructor getConstructor(Class clazz) {
return Classes.getDeclaredConstructor(clazz, Inspection.class, MaxObject.class);
}
private Constructor lookupViewConstructor(Map<Class, Constructor> map, Class clazz) {
Class javaClass = clazz;
while (javaClass != null) {
final Constructor constructor = map.get(javaClass);
if (constructor != null) {
return constructor;
}
javaClass = javaClass.getSuperclass();
}
return null;
}
public boolean isObjectViewObservingObject(long oid) {
for (MaxObject object : objectToView.keySet()) {
if (object.reference().makeOID() == oid) {
return true;
}
}
return false;
}
public void resetObjectToViewMapEntry(MaxObject oldObject, MaxObject newObject, ObjectView objectView) {
removeView(oldObject, objectView);
addView(newObject, objectView);
}
private void addView(MaxObject object, ObjectView view) {
final ObjectView collision = objectToView.put(object, view);
assert collision == null;
refreshActions();
}
private void removeView(MaxObject object, ObjectView view) {
final ObjectView mappedView = objectToView.remove(object);
if (view != null) {
assert mappedView == view;
}
refreshActions();
}
private void refreshActions() {
for (InspectorAction action : makeViewActions) {
action.refresh(true);
}
for (InspectorAction action : closeViewActions) {
action.refresh(true);
}
}
/**
* @return all existing instances of {@link ObjectView}, even if hidden or iconic.
*/
private Set<ObjectView> objectViews() {
return new HashSet<ObjectView>(objectToView.values());
}
private final class InteractiveViewObjectByAddressAction extends InspectorAction {
InteractiveViewObjectByAddressAction() {
super(inspection(), "View object at address...");
}
@Override
protected void procedure() {
new AddressInputDialog(inspection(), vm().heap().bootHeapRegion().memoryRegion().start(), "View object at address...", "View") {
@Override
public void entered(Address address) {
try {
final MaxObject object = vm().objects().findAnyObjectAt(address);
if (object != null) {
focus().setHeapObject(object);
} else {
gui().errorMessage("object not found at " + address.to0xHexString());
}
} catch (MaxVMBusyException e) {
gui().errorMessage("VM Busy");
}
}
};
}
}
private final class InteractiveViewObjectByIDAction extends InspectorAction {
InteractiveViewObjectByIDAction() {
super(inspection(), "View object by ID...");
}
@Override
protected void procedure() {
final String input = gui().inputDialog("View object by ID..", "");
if (input == null) {
// User clicked cancel.
return;
}
try {
final long oid = Long.parseLong(input);
final MaxObject object = vm().objects().findObjectByOID(oid);
if (object != null) {
focus().setHeapObject(object);
} else {
gui().errorMessage("failed to find heap object for ID: " + input);
}
} catch (NumberFormatException numberFormatException) {
gui().errorMessage("Not an object ID: " + input);
}
}
}
private final class CloseViewsByStatusAction extends InspectorAction {
final ObjectStatus status;
CloseViewsByStatusAction(ObjectStatus status) {
super(inspection(), "Close unpinned " + status.label() + " object views");
this.status = status;
}
@Override
protected void procedure() {
for (ObjectView view : new ArrayList<ObjectView>(objectToView.values())) {
if (view.object().status() == status && !view.isPinned()) {
view.dispose();
}
}
}
@Override
public
void refresh(boolean force) {
for (ObjectView view : objectToView.values()) {
if (view.object().status() == status) {
setEnabled(true);
return;
}
}
setEnabled(false);
}
}
/**
* Action: creates a view for a specific heap object in the VM.
*/
private final class ViewSpecifiedObjectAction extends InspectorAction {
private static final String DEFAULT_TITLE = "View object";
final MaxObject object;
ViewSpecifiedObjectAction(MaxObject object, String actionTitle) {
super(inspection(), actionTitle == null ? DEFAULT_TITLE : actionTitle);
this.object = object;
}
@Override
protected void procedure() {
focus().setHeapObject(object);
}
}
}