/*
* Copyright (c) 2007, 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.value;
import java.awt.*;
import java.awt.datatransfer.*;
import java.awt.event.*;
import com.sun.max.ins.*;
import com.sun.max.ins.gui.*;
import com.sun.max.ins.method.*;
import com.sun.max.ins.object.*;
import com.sun.max.lang.*;
import com.sun.max.program.*;
import com.sun.max.tele.*;
import com.sun.max.tele.MaxMarkBitmap.MarkColor;
import com.sun.max.tele.debug.*;
import com.sun.max.tele.method.*;
import com.sun.max.tele.object.*;
import com.sun.max.unsafe.*;
import com.sun.max.vm.actor.holder.*;
import com.sun.max.vm.compiler.*;
import com.sun.max.vm.value.*;
// TODO (mlvdv) the design of this class has long gotten out of control; there are much better alternatives,
// for example reifying a subset of the display modes using the State patter.
/**
* A textual label for a word of machine data from the VM,
* with multiple display modes and user interaction affordances.
*/
public class WordValueLabel extends ValueLabel {
private static final int TRACE_VALUE = 1;
// Optionally supplied component that needs to be
// repainted when this label changes its appearance.
private final Component parent;
/**
* The expected kind of word value. The visual
* representations available (of which there may only
* be one) are derived from this and the word's value.
*/
public enum ValueMode {
WORD,
REFERENCE,
HUB_REFERENCE,
LITERAL_REFERENCE,
INTEGER_REGISTER,
FLAGS_REGISTER,
FLOATING_POINT,
SIZE,
CALL_ENTRY_POINT,
ITABLE_ENTRY,
CALL_RETURN_POINT;
}
private final ValueMode valueMode;
/**
* The actual kind of word value, determined empirically by reading from the VM; this may change after update.
* Possible visual presentations of a word, constrained by the {@linkplain ValueMode valueMode} of the
* label and its value.
*/
private enum DisplayMode {
/**
* Display generically as a word of data, about which nothing is known other than non-null.
*/
WORD,
/**
* Display generically as a word of data in the special case where the word is the null value.
*/
NULL_WORD,
/**
* Expected to be an object reference, but something about it is broken.
*/
INVALID_OBJECT_REFERENCE,
/**
* Numeric display of a valid object reference.
*/
OBJECT_REFERENCE,
/**
* Textual display of a valid object reference.
*/
OBJECT_REFERENCE_TEXT,
/**
* Numeric display of a valid object reference occurring in a Hub field.
*/
HUB_REFERENCE,
/**
* Textual display of a valid object reference occurring in a Hub field.
*/
HUB_REFERENCE_TEXT,
/**
* Numeric display of a valid object reference.
*/
QUASI_OBJECT_REFERENCE,
/**
* Textual display of a valid object reference.
*/
QUASI_OBJECT_REFERENCE_TEXT,
/**
* Numeric display of a valid pointer into a stack.
*/
STACK_LOCATION,
/**
* Textual display of a valid pointer into a stack.
*/
STACK_LOCATION_TEXT,
/**
* Numeric display of a valid pointer to a thread local value.
*/
THREAD_LOCALS_BLOCK_LOCATION,
/**
* Textual display of a valid pointer to a thread local value.
*/
THREAD_LOCALS_BLOCK_LOCATION_TEXT,
/**
* Numeric display of a valid pointer to a method entry in compiled code.
*/
CALL_ENTRY_POINT,
/**
* Textual display of a valid pointer to a method entry in compiled code.
*/
CALL_ENTRY_POINT_TEXT,
/**
* The internal ID of a {@link ClassActor} in the VM.
*/
CLASS_ACTOR_ID,
CLASS_ACTOR,
/**
* Numeric display of a valid pointer into a method in compiled code, not at the entry.
*/
CALL_RETURN_POINT,
/**
* Textual display of a valid pointer into a method in compiled code, not at the entry.
*/
CALL_RETURN_POINT_TEXT,
/**
* Numeric display of a valid pointer into a native function.
*/
NATIVE_FUNCTION,
/**
* Textual display of a valid pointer into a native function.
*/
NATIVE_FUNCTION_TEXT,
/**
* Display of bits interpreted as flags.
*/
FLAGS,
/**
* Display of numeric value in decimal.
*/
DECIMAL,
/**
* Display of a floating point value.
*/
FLOAT,
/**
* Display of an extended floating point value.
*/
DOUBLE,
/**
* Display of an unsigned decimal numeric value representing a Size.
*/
SIZE,
/**
* Numeric display of what is expected to be a reference, but which is not checked by reading from the VM.
*/
UNCHECKED_REFERENCE,
/**
* Numeric display of what is expected to point to a code call site, but which is not checked by reading from the VM.
*/
UNCHECKED_CALL_POINT,
/**
* Numeric display of a word for which there are no expectations, and which is not checked by reading from the VM.
*/
UNCHECKED_WORD,
/**
* Special display of a word that has been filled with the special "Zapped" value.
*/
ZAPPED,
/**
* Numeric display of a word whose value is invalid relative to expectations for it.
*/
INVALID,
/**
* Display in situations where the value cannot be read from the VM.
*/
UNREADABLE;
}
private DisplayMode displayMode = null;
private final boolean preferTextInitially;
private Font wordDataFont;
/**
* Creates a display label for a word of machine data, initially set to null.
* <p>
* Content of label is supplied by override {@link ValueLabel#fetchValue()}, which
* gets called initially and when the label is refreshed.
* <br>
* Display state can be cycled among alternate presentations in some situations.
* <p>
* Can be used as a cell renderer in a table, but the enclosing table must be explicitly repainted
* when the display state is cycled; this will be done automatically if the table is passed in
* as the parent component.
*
* @param inspection
* @param valueMode presumed type of value for the word, influences display modes
* @param parent a component that should be repainted when the display state is cycled;
*/
public WordValueLabel(Inspection inspection, ValueMode valueMode, Component parent) {
this(inspection, valueMode, Word.zero(), parent);
}
/**
* Creates a display label for a word of machine data, initially set to null.
* <p>
* Content of label is supplied by override {@link ValueLabel#fetchValue()}, which
* gets called initially and when the label is refreshed.
* <br>
* Display state can be cycled among alternate presentations in some situations.
* <p>
* Can be used as a cell renderer in a table, but the enclosing table must be explicitly repainted
* when the display state is cycled; this will be done automatically if the table is passed in
* as the parent component.
*
* @param inspection
* @param valueMode presumed type of value for the word, influences display modes
* @param parent a component that should be repainted when the display state is cycled;
* @param preferTextInitially if {@code true} causes any possible alternate (textual) display to be used initially.
*/
public WordValueLabel(Inspection inspection, ValueMode valueMode, Component parent, boolean preferTextInitially) {
this(inspection, valueMode, Word.zero(), parent, preferTextInitially);
}
/**
* Creates a display label for a word of machine data, initially set to null.
* <p>
* Content of label is set initially by parameter. It can be updated by overriding{@link ValueLabel#fetchValue()}, which
* gets called initially and when the label is refreshed.
* <p>
* Display state can be cycled among alternate presentations in some situations.
* <p>
* Can be used as a cell renderer in a table, but the enclosing table must be explicitly repainted
* when the display state is cycled; this will be done automatically if the table is passed in
* as the parent component.
*
* @param inspection
* @param valueMode presumed type of value for the word, influences display modes
* @param word initial value for content.
* @param parent a component that should be repainted when the display state is cycled;
*/
public WordValueLabel(Inspection inspection, ValueMode valueMode, Word word, Component parent) {
this(inspection, valueMode, word, parent, inspection.preference().forceTextualWordValueDisplay());
}
/**
* Creates a display label for a word of machine data, initially set to null.
* <p>
* Content of label is set initially by parameter. It can be updated by overriding{@link ValueLabel#fetchValue()}, which
* gets called initially and when the label is refreshed.
* <p>
* Display state can be cycled among alternate presentations in some situations, and the {@code forceTxt} can specify
* which to use initially.
* <p>
* Can be used as a cell renderer in a table, but the enclosing table must be explicitly repainted
* when the display state is cycled; this will be done automatically if the table is passed in
* as the parent component.
*
* @param inspection
* @param valueMode presumed type of value for the word, influences display modes
* @param word initial value for content.
* @param parent a component that should be repainted when the display state is cycled;
* @param preferTextInitially if {@code true} causes any possible alternate (textual) display to be used initially.
*/
public WordValueLabel(Inspection inspection, ValueMode valueMode, Word word, Component parent, boolean preferTextInitially) {
super(inspection, null);
this.parent = parent;
this.valueMode = valueMode;
this.wordDataFont = inspection.preference().style().defaultWordDataFont();
this.preferTextInitially = preferTextInitially;
initializeValue();
if (value() == null) {
setValue(new WordValue(word));
}
redisplay();
addMouseListener(new InspectorMouseClickAdapter(inspection()) {
@Override
public void procedure(final MouseEvent mouseEvent) {
//System.out.println("WVL (" + _valueMode.toString() + ", " + _valueKind.toString() + ")");
switch (inspection().gui().getButton(mouseEvent)) {
case MouseEvent.BUTTON1: {
if (mouseEvent.isShiftDown()) {
final InspectorAction viewMemoryAction = getViewMemoryAction(value());
if (viewMemoryAction != null) {
viewMemoryAction.perform();
}
} else if (mouseEvent.isMetaDown()) {
InspectorAction inspectAction = getCloseAndViewObjectAction(value());
if (inspectAction == null) {
inspectAction = getInspectValueAction(value());
}
if (inspectAction != null) {
inspectAction.perform();
}
} else {
final InspectorAction inspectAction = getInspectValueAction(value());
if (inspectAction != null) {
inspectAction.perform();
}
}
break;
}
case MouseEvent.BUTTON2: {
final InspectorAction cycleAction = getCycleDisplayTextAction();
if (cycleAction != null) {
cycleAction.perform();
}
break;
}
case MouseEvent.BUTTON3: {
final InspectorPopupMenu menu = new InspectorPopupMenu();
menu.add(new WordValueMenuItems(inspection(), value()));
switch (displayMode) {
case OBJECT_REFERENCE:
case OBJECT_REFERENCE_TEXT: {
MaxObject object = null;
try {
object = vm().objects().findObjectAt(value().toWord().asAddress());
} catch (MaxVMBusyException e) {
}
if (object != null) {
final TeleClassMethodActor teleClassMethodActor = object.getTeleClassMethodActorForObject();
if (teleClassMethodActor != null) {
// Add method-related menu items
final ClassMethodActorMenuItems items = new ClassMethodActorMenuItems(inspection(), teleClassMethodActor);
items.addTo(menu);
}
}
break;
}
case QUASI_OBJECT_REFERENCE:
case QUASI_OBJECT_REFERENCE_TEXT: {
// TODO (mlvdv) special right-button menu items to a pointer at a quasi object, perhaps specialized for each kind
break;
}
case HUB_REFERENCE:
case HUB_REFERENCE_TEXT: {
// TODO (mlvdv) special right-button menu items appropriate to a forwarding pointer
break;
}
case STACK_LOCATION:
case STACK_LOCATION_TEXT: {
// TODO (mlvdv) special right-button menu items appropriate to a pointer into stack memory
break;
}
case THREAD_LOCALS_BLOCK_LOCATION:
case THREAD_LOCALS_BLOCK_LOCATION_TEXT: {
// TODO (mlvdv) special right-button menu items appropriate to a pointer into a thread locals block
break;
}
default: {
break;
}
}
menu.show(mouseEvent.getComponent(), mouseEvent.getX(), mouseEvent.getY());
}
}
}
});
}
/** Object in the VM heap pointed to by the word, if it is a valid reference. */
private MaxObject object;
/** Non-null if a Class ID. */
private TeleClassActor teleClassActor;
/** Non-null if a pointer into a method compilation. */
private MaxCompilation compilation;
/** Non-null if a tagged pointer into a method compilation, only meaningful if compilation is non-null. */
private RemoteCodePointer taggedCodePointer;
/** Non-null if a pointer into a native function. */
TeleNativeFunction nativeFunction;
/** Non-null if a pointer into thread memory. */
private MaxThread thread;
/** Non-null if a pointer into a stack. */
private MaxStack stack;
/** Non-null if pointer at a thread local variable. */
private MaxThreadLocalsBlock threadLocalsBlock;
@Override
public final void setValue(Value newValue) {
object = null;
teleClassActor = null;
compilation = null;
taggedCodePointer = null;
thread = null;
stack = null;
threadLocalsBlock = null;
/**
* The previous display mode, null initially, which will supersede the default and be reused if appropriate
*/
final DisplayMode oldDisplayMode = displayMode;
/**
* {@code true} only the first time the value gets set <em>and</em> there is a preference for initial text.
* After the first time, the mode previously in effect, as selected by the user, prevails if appropriate to the value.
*/
final boolean forceText = oldDisplayMode == null && preferTextInitially;
if (newValue == VoidValue.VOID) {
displayMode = DisplayMode.UNREADABLE;
} else if (vm().memoryIO().isZappedValue(newValue) && valueMode != ValueMode.INTEGER_REGISTER) {
// We want to use a special display for this value when in memory, but not in a register when the zapping is being done
displayMode = DisplayMode.ZAPPED;
} else if (valueMode == ValueMode.FLAGS_REGISTER) {
if (newValue == null) {
displayMode = DisplayMode.INVALID;
} else if (displayMode == null) {
displayMode = DisplayMode.FLAGS;
}
} else if (valueMode == ValueMode.FLOATING_POINT) {
if (newValue == null) {
displayMode = DisplayMode.INVALID;
} else if (displayMode == null) {
displayMode = DisplayMode.DOUBLE;
}
} else if (valueMode == ValueMode.SIZE) {
displayMode = DisplayMode.SIZE;
} else if (!preference().investigateWordValues()) {
if (valueMode == ValueMode.REFERENCE || valueMode == ValueMode.LITERAL_REFERENCE) {
displayMode = DisplayMode.UNCHECKED_REFERENCE;
} else if (valueMode == ValueMode.CALL_ENTRY_POINT || valueMode == ValueMode.CALL_RETURN_POINT) {
displayMode = DisplayMode.UNCHECKED_CALL_POINT;
} else {
displayMode = DisplayMode.UNCHECKED_WORD;
}
} else {
displayMode = DisplayMode.WORD;
if (vm().isBootImageRelocated()) {
try {
final Address address = newValue.toWord().asAddress();
thread = vm().threadManager().findThread(address);
// From here on, we need to try reading from the VM, if it is available
if (newValue == null || newValue.isZero()) {
if (valueMode == ValueMode.REFERENCE || preferTextInitially) {
displayMode = DisplayMode.NULL_WORD;
}
} else if ((object = vm().objects().findObjectAt(address)) != null) {
// Value is the address of an object origin; select display mode, keeping previous one if relevant.
switch(valueMode) {
case HUB_REFERENCE:
displayMode = (oldDisplayMode == DisplayMode.HUB_REFERENCE_TEXT || forceText) ? DisplayMode.HUB_REFERENCE_TEXT : DisplayMode.HUB_REFERENCE;
break;
default:
displayMode = (oldDisplayMode == DisplayMode.OBJECT_REFERENCE_TEXT || forceText) ? DisplayMode.OBJECT_REFERENCE_TEXT : DisplayMode.OBJECT_REFERENCE;
}
} else if ((object = vm().objects().findQuasiObjectAt(address)) != null) {
displayMode = (oldDisplayMode == DisplayMode.QUASI_OBJECT_REFERENCE_TEXT || forceText) ? DisplayMode.QUASI_OBJECT_REFERENCE_TEXT : DisplayMode.QUASI_OBJECT_REFERENCE;
} else if (thread != null && thread.stack().memoryRegion().contains(address)) {
stack = thread.stack();
displayMode = (oldDisplayMode == DisplayMode.STACK_LOCATION_TEXT || forceText) ? DisplayMode.STACK_LOCATION_TEXT : DisplayMode.STACK_LOCATION;
} else if (thread != null && thread.localsBlock().memoryRegion() != null && thread.localsBlock().memoryRegion().contains(address)) {
threadLocalsBlock = thread.localsBlock();
displayMode = (oldDisplayMode == DisplayMode.THREAD_LOCALS_BLOCK_LOCATION_TEXT || forceText) ? DisplayMode.THREAD_LOCALS_BLOCK_LOCATION_TEXT : DisplayMode.THREAD_LOCALS_BLOCK_LOCATION;
} else if (valueMode == ValueMode.REFERENCE || valueMode == ValueMode.LITERAL_REFERENCE) {
displayMode = DisplayMode.INVALID_OBJECT_REFERENCE;
} else if (valueMode == ValueMode.HUB_REFERENCE) {
object = vm().objects().findForwardedObjectAt(address);
if (object != null) {
displayMode = DisplayMode.HUB_REFERENCE_TEXT;
} else {
displayMode = DisplayMode.INVALID_OBJECT_REFERENCE;
}
} else {
compilation = vm().machineCode().findCompilation(address);
if (compilation == null) {
// No compilation at that address; maybe it's really a tagged pointer.
taggedCodePointer = vm().machineCode().makeCodePointerFromTaggedLong(address.toLong());
if (taggedCodePointer != null) {
// Could be a code pointer
compilation = vm().machineCode().findCompilation(taggedCodePointer);
}
}
if (compilation != null) {
// The word points at a method compilation
final Address codeStart = compilation.getCodeStart();
final Word jitEntryPoint = codeStart.plus(CallEntryPoint.BASELINE_ENTRY_POINT.offset());
final Word optimizedEntryPoint = codeStart.plus(CallEntryPoint.OPTIMIZED_ENTRY_POINT.offset());
if (newValue.toWord().equals(optimizedEntryPoint) || newValue.toWord().equals(jitEntryPoint)) {
displayMode = (oldDisplayMode == DisplayMode.CALL_ENTRY_POINT_TEXT || forceText) ? DisplayMode.CALL_ENTRY_POINT_TEXT : DisplayMode.CALL_ENTRY_POINT;
} else {
displayMode = (oldDisplayMode == DisplayMode.CALL_RETURN_POINT_TEXT || forceText) ? DisplayMode.CALL_RETURN_POINT_TEXT : DisplayMode.CALL_RETURN_POINT;
}
} else {
nativeFunction = vm().machineCode().findNativeFunction(address);
if (nativeFunction != null) {
// The word points into a native function
displayMode = (valueMode == ValueMode.CALL_ENTRY_POINT || oldDisplayMode == DisplayMode.NATIVE_FUNCTION_TEXT || forceText) ? DisplayMode.NATIVE_FUNCTION_TEXT : DisplayMode.NATIVE_FUNCTION;
} else if (valueMode == ValueMode.ITABLE_ENTRY) {
final TeleClassActor teleClassActor = vm().classes().findTeleClassActor(address.toInt());
if (teleClassActor != null) {
this.teleClassActor = teleClassActor;
displayMode = DisplayMode.CLASS_ACTOR;
} else {
displayMode = DisplayMode.CLASS_ACTOR_ID;
}
}
}
}
final MaxMarkBitmap markBitMap = vm().heap().markBitmap();
if (markBitMap != null && markBitMap.isCovered(address)) {
final int bitIndex = markBitMap.getBitIndexOf(address);
final StringBuilder sb = new StringBuilder();
sb.append("<br>Heap mark bit(");
sb.append(bitIndex);
sb.append(")=");
sb.append(markBitMap.isBitSet(bitIndex) ? "1" : "0");
sb.append(", color=");
sb.append(markBitMap.getMarkColor(bitIndex));
this.setToolTipSuffix(sb.toString());
}
} catch (TerminatedProcessIOException terminatedProcessIOException) {
object = null;
teleClassActor = null;
displayMode = DisplayMode.WORD;
} catch (Throwable throwable) {
object = null;
teleClassActor = null;
displayMode = DisplayMode.INVALID;
setWrappedToolTipHtmlText("<b>" + throwable + "</b><br>See log for complete stack trace.");
throwable.printStackTrace(Trace.stream());
}
}
}
super.setValue(newValue);
}
public void redisplay() {
this.wordDataFont = inspection().preference().style().defaultWordDataFont();
setValue(value());
}
@Override
public void updateText() {
final Value value = value();
if (value == null) {
return;
}
final InspectorStyle style = preference().style();
final InspectorNameDisplay nameDisplay = inspection().nameDisplay();
switch (displayMode) {
case WORD: {
setFont(wordDataFont);
setWrappedText(hexString(value));
if (value.isZero()) {
setForeground(style.wordNullDataColor());
setWrappedToolTipHtmlText("zero");
} else {
setForeground(null);
final StringBuilder ttText = new StringBuilder();
ttText.append(hexString(value));
ttText.append("<br>Decimal= ").append(Long.toString(value.toLong()));
final Address address = value.toWord().asAddress();
final MaxMemoryRegion memoryRegion = vm().state().findMemoryRegion(address);
if (memoryRegion != null) {
ttText.append("<br>Points into region ").append(memoryRegion.regionName());
}
setWrappedToolTipHtmlText(ttText.toString());
}
break;
}
case UNCHECKED_WORD: {
setFont(wordDataFont);
setWrappedText(hexString(value));
if (value.isZero()) {
setForeground(style.wordNullDataColor());
setWrappedToolTipHtmlText("zero");
} else {
setForeground(null);
setWrappedToolTipHtmlText(valueToDecimalAndHex(value) + " - UNCHECKED");
}
break;
}
case NULL_WORD: {
setFont(style.wordAlternateTextFont());
setForeground(style.wordNullDataColor());
setWrappedText("null");
setWrappedToolTipHtmlText("null");
break;
}
case INVALID: {
setFont(style.wordAlternateTextFont());
setForeground(style.wordInvalidDataColor());
setWrappedText(hexString(value));
if (valueMode == ValueMode.LITERAL_REFERENCE) {
setWrappedToolTipHtmlText("invalid reference");
} else {
setWrappedToolTipHtmlText(valueToDecimalAndHex(value));
}
break;
}
case OBJECT_REFERENCE: {
setFont(wordDataFont);
setForeground(style.wordValidObjectReferenceDataColor());
setWrappedText(hexString(value));
try {
// The syntax of object reference names contains "<" and ">"; make them safe for HTML tool tips.
final StringBuilder toolTipSB = new StringBuilder();
toolTipSB.append(value.toWord().toPadded0xHexString('0'));
toolTipSB.append("<br>Reference(").append(object.status().label()).append(") to ").append(htmlify(nameDisplay.referenceToolTipText(object)));
toolTipSB.append("<br>In ");
final MaxMemoryRegion memoryRegion = vm().state().findMemoryRegion(value().toWord().asAddress());
if (memoryRegion == null) {
toolTipSB.append(htmlify("<unknown memory region>"));
} else {
toolTipSB.append("\"").append(nameDisplay.longName(memoryRegion)).append("\"");
}
final String gcDescription = object.reference().gcDescription();
if (gcDescription != null) {
toolTipSB.append("<br>GC: " + gcDescription);
}
setWrappedToolTipHtmlText(toolTipSB.toString());
} catch (Throwable throwable) {
// If we don't catch this the views will not be updated at all.
object = null;
displayMode = DisplayMode.INVALID_OBJECT_REFERENCE;
setWrappedToolTipHtmlText("<b>" + throwable + "</b><br>See log for complete stack trace.");
throwable.printStackTrace(Trace.stream());
}
break;
}
case QUASI_OBJECT_REFERENCE: {
// TODO (mlvdv) specialize the display for each kind of quasi object
setFont(wordDataFont);
setForeground(style.wordNullDataColor());
setWrappedText(hexString(value));
try {
// The syntax of object reference names contains "<" and ">"; make them safe for HTML tool tips.
final StringBuilder toolTipSB = new StringBuilder();
toolTipSB.append(value.toWord().toPadded0xHexString('0'));
toolTipSB.append("<br>Quasi-reference(").append(object.status().label()).append(") to ").append(htmlify(nameDisplay.referenceToolTipText(object)));
toolTipSB.append("<br>In ");
final MaxMemoryRegion memoryRegion = vm().state().findMemoryRegion(value().toWord().asAddress());
if (memoryRegion == null) {
toolTipSB.append(htmlify("<unknown memory region>"));
} else {
toolTipSB.append("\"").append(nameDisplay.longName(memoryRegion)).append("\"");
}
final String gcDescription = object.reference().gcDescription();
if (gcDescription != null) {
toolTipSB.append("<br>GC: " + gcDescription);
}
setWrappedToolTipHtmlText(toolTipSB.toString());
} catch (Throwable throwable) {
// If we don't catch this the views will not be updated at all.
object = null;
displayMode = DisplayMode.INVALID_OBJECT_REFERENCE;
setWrappedToolTipHtmlText("<b>" + throwable + "</b><br>See log for complete stack trace.");
throwable.printStackTrace(Trace.stream());
}
break;
}
case HUB_REFERENCE: {
final Address address = value.toWord().asAddress();
String forward = "";
try {
if (vm().objects().findForwardedObjectAt(address) != null) {
forward = "=> ";
}
} catch (MaxVMBusyException e) {
}
setFont(wordDataFont);
setForeground(style.wordValidObjectReferenceDataColor());
setWrappedText(forward + hexString(value));
try {
// The syntax of object reference names contains "<" and ">"; make them safe for HTML tool tips.
final StringBuilder toolTipSB = new StringBuilder();
toolTipSB.append(value.toWord().toPadded0xHexString('0'));
toolTipSB.append("<br>Pointer to ").append(htmlify(nameDisplay.referenceToolTipText(object)));
toolTipSB.append("<br>In ");
final MaxMemoryRegion memoryRegion = vm().state().findMemoryRegion(value().toWord().asAddress());
if (memoryRegion == null) {
toolTipSB.append(htmlify("<unknown memory region>"));
} else {
toolTipSB.append("\"").append(nameDisplay.longName(memoryRegion)).append("\"");
}
final String gcDescription = object.reference().gcDescription();
if (gcDescription != null) {
toolTipSB.append("<br>GC: " + gcDescription);
}
setWrappedToolTipHtmlText(toolTipSB.toString());
} catch (Throwable throwable) {
// If we don't catch this the views will not be updated at all.
object = null;
displayMode = DisplayMode.INVALID_OBJECT_REFERENCE;
setWrappedToolTipHtmlText("<b>" + throwable + "</b><br>See log for complete stack trace.");
throwable.printStackTrace(Trace.stream());
}
break;
}
case OBJECT_REFERENCE_TEXT: {
final boolean objectIsForwarded = object != null && object.reference().status().isForwarder();
setFont(style.wordAlternateTextFont());
if (objectIsForwarded) {
setForeground(style.wordForwardingReferenceDataColor());
} else {
setForeground(style.wordValidObjectReferenceDataColor());
}
try {
final String labelText = nameDisplay.referenceLabelText(object);
if (labelText != null) {
setWrappedText(labelText);
// The syntax of object reference names contains "<" and ">"; make them safe for HTML tool tips.
final StringBuilder toolTipSB = new StringBuilder();
toolTipSB.append(value.toWord().toPadded0xHexString('0'));
toolTipSB.append("<br>Reference(").append(object.status().label()).append(") to ").append(htmlify(nameDisplay.referenceToolTipText(object)));
toolTipSB.append("<br>In ");
final MaxMemoryRegion memoryRegion = vm().state().findMemoryRegion(value().toWord().asAddress());
if (memoryRegion == null) {
toolTipSB.append(htmlify("<unknown memory region>"));
} else {
toolTipSB.append("\"").append(nameDisplay.longName(memoryRegion)).append("\"");
}
final String gcDescription = object.reference().gcDescription();
if (gcDescription != null) {
toolTipSB.append("<br>GC: " + gcDescription);
}
setWrappedToolTipHtmlText(toolTipSB.toString());
break;
}
} catch (Throwable throwable) {
// If we don't catch this the views will not be updated at all.
object = null;
displayMode = DisplayMode.INVALID_OBJECT_REFERENCE;
setWrappedToolTipHtmlText("<b>" + throwable + "</b><br>See log for complete stack trace.");
throwable.printStackTrace(Trace.stream());
break;
}
displayMode = DisplayMode.OBJECT_REFERENCE;
updateText();
break;
}
case QUASI_OBJECT_REFERENCE_TEXT: {
// TODO (mlvdv) specialize the display for each kind of quasi object
setFont(style.wordAlternateTextFont());
setForeground(style.wordNullDataColor());
try {
final String labelText = nameDisplay.referenceLabelText(object);
if (labelText != null) {
setWrappedText(labelText);
// The syntax of object reference names contains "<" and ">"; make them safe for HTML tool tips.
final StringBuilder toolTipSB = new StringBuilder();
toolTipSB.append(value.toWord().toPadded0xHexString('0'));
toolTipSB.append("<br>Quasi-reference(").append(object.status().label()).append(") to ").append(htmlify(nameDisplay.referenceToolTipText(object)));
toolTipSB.append("<br>In ");
final MaxMemoryRegion memoryRegion = vm().state().findMemoryRegion(value().toWord().asAddress());
if (memoryRegion == null) {
toolTipSB.append(htmlify("<unknown memory region>"));
} else {
toolTipSB.append("\"").append(nameDisplay.longName(memoryRegion)).append("\"");
}
final String gcDescription = object.reference().gcDescription();
if (gcDescription != null) {
toolTipSB.append("<br>GC: " + gcDescription);
}
setWrappedToolTipHtmlText(toolTipSB.toString());
break;
}
} catch (Throwable throwable) {
// If we don't catch this the views will not be updated at all.
object = null;
displayMode = DisplayMode.INVALID_OBJECT_REFERENCE;
setWrappedToolTipHtmlText("<b>" + throwable + "</b><br>See log for complete stack trace.");
throwable.printStackTrace(Trace.stream());
break;
}
displayMode = DisplayMode.OBJECT_REFERENCE;
updateText();
break;
}
case HUB_REFERENCE_TEXT: {
final boolean hubIsForwarded = object != null && object.reference().status().isForwarder();
setFont(style.wordAlternateTextFont());
if (hubIsForwarded) {
setForeground(style.wordForwardingReferenceDataColor());
} else {
setForeground(style.wordValidObjectReferenceDataColor());
}
try {
final Address address = value.toWord().asAddress();
final String forward = vm().objects().findForwardedObjectAt(address) == null ? "" : "=> ";
final String labelText = nameDisplay.referenceLabelText(object);
if (labelText != null) {
setWrappedText(forward + labelText);
// The syntax of object reference names contains "<" and ">"; make them safe for HTML tool tips.
final StringBuilder toolTipSB = new StringBuilder();
toolTipSB.append(value.toWord().toPadded0xHexString('0'));
toolTipSB.append(hubIsForwarded ? "<br> Forwarder to" : "<br>Pointer to ").append(htmlify(nameDisplay.referenceToolTipText(object)));
toolTipSB.append("<br>In ");
final MaxMemoryRegion memoryRegion = vm().state().findMemoryRegion(value().toWord().asAddress());
if (memoryRegion == null) {
toolTipSB.append(htmlify("<unknown memory region>"));
} else {
toolTipSB.append("\"").append(nameDisplay.longName(memoryRegion)).append("\"");
}
final String gcDescription = object.reference().gcDescription();
if (gcDescription != null) {
toolTipSB.append("<br>GC: " + gcDescription);
}
setWrappedToolTipHtmlText(toolTipSB.toString());
break;
}
} catch (Throwable throwable) {
// If we don't catch this the views will not be updated at all.
object = null;
displayMode = DisplayMode.INVALID_OBJECT_REFERENCE;
setWrappedToolTipHtmlText("<b>" + throwable + "</b><br>See log for complete stack trace.");
throwable.printStackTrace(Trace.stream());
break;
}
displayMode = DisplayMode.OBJECT_REFERENCE;
updateText();
break;
}
case STACK_LOCATION: {
setFont(wordDataFont);
setForeground(style.wordStackLocationDataColor());
setWrappedText(hexString(value));
final String threadName = nameDisplay.longName(thread);
final Address address = value().asWord().asAddress();
final long offset = address.minus(stack.memoryRegion().start()).toLong();
final StringBuilder ttBuilder = new StringBuilder();
ttBuilder.append(value.toWord().to0xHexString());
try {
final MaxStackFrame stackFrame = stack.findStackFrame(address);
String methodName = null;
if (stackFrame instanceof MaxStackFrame.Compiled) {
methodName = inspection().nameDisplay().veryShortName(stackFrame.compilation());
}
if (stackFrame == null) {
ttBuilder.append("<br>Points into stack for thread ").append(threadName);
ttBuilder.append("<br>").append(longToDecimalAndHex(offset)).append(" bytes from beginning");
} else {
ttBuilder.append("<br>Points at stack frame for thread ").append(threadName);
final String positionString = Integer.toString(stackFrame.position());
if (methodName == null) {
ttBuilder.append("<br> position=").append(positionString);
} else {
ttBuilder.append("<br> ").append(positionString).append(": ").append(methodName);
}
}
} catch (NullPointerException enull) {
ttBuilder.append("<br>Points into stack for thread ").append(threadName);
ttBuilder.append("<br>").append(longToDecimalAndHex(offset)).append(" bytes from beginning");
}
setWrappedToolTipHtmlText(ttBuilder.toString());
break;
}
case STACK_LOCATION_TEXT: {
setFont(style.wordAlternateTextFont());
setForeground(style.wordStackLocationDataColor());
final String threadName = nameDisplay.longName(thread);
final Address address = value().asWord().asAddress();
final long offset = address.minus(stack.memoryRegion().start()).toLong();
final String offsetString = offset >= 0 ? ("+" + offset) : Long.toString(offset);
final MaxStackFrame stackFrame = stack.findStackFrame(address);
String methodName = null;
if (stackFrame instanceof MaxStackFrame.Compiled) {
methodName = inspection().nameDisplay().veryShortName(stackFrame.compilation());
}
final StringBuilder textBuilder = new StringBuilder();
textBuilder.append(threadName).append(" ");
final StringBuilder ttBuilder = new StringBuilder();
ttBuilder.append(value.toWord().to0xHexString());
if (stackFrame == null) {
textBuilder.append(offsetString);
ttBuilder.append("<br>Points into stack for thread ").append(threadName);
ttBuilder.append("<br>").append(longToDecimalAndHex(offset)).append(" bytes from beginning");
} else {
ttBuilder.append("<br>Points into stack frame for thread ").append(threadName);
final String positionString = Integer.toString(stackFrame.position());
if (methodName == null) {
textBuilder.append(offsetString);
ttBuilder.append("<br> position=").append(positionString);
} else {
textBuilder.append(positionString).append(": ").append(methodName);
ttBuilder.append("<br> ").append(positionString).append(": ").append(methodName);
}
}
setWrappedText(textBuilder.toString());
setWrappedToolTipHtmlText(ttBuilder.toString());
break;
}
case THREAD_LOCALS_BLOCK_LOCATION: {
setFont(wordDataFont);
setForeground(style.wordThreadLocalsBlockLocationDataColor());
setWrappedText(hexString(value));
final String threadName = nameDisplay.longName(thread);
final Address address = value().asWord().asAddress();
final long offset = address.minus(thread.localsBlock().memoryRegion().start()).toLong();
MaxThreadLocalVariable tlVariable = null;
MaxThreadLocalsArea tlArea = threadLocalsBlock.findTLA(address);
if (tlArea != null) {
tlVariable = tlArea.findThreadLocalVariable(address);
}
final StringBuilder ttBuilder = new StringBuilder();
ttBuilder.append(value.toWord().to0xHexString());
if (tlVariable == null) {
ttBuilder.append("<br>Points into thread locals area for thread ").append(threadName);
ttBuilder.append("<br>").append(longToDecimalAndHex(offset)).append(" bytes from beginning");
} else {
ttBuilder.append("<br>Points at thread local variable ").append(tlVariable.variableName()).append(" for:");
ttBuilder.append("<br> thread=").append(threadName);
ttBuilder.append("<br> state=").append(tlVariable.safepointState().name());
ttBuilder.append("<br> desc.=").append(tlVariable.entityDescription());
ttBuilder.append("<br> value=").append(tlVariable.value().toString());
}
setWrappedToolTipHtmlText(ttBuilder.toString());
break;
}
case THREAD_LOCALS_BLOCK_LOCATION_TEXT: {
setFont(style.wordAlternateTextFont());
setForeground(style.wordThreadLocalsBlockLocationDataColor());
final String threadName = nameDisplay.longName(thread);
final Address address = value().asWord().asAddress();
final long offset = address.minus(thread.localsBlock().memoryRegion().start()).toLong();
MaxThreadLocalVariable tlVariable = null;
MaxThreadLocalsArea tlArea = threadLocalsBlock.findTLA(address);
if (tlArea != null) {
tlVariable = tlArea.findThreadLocalVariable(address);
}
final StringBuilder textBuilder = new StringBuilder();
textBuilder.append(threadName).append(" ");
final StringBuilder ttBuilder = new StringBuilder();
ttBuilder.append(value.toWord().to0xHexString());
if (tlVariable == null) {
textBuilder.append(longToPlusMinusDecimal(offset));
ttBuilder.append("<br>Points into thread locals area for thread ").append(threadName);
ttBuilder.append("<br>").append(longToDecimalAndHex(offset)).append(" bytes from beginning");
} else {
textBuilder.append(tlVariable.variableName());
ttBuilder.append("<br>Points at thread local variable ").append(tlVariable.variableName()).append(" for:");
ttBuilder.append("<br> thread=").append(threadName);
ttBuilder.append("<br> state=").append(tlVariable.safepointState().name());
ttBuilder.append("<br> desc.=").append(tlVariable.entityDescription());
ttBuilder.append("<br> value=").append(tlVariable.value().toString());
}
setWrappedText(textBuilder.toString());
setWrappedToolTipHtmlText(ttBuilder.toString());
break;
}
case UNCHECKED_REFERENCE: {
setFont(wordDataFont);
setForeground(style.wordUncheckedReferenceDataColor());
setWrappedText(hexString(value));
final StringBuilder toolTipSB = new StringBuilder();
toolTipSB.append(value.toWord().toPadded0xHexString('0'));
if (valueMode == ValueMode.LITERAL_REFERENCE) {
toolTipSB.append("<br>").append(htmlify("<unchecked>"));
} else {
toolTipSB.append("<br>").append(htmlify("Unchecked Reference"));
}
setWrappedToolTipHtmlText(toolTipSB.toString());
break;
}
case INVALID_OBJECT_REFERENCE: {
setFont(wordDataFont);
setForeground(style.wordInvalidObjectReferenceDataColor());
setWrappedText(hexString(value));
final StringBuilder toolTipSB = new StringBuilder();
toolTipSB.append(value.toWord().toPadded0xHexString('0'));
if (valueMode == ValueMode.LITERAL_REFERENCE) {
toolTipSB.append("<br>").append(htmlify("<invalid>"));
} else {
toolTipSB.append("<br> invalid object reference");
final MaxEntityMemoryRegion<?> memoryRegion = vm().state().findMemoryRegion(value().asWord().asAddress());
if (memoryRegion == null) {
toolTipSB.append("<br> points into no known memory region");
} else {
toolTipSB.append("<br> points into region " + memoryRegion.regionName());
}
}
setWrappedToolTipHtmlText(toolTipSB.toString());
break;
}
case CALL_ENTRY_POINT: {
setFont(wordDataFont);
setForeground(style.wordCallEntryPointColor());
setWrappedText(hexString(value));
if (compilation != null) {
final StringBuilder toolTip = new StringBuilder();
toolTip.append(value.toWord().to0xHexString());
if (taggedCodePointer != null) {
toolTip.append("<br>Decimal= " + Long.toString(value.toLong()));
toolTip.append("<br>POSSIBLY TAGGED encoding of " + taggedCodePointer.getAddress().to0xHexString());
}
toolTip.append("<br>Points to entry in compilation " + nameDisplay.longMethodCompilationID(compilation) + " for method");
toolTip.append("<br>" + htmlify(nameDisplay.longName(compilation)));
setWrappedToolTipHtmlText(toolTip.toString());
}
break;
}
case CALL_ENTRY_POINT_TEXT: {
setFont(style.wordAlternateTextFont());
setForeground(style.wordCallEntryPointColor());
setWrappedText(nameDisplay.veryShortName(compilation));
if (compilation != null) {
final StringBuilder toolTip = new StringBuilder();
toolTip.append(value.toWord().to0xHexString());
if (taggedCodePointer != null) {
toolTip.append("<br>Decimal= " + Long.toString(value.toLong()));
toolTip.append("<br>POSSIBLY TAGGED encoding of " + taggedCodePointer.getAddress().to0xHexString());
}
toolTip.append("<br>Points to entry in compilation " + nameDisplay.longMethodCompilationID(compilation) + " for method");
toolTip.append("<br>" + htmlify(nameDisplay.longName(compilation)));
setWrappedToolTipHtmlText(toolTip.toString());
}
break;
}
case NATIVE_FUNCTION: {
setFont(wordDataFont);
setForeground(style.wordCallEntryPointColor());
setWrappedText(hexString(value));
setWrappedToolTipHtmlText(value.toWord().to0xHexString() +
"<br>Points into native function: " + nameDisplay.longName(nativeFunction));
break;
}
case NATIVE_FUNCTION_TEXT: {
setFont(style.wordAlternateTextFont());
setForeground(style.wordCallEntryPointColor());
setWrappedText(nameDisplay.shortName(nativeFunction));
setWrappedToolTipHtmlText(value.toWord().to0xHexString() +
"<br>Points into native function: " + nameDisplay.longName(nativeFunction));
break;
}
case CLASS_ACTOR_ID: {
setFont(wordDataFont);
setForeground(null);
setWrappedText(Long.toString(value.asWord().asAddress().toLong()));
if (teleClassActor != null) {
setWrappedToolTipHtmlText(nameDisplay.referenceToolTipText(teleClassActor));
} else {
setToolTipText("Class{???}");
}
break;
}
case CLASS_ACTOR: {
setWrappedText(teleClassActor.classActor().simpleName());
setWrappedToolTipHtmlText(nameDisplay.referenceToolTipText(teleClassActor));
break;
}
case CALL_RETURN_POINT: {
setFont(wordDataFont);
setForeground(style.wordCallReturnPointColor());
setWrappedText(hexString(value));
if (compilation != null) {
final StringBuilder toolTip = new StringBuilder();
toolTip.append(value.toWord().to0xHexString());
final Address address = taggedCodePointer == null ? value.toWord().asAddress() : taggedCodePointer.getAddress();
if (taggedCodePointer != null) {
toolTip.append("<br>Decimal= " + Long.toString(value.toLong()));
toolTip.append("<br>POSSIBLY TAGGED encoding of " + address.to0xHexString());
}
final long position = address.minus(compilation.getCodeStart()).toLong();
toolTip.append("<br>Points into compilation " + nameDisplay.longMethodCompilationID(compilation) + " for method");
toolTip.append("<br>" + htmlify(nameDisplay.longName(compilation)));
toolTip.append("<br>" + longToDecimalAndHex(position) + "bytes from beginning");
setWrappedToolTipHtmlText(toolTip.toString());
}
break;
}
case CALL_RETURN_POINT_TEXT: {
setFont(style.wordAlternateTextFont());
setForeground(style.wordCallReturnPointColor());
if (compilation != null) {
final Address address = taggedCodePointer == null ? value.toWord().asAddress() : taggedCodePointer.getAddress();
setWrappedText(nameDisplay.veryShortName(compilation, address));
final StringBuilder toolTip = new StringBuilder();
toolTip.append(value.toWord().to0xHexString());
if (taggedCodePointer != null) {
toolTip.append("<br>Decimal= " + Long.toString(value.toLong()));
toolTip.append("<br>POSSIBLY TAGGED encoding of " + address.to0xHexString());
}
final long position = address.minus(compilation.getCodeStart()).toLong();
toolTip.append("<br>Points into compilation " + nameDisplay.longMethodCompilationID(compilation) + " for method");
toolTip.append("<br>" + htmlify(nameDisplay.longName(compilation)));
toolTip.append("<br>" + longToDecimalAndHex(position) + "bytes from beginning");
setWrappedToolTipHtmlText(toolTip.toString());
}
break;
}
case UNCHECKED_CALL_POINT: {
setFont(wordDataFont);
setForeground(style.wordUncheckedCallPointColor());
setWrappedText(hexString(value));
setWrappedToolTipHtmlText("Unchecked call entry/return point");
break;
}
case FLAGS: {
setFont(style.wordFlagsFont());
setForeground(null);
setWrappedText(focus().thread().registers().stateRegisterValueToString(value.toLong()));
setWrappedToolTipHtmlText("Flags 0x" + hexString(value));
break;
}
case DECIMAL: {
setFont(style.decimalDataFont());
setForeground(null);
setWrappedText(Integer.toString(value.toInt()));
setWrappedToolTipHtmlText("0x" + hexString(value));
break;
}
case SIZE: {
setFont(style.sizeDataFont());
setForeground(null);
if (value.isZero()) {
setWrappedText("0");
setWrappedToolTipHtmlText("zero");
} else {
final long longValue = value.toLong();
setWrappedText(Long.toString(longValue));
if (longValue == Long.MAX_VALUE) {
setWrappedToolTipHtmlText("MAX Size" + " [" + valueToDecimalAndHex(value) + "]");
} else {
setWrappedToolTipHtmlText(Longs.toUnitsString(longValue, false) + " [" + valueToDecimalAndHex(value) + "]");
}
}
break;
}
case FLOAT: {
setFont(style.wordAlternateTextFont());
setForeground(null);
final String floatText = valueToFloatText(value);
final String doubleText = valueToDoubleText(value);
setWrappedText(floatText);
setWrappedToolTipHtmlText("0x" + hexString(value) + "<br>As float = " + floatText + "<br>As double = " + doubleText);
break;
}
case DOUBLE: {
setFont(style.wordAlternateTextFont());
setForeground(null);
final String floatText = valueToFloatText(value);
final String doubleText = valueToDoubleText(value);
setWrappedText(doubleText);
setWrappedToolTipHtmlText("0x" + hexString(value) + "<br>As float = " + floatText + "<br>As double = " + doubleText);
break;
}
case ZAPPED: {
setFont(wordDataFont);
setForeground(null);
setWrappedText(nameDisplay.zappedDataShortText());
setWrappedToolTipHtmlText(nameDisplay.zappedDataLongText());
break;
}
case UNREADABLE: {
setFont(style.wordAlternateTextFont());
setForeground(style.wordInvalidDataColor());
setWrappedText(nameDisplay.unreadableDataShortText());
setWrappedToolTipHtmlText(nameDisplay.unreadableDataLongText());
break;
}
}
if (parent != null) {
parent.repaint();
}
}
String hexString(Value value) {
if (value == VoidValue.VOID) {
return "";
}
return (valueMode == ValueMode.WORD
|| valueMode == ValueMode.INTEGER_REGISTER
|| valueMode == ValueMode.FLAGS_REGISTER
|| valueMode == ValueMode.FLOATING_POINT) ? value.toWord().toPaddedHexString('0') : value.toWord().toHexString();
}
/**
* Sets the default font for displaying word data in this label, overriding
* the default specified by the style mechanism.
*
* @param font a font do use in this label as the default font for word data
* @see InspectorStyle#defaultWordDataFont()
*/
public void setWordDataFont(Font font) {
this.wordDataFont = font;
}
private InspectorAction getCycleDisplayTextAction() {
DisplayMode alternateValueKind = displayMode;
if (valueMode == ValueMode.FLAGS_REGISTER) {
switch (displayMode) {
case WORD: {
alternateValueKind = DisplayMode.FLAGS;
break;
}
case FLAGS: {
alternateValueKind = DisplayMode.WORD;
break;
}
default: {
break;
}
}
}
if (valueMode == ValueMode.FLOATING_POINT) {
switch (alternateValueKind) {
case WORD: {
alternateValueKind = DisplayMode.DOUBLE;
break;
}
case DOUBLE: {
alternateValueKind = DisplayMode.FLOAT;
break;
}
case FLOAT: {
alternateValueKind = DisplayMode.WORD;
break;
}
default: {
break;
}
}
}
if (valueMode == ValueMode.INTEGER_REGISTER) {
switch (alternateValueKind) {
case WORD: {
alternateValueKind = DisplayMode.DECIMAL;
break;
}
case DECIMAL: {
alternateValueKind = DisplayMode.WORD;
break;
}
default: {
break;
}
}
}
switch (alternateValueKind) {
case OBJECT_REFERENCE: {
alternateValueKind = DisplayMode.OBJECT_REFERENCE_TEXT;
break;
}
case OBJECT_REFERENCE_TEXT: {
alternateValueKind = DisplayMode.OBJECT_REFERENCE;
break;
}
case QUASI_OBJECT_REFERENCE: {
alternateValueKind = DisplayMode.QUASI_OBJECT_REFERENCE_TEXT;
break;
}
case QUASI_OBJECT_REFERENCE_TEXT: {
alternateValueKind = DisplayMode.QUASI_OBJECT_REFERENCE;
break;
}
case HUB_REFERENCE: {
alternateValueKind = DisplayMode.HUB_REFERENCE_TEXT;
break;
}
case HUB_REFERENCE_TEXT: {
alternateValueKind = DisplayMode.HUB_REFERENCE;
break;
}
case STACK_LOCATION: {
alternateValueKind = DisplayMode.STACK_LOCATION_TEXT;
break;
}
case STACK_LOCATION_TEXT: {
alternateValueKind = DisplayMode.STACK_LOCATION;
break;
}
case THREAD_LOCALS_BLOCK_LOCATION: {
alternateValueKind = DisplayMode.THREAD_LOCALS_BLOCK_LOCATION_TEXT;
break;
}
case THREAD_LOCALS_BLOCK_LOCATION_TEXT: {
alternateValueKind = DisplayMode.THREAD_LOCALS_BLOCK_LOCATION;
break;
}
case CALL_ENTRY_POINT: {
alternateValueKind = DisplayMode.CALL_ENTRY_POINT_TEXT;
break;
}
case CALL_ENTRY_POINT_TEXT: {
alternateValueKind = DisplayMode.CALL_ENTRY_POINT;
break;
}
case NATIVE_FUNCTION: {
alternateValueKind = DisplayMode.NATIVE_FUNCTION_TEXT;
break;
}
case NATIVE_FUNCTION_TEXT: {
alternateValueKind = DisplayMode.NATIVE_FUNCTION;
break;
}
case CLASS_ACTOR_ID: {
if (teleClassActor != null) {
alternateValueKind = DisplayMode.CLASS_ACTOR;
}
break;
}
case CLASS_ACTOR: {
alternateValueKind = DisplayMode.CLASS_ACTOR_ID;
break;
}
case CALL_RETURN_POINT: {
alternateValueKind = DisplayMode.CALL_RETURN_POINT_TEXT;
break;
}
case CALL_RETURN_POINT_TEXT: {
alternateValueKind = DisplayMode.CALL_RETURN_POINT;
break;
}
default: {
break;
}
}
if (alternateValueKind != displayMode) {
final DisplayMode newValueKind = alternateValueKind;
return new InspectorAction(inspection(), "Cycle alternate display text") {
@Override
public void procedure() {
Trace.line(TRACE_VALUE, "WVL: " + displayMode.toString() + "->" + newValueKind);
displayMode = newValueKind;
WordValueLabel.this.updateText();
}
};
}
return null;
}
private InspectorAction getCloseAndViewObjectAction(Value value) {
InspectorAction action = null;
final Address valueAsAddress = value.toWord().asAddress();
switch (displayMode) {
case OBJECT_REFERENCE:
case OBJECT_REFERENCE_TEXT:
case HUB_REFERENCE:
case HUB_REFERENCE_TEXT:
case QUASI_OBJECT_REFERENCE:
case QUASI_OBJECT_REFERENCE_TEXT:
case UNCHECKED_REFERENCE:
if (parent instanceof InspectorViewElement) {
final InspectorViewElement viewElement = (InspectorViewElement) parent;
final InspectorView oldView = viewElement.getView();
if (oldView instanceof ObjectView) {
MaxObject newObject = null;
try {
newObject = vm().objects().findAnyObjectAt(valueAsAddress);
if (newObject == null) {
newObject = vm().objects().findForwardedObjectAt(valueAsAddress);
}
} catch (MaxVMBusyException e) {
}
if (newObject != null) {
final MaxObject finalObject = newObject;
action = new InspectorAction(inspection(), null) {
@Override
protected void procedure() {
final ObjectView newView = views().objects().makeView(finalObject);
if (newView != null) {
if (newView == oldView) {
newView.highlight();
} else {
if (!oldView.isPinned()) {
oldView.dispose();
}
}
}
}
};
}
}
}
break;
default:
action = null;
}
return action;
}
private InspectorAction getInspectValueAction(Value value) {
InspectorAction action = null;
final Address valueAsAddress = value.toWord().asAddress();
switch (displayMode) {
case OBJECT_REFERENCE:
case OBJECT_REFERENCE_TEXT:
case QUASI_OBJECT_REFERENCE:
case QUASI_OBJECT_REFERENCE_TEXT:
case UNCHECKED_REFERENCE: {
MaxObject object = null;
try {
object = vm().objects().findAnyObjectAt(valueAsAddress);
} catch (MaxVMBusyException e) {
}
if (object != null) {
action = views().objects().makeViewAction(object, null);
}
break;
}
case HUB_REFERENCE:
case HUB_REFERENCE_TEXT:
MaxObject object = null;
try {
object = vm().objects().findAnyObjectAt(valueAsAddress);
if (object == null) {
object = vm().objects().findForwardedObjectAt(valueAsAddress);
}
} catch (MaxVMBusyException e) {
}
if (object != null) {
action = views().objects().makeViewAction(object, null);
}
break;
case CALL_ENTRY_POINT:
case CALL_ENTRY_POINT_TEXT:
case CALL_RETURN_POINT:
case CALL_RETURN_POINT_TEXT:
case NATIVE_FUNCTION:
case NATIVE_FUNCTION_TEXT:
case UNCHECKED_CALL_POINT: {
try {
final Address address = taggedCodePointer == null ? valueAsAddress : taggedCodePointer.getAddress();
final MaxCodeLocation codeLocation = vm().codeLocations().createMachineCodeLocation(address, "code address from WordValueLabel");
action = new InspectorAction(inspection(), "View Code at address") {
@Override
public void procedure() {
focus().setCodeLocation(codeLocation, true);
}
};
} catch (InvalidCodeAddressException e) {
}
break;
}
case CLASS_ACTOR_ID:
case CLASS_ACTOR: {
final TeleClassActor teleClassActor = vm().classes().findTeleClassActor(value.asWord().asAddress().toInt());
if (teleClassActor != null) {
action = views().objects().makeViewAction(teleClassActor, "View ClassActor");
}
break;
}
case STACK_LOCATION:
case STACK_LOCATION_TEXT:
case THREAD_LOCALS_BLOCK_LOCATION:
case THREAD_LOCALS_BLOCK_LOCATION_TEXT:
case WORD:
case NULL_WORD:
case INVALID_OBJECT_REFERENCE:
case FLAGS:
case DECIMAL:
case FLOAT:
case DOUBLE:
case UNCHECKED_WORD:
case INVALID:
case UNREADABLE: {
// no action
break;
}
}
return action;
}
private InspectorAction getViewMemoryAction(Value value) {
InspectorAction action = null;
if (value != VoidValue.VOID) {
final Address address = value.toWord().asAddress();
switch (displayMode) {
case INVALID_OBJECT_REFERENCE:
case UNCHECKED_REFERENCE:
case STACK_LOCATION:
case STACK_LOCATION_TEXT:
case THREAD_LOCALS_BLOCK_LOCATION:
case THREAD_LOCALS_BLOCK_LOCATION_TEXT:
case CALL_ENTRY_POINT:
case CALL_ENTRY_POINT_TEXT:
case CALL_RETURN_POINT:
case CALL_RETURN_POINT_TEXT:
case NATIVE_FUNCTION:
case NATIVE_FUNCTION_TEXT:
case UNCHECKED_CALL_POINT: {
action = views().memory().makeViewAction(address, null);
break;
}
case OBJECT_REFERENCE:
case OBJECT_REFERENCE_TEXT:
case QUASI_OBJECT_REFERENCE:
case QUASI_OBJECT_REFERENCE_TEXT:
case HUB_REFERENCE:
case HUB_REFERENCE_TEXT: {
if (object != null) {
action = views().memory().makeViewAction(object, "View memory for Object (Shift-Left-Button)");
} else {
action = views().memory().makeViewAction(address, null);
}
break;
}
case WORD:
case NULL_WORD:
case CLASS_ACTOR_ID:
case CLASS_ACTOR:
case FLAGS:
case DECIMAL:
case FLOAT:
case DOUBLE:
case UNCHECKED_WORD:
case INVALID: {
if (address.isNotZero()) {
action = views().memory().makeViewAction(address, null);
}
break;
}
case UNREADABLE:
break;
}
}
return action;
}
private InspectorAction getShowMemoryRegionAction(Value value) {
InspectorAction action = null;
if (value != VoidValue.VOID) {
final Address address = value.toWord().asAddress();
final MaxMemoryRegion memoryRegion = vm().state().findMemoryRegion(address);
if (memoryRegion != null) {
action = actions().selectMemoryRegion(memoryRegion);
}
}
return action;
}
private InspectorAction getShowHeapMarkAction(Value value) {
InspectorAction action = null;
final MaxMarkBitmap markBitMap = vm().heap().markBitmap();
if (value != VoidValue.VOID && markBitMap != null) {
final Address address = value.toWord().asAddress();
if (markBitMap.isCovered(address)) {
final int bitIndex = markBitMap.getBitIndexOf(address);
final MarkColor markColor = markBitMap.getMarkColor(bitIndex);
final StringBuilder sb = new StringBuilder();
sb.append("Show heap mark bit(");
sb.append(bitIndex);
sb.append(")=");
sb.append(markBitMap.isBitSet(bitIndex) ? "1" : "0");
sb.append(", color=");
sb.append(markColor);
action = new InspectorAction(inspection(), sb.toString()) {
@Override
protected void procedure() {
focus().setMarkBitIndex(bitIndex);
focus().setAddress(address);
}
};
}
}
return action;
}
@Override
public Transferable getTransferable() {
Transferable transferable = null;
if (value() != VoidValue.VOID) {
final Address address = value().toWord().asAddress();
switch (displayMode) {
case INVALID_OBJECT_REFERENCE:
case UNCHECKED_REFERENCE:
case STACK_LOCATION:
case STACK_LOCATION_TEXT:
case THREAD_LOCALS_BLOCK_LOCATION:
case THREAD_LOCALS_BLOCK_LOCATION_TEXT:
case CALL_ENTRY_POINT:
case CALL_RETURN_POINT:
case UNCHECKED_CALL_POINT:
case WORD:
case NULL_WORD:
case CLASS_ACTOR_ID:
case CLASS_ACTOR:
case FLAGS:
case DECIMAL:
case FLOAT:
case DOUBLE:
case OBJECT_REFERENCE:
case QUASI_OBJECT_REFERENCE:
case UNCHECKED_WORD:
case INVALID: {
if (vm().state().findMemoryRegion(address) != null) {
transferable = new InspectorTransferable.AddressTransferable(inspection(), address);
}
break;
}
case HUB_REFERENCE:
case HUB_REFERENCE_TEXT:
// TODO (mlvdv) what to transfer in this case?
break;
case OBJECT_REFERENCE_TEXT:
case QUASI_OBJECT_REFERENCE_TEXT: {
if (object != null) {
transferable = new InspectorTransferable.TeleObjectTransferable(inspection(), object);
} else {
transferable = new InspectorTransferable.AddressTransferable(inspection(), address);
}
break;
}
case CALL_ENTRY_POINT_TEXT:
case CALL_RETURN_POINT_TEXT: {
if (compilation != null) {
transferable = new InspectorTransferable.TeleObjectTransferable(inspection(), compilation.representation());
} else {
transferable = new InspectorTransferable.AddressTransferable(inspection(), address);
}
break;
}
case UNREADABLE:
break;
}
}
return transferable;
}
private final class WordValueMenuItems extends InspectorPopupMenuItems {
private final class MenuViewObjectAction extends InspectorAction {
private final InspectorAction inspectAction;
private MenuViewObjectAction(Value value) {
super(inspection(), "View Object (Left-Button)");
inspectAction = getInspectValueAction(value);
setEnabled(inspectAction != null);
}
@Override
public void procedure() {
inspectAction.perform();
}
}
private final class MenuCloseAndViewObjectAction extends InspectorAction {
private InspectorAction inspectAction;
private MenuCloseAndViewObjectAction(Value value) {
super(inspection(), "Close and View Object (Meta-Left-Button)");
inspectAction = getCloseAndViewObjectAction(value);
setEnabled(inspectAction != null);
}
@Override
public void procedure() {
inspectAction.perform();
}
}
private final class MenuCycleDisplayAction extends InspectorAction {
private final InspectorAction cycleAction;
private MenuCycleDisplayAction() {
super(inspection(), "Cycle display (Middle-Button)");
cycleAction = getCycleDisplayTextAction();
setEnabled(cycleAction != null);
}
@Override
public void procedure() {
cycleAction.perform();
}
}
private final class MenuViewMemoryAction extends InspectorAction {
private final InspectorAction viewMemoryAction;
private MenuViewMemoryAction(Value value) {
super(inspection(), "View memory");
viewMemoryAction = getViewMemoryAction(value);
if (viewMemoryAction == null) {
setEnabled(false);
} else {
setName(viewMemoryAction.name());
setEnabled(true);
}
}
@Override
public void procedure() {
viewMemoryAction.perform();
}
}
private final class MenuShowMemoryRegionAction extends InspectorAction {
private final InspectorAction showMemoryRegionAction;
private MenuShowMemoryRegionAction(Value value) {
super(inspection(), "Show memory region");
showMemoryRegionAction = getShowMemoryRegionAction(value);
if (showMemoryRegionAction == null) {
setEnabled(false);
} else {
setEnabled(true);
setName(showMemoryRegionAction.name());
}
}
@Override
public void procedure() {
showMemoryRegionAction.perform();
}
}
private final class MenuShowHeapMarkAction extends InspectorAction {
private final InspectorAction showHeapMarkAction;
private MenuShowHeapMarkAction(Value value) {
super(inspection(), "Show heap bitmap mark for location");
showHeapMarkAction = getShowHeapMarkAction(value);
if (showHeapMarkAction == null) {
setEnabled(false);
} else {
setEnabled(true);
setName(showHeapMarkAction.name());
}
}
@Override
public void procedure() {
showHeapMarkAction.perform();
}
}
public WordValueMenuItems(Inspection inspection, Value value) {
add(actions().copyValue(value, "Copy value to clipboard"));
add(new MenuViewObjectAction(value));
add(new MenuViewMemoryAction(value));
add(new MenuCloseAndViewObjectAction(value));
add(new MenuCycleDisplayAction());
add(new MenuShowHeapMarkAction(value));
add(new MenuShowMemoryRegionAction(value));
}
}
}