Merge remote-tracking branch

'origin/GP-5987_ghidragon_hiding_function_body_in_listing--SQUASHED'
(Closes #4274)
This commit is contained in:
Ryan Kurtz
2025-10-01 13:29:16 -04:00
34 changed files with 1520 additions and 120 deletions

View File

@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -48,6 +48,15 @@ public class DBTraceProgramViewPropertyMapManager implements PropertyMapManager
return map.getAddressSetView(Lifespan.at(program.snap));
}
@Override
public void clear() {
AddressSetView view = getAddressSetView();
AddressRangeIterator addressRanges = view.getAddressRanges();
for (AddressRange range : addressRanges) {
removeRange(range.getMinAddress(), range.getMaxAddress());
}
}
@Override
public boolean intersects(Address start, Address end) {
return getAddressSetView().intersects(start, end);
@@ -62,7 +71,7 @@ public class DBTraceProgramViewPropertyMapManager implements PropertyMapManager
public boolean removeRange(Address start, Address end) {
return map.clear(Lifespan.at(program.snap), new AddressRangeImpl(start, end));
}
@Override
public boolean remove(Address addr) {
return removeRange(addr, addr);

View File

@@ -98,6 +98,7 @@ color.fg.listing.pcode.userop = color.palette.blue
color.fg.listing.stack.depth = color.palette.blue
color.fg.listing.taint = color.fg
color.fg.listing.collapsed = color.palette.gray
color.bg.listing.comparison.bytes = color.palette.lightgreen
color.bg.listing.comparison.mnemonic = color.palette.lightgreen

View File

@@ -654,27 +654,55 @@
of where in the containing structure the popup menu was activated.</P>
</BLOCKQUOTE>
</BLOCKQUOTE>
<H2>Opening/Closing Functions</H2>
<BLOCKQUOTE>
<P>Functions can be open or closed in the listing display. When closed, all the instructions
that make up that function are hidden from view in the listing display. Each consecutive
range of instructions in a function are replaced with a "collapsed code" line to indicate
that some code is hidden and which function the hidden code belongs to. On the function
signature line, there is an open/close control (+/-) that can be used to open or close
a function.</P>
<P>The open/close state for functions is persisted across sessions. Also, navigating to
an address in the body of a closed function will cause that function to be opened.</P>
<H3>Actions for Opening/Closing a Function</H3>
<BLOCKQUOTE>
<P>In addition to the control widget in the display, there are also several actions
that can be used to control the open/close state of a single function or all functions.
</P>
<UL>
<LI><A name="Open_All_Functions"></A><B>Open All Functions</B> - This action will
open all currently closed functions.</LI>
<LI><A name="Close_All_Functions"></A><B>Close All Functions</B> - This action will
close all currently open functions.</LI>
<LI><A name="Toggle_Function"></A><B>Open/Close Functionn</B> - This action will
open a closed function or close an open function.</LI>
</UL>
</BLOCKQUOTE>
</BLOCKQUOTE>
<H2>Opening/Closing Function Variables Display</H2>
<BLOCKQUOTE>
The display of parameters and local variables for a function can be toggled on or off.
Normally, these are displayed just below the function signature, but they can be turned off
to conserve screen space. There is an open/close control (+/-)
on the first parameter/variable line that can be used to toggle them on or off.</P>
on the first parameter/variable line that can be used to toggle them on or off. Note that
unlike the open/close state for functions, the open/close state for variables is not
persistent across sessions for a program. The default state of whether or not to
display variables is a tool state is persisted for the tool, not individual programs.</P>
<H3>Actions for Opening/Closing Function Variables Display</H3>
<BLOCKQUOTE>
<P>In addition to the control widget in the display, there are also several actions that
can be used to control the display of function variables.</P>
<UL>
<LI><A name="Show_All_Variables"></A><B>Show/Hide All Variables</B> - This toggle
<LI><A name="Show_Variables"></A><B>Show/Hide Variables</B> - This action toggles
the display of the variables section for a function. If they are showing, they will be
hidden and if they hare hidden, they will be shown. The default keybinding is
<CODE>Space</CODE> so if you are on a function or variable, pressing the space bar will
toggle the display state.</LI>
<LI><A name="Show_Variables_By_Default"></A><B>Show Variables By Default</B> - This toggle
action can be used to globally control whether or not function variables are displayed.
Individual functions can still be toggled on or off, but this sets the default for all
functions.</LI>
<LI><A name="Show_Variables"></A><B>Show/Hide Variables</B> - This action toggles
the display of the variables. If they are showing, this will turn them off. And if they
are not showing, this action will turn them on. The default keybinding is
<CODE>Space</CODE> so if you are on a function or variable, pressing the space bar will
toggle the display state.</LI>
</UL>
</BLOCKQUOTE>
</BLOCKQUOTE>

View File

@@ -131,6 +131,7 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
private MultiListingLayoutModel multiModel;
private ToggleDockingAction toggleVariablesAction;
private ToggleDockingAction toggleFunctionsAction;
public CodeViewerProvider(CodeBrowserPluginInterface plugin, FormatManager formatMgr,
boolean isConnected) {
@@ -481,24 +482,49 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
action = new GotoNextFunctionAction(tool, plugin.getName());
tool.addAction(action);
new ActionBuilder("Open All Functions", plugin.getName())
.popupMenuPath("Function", "Open All Functions")
.popupMenuGroup("Visibility", "1")
.helpLocation(new HelpLocation("CodeBrowserPlugin", "Open_All_Functions"))
.withContext(ProgramLocationActionContext.class)
.onAction(c -> showAllFunctions(true))
.buildAndInstallLocal(this);
new ActionBuilder("Close All Functions", plugin.getName())
.popupMenuPath("Function", "Close All Functions")
.popupMenuGroup("Visibility", "2")
.helpLocation(new HelpLocation("CodeBrowserPlugin", "Close_All_Functions"))
.withContext(ProgramLocationActionContext.class)
.onAction(c -> showAllFunctions(false))
.buildAndInstallLocal(this);
new ActionBuilder("Toggle Open Function", plugin.getName())
.popupMenuPath("Function", "Open/Close Function")
.popupMenuGroup("Visibility", "3")
.helpLocation(new HelpLocation("CodeBrowserPlugin", "Toggle_Function"))
.keyBinding("SPACE")
.withContext(ProgramLocationActionContext.class)
.validWhen(this::isInCollapsableCodeArea)
.enabledWhen(this::isInCollapsableCodeArea)
.onAction(c -> toggleShowFunction(c))
.buildAndInstallLocal(this);
toggleVariablesAction =
new ToggleActionBuilder("Show All Function Variables", plugin.getName())
.popupMenuPath("Function", "Show/Hide All Variables")
.popupMenuGroup("Variables")
new ToggleActionBuilder("Show Function Variables By Default", plugin.getName())
.popupMenuPath("Function", "Show Variables By Default")
.popupMenuGroup("Visibility", "5")
.helpLocation(new HelpLocation("CodeBrowserPlugin", "Show_All_Variables"))
.selected(true)
.withContext(ProgramLocationActionContext.class)
.onAction(c -> showVariablesForAllFunctions(toggleVariablesAction.isSelected()))
.buildAndInstallLocal(this);
new ActionBuilder("Toggle Show Function Variables", plugin.getName())
new ActionBuilder("Show/Hide Function Variables", plugin.getName())
.popupMenuPath("Function", "Show/Hide Variables")
.popupMenuGroup("Variables")
.popupMenuGroup("Visibility", "4")
.helpLocation(new HelpLocation("CodeBrowserPlugin", "Show_Variables"))
.keyBinding("SPACE")
.withContext(ProgramLocationActionContext.class)
.validWhen(this::isInFunctionArea)
.enabledWhen(this::isInFunctionArea)
.validWhen(this::isInFunctionVariablesArea)
.enabledWhen(this::isInFunctionVariablesArea)
.onAction(c -> toggleShowVariables(c.getAddress()))
.buildAndInstallLocal(this);
@@ -506,6 +532,45 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
}
private void showAllFunctions(boolean selected) {
ListingModel model = listingPanel.getListingModel();
model.setAllFunctionsOpen(selected);
}
private void toggleShowFunction(ProgramLocationActionContext context) {
Address cuAddress = context.getAddress();
Function function = program.getListing().getFunctionContaining(cuAddress);
if (function == null) {
return;
}
ListingModel model = listingPanel.getListingModel();
Address functionAddress = function.getEntryPoint();
boolean open = model.isFunctionOpen(functionAddress);
model.setFunctionOpen(functionAddress, !open);
if (context.getLocation() instanceof FunctionSignatureFieldLocation) {
// no need to move cursor
return;
}
if (!open) {
setLocation(new ProgramLocation(program, cuAddress));
}
else {
// We have to use the CollapsedCodeLocation in this case, any other goto to
// an address that is collapse will open it up, which we don't want since
// we just closed it. Also, we need to correct to the start of the range since
// that is the address that will have the CollapsedField
Address corrected = adjustToStartOfContainingRange(function, cuAddress);
setLocation(new CollapsedCodeLocation(program, corrected));
}
}
private Address adjustToStartOfContainingRange(Function function, Address cuAddress) {
AddressSetView body = function.getBody();
AddressRange range = body.getRangeContaining(cuAddress);
return range == null ? function.getEntryPoint() : range.getMinAddress();
}
private void toggleShowVariables(Address address) {
ListingModel model = listingPanel.getListingModel();
boolean open = model.areFunctionVariablesOpen(address);
@@ -518,12 +583,28 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
model.setAllFunctionVariablesOpen(selected);
}
private boolean isInFunctionArea(ProgramLocationActionContext context) {
private boolean isInFunctionVariablesArea(ProgramLocationActionContext context) {
ProgramLocation location = context.getLocation();
return location instanceof FunctionLocation ||
return location instanceof VariableLocation ||
location instanceof VariablesOpenCloseLocation;
}
private boolean isInCollapsableCodeArea(ProgramLocationActionContext context) {
ProgramLocation location = context.getLocation();
// this allows the code collapse to be toggled on instructions in the body of a function,
// but we have to exclude the variable locations so as to not interfere with the
// open/close variables action which also is mapped to <SPACE>
if (location instanceof CodeUnitLocation && !(location instanceof VariableLocation) &&
!(location instanceof VariablesOpenCloseLocation)) {
return true;
}
return location instanceof FunctionSignatureFieldLocation ||
location instanceof FunctionOpenCloseLocation ||
location instanceof CollapsedCodeLocation;
}
private void buildQuickTogleFieldActions() {
List<String> quickToggleFieldNames = formatMgr.getQuickToggleFieldNames();
int count = 0;

View File

@@ -19,6 +19,7 @@ import java.awt.*;
import docking.widgets.fieldpanel.support.*;
import generic.theme.GIcon;
import generic.theme.Gui;
import ghidra.app.util.viewer.proxy.EmptyProxy;
import ghidra.app.util.viewer.proxy.ProxyObj;
@@ -26,6 +27,7 @@ import ghidra.app.util.viewer.proxy.ProxyObj;
* FactoryField class for displaying the open/close field.
*/
public abstract class AbstractOpenCloseField implements ListingField {
protected static final Font HIDDEN_FONT = Gui.getFont("font.listing.base.hidden.field");
protected static final GIcon OPEN_ICON =
new GIcon("icon.base.util.viewer.fieldfactory.openclose.open");
protected static final GIcon CLOSED_ICON =

View File

@@ -0,0 +1,107 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.viewer.field;
import java.math.BigInteger;
import docking.widgets.fieldpanel.field.*;
import docking.widgets.fieldpanel.support.FieldLocation;
import ghidra.app.util.ListingHighlightProvider;
import ghidra.app.util.viewer.format.FieldFormatModel;
import ghidra.app.util.viewer.listingpanel.ListingModel;
import ghidra.app.util.viewer.proxy.CodeUnitProxy;
import ghidra.app.util.viewer.proxy.ProxyObj;
import ghidra.framework.options.Options;
import ghidra.framework.options.ToolOptions;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.*;
import ghidra.program.util.CollapsedCodeLocation;
import ghidra.program.util.ProgramLocation;
/**
* Generates field to indicate collapsed function for areas of hidden code.
*/
public class CollapsedCodeFieldFactory extends FieldFactory {
public static final String FIELD_NAME = "Collapsed Code";
public CollapsedCodeFieldFactory() {
super(FIELD_NAME);
}
/**
* Constructor
* @param model the model that the field belongs to.
* @param hlProvider the HightLightStringProvider.
* @param displayOptions the Options for display properties.
* @param fieldOptions the Options for field specific properties.
*/
private CollapsedCodeFieldFactory(FieldFormatModel model, ListingHighlightProvider hlProvider,
Options displayOptions, Options fieldOptions) {
super(FIELD_NAME, model, hlProvider, displayOptions, fieldOptions);
}
@Override
public ListingField getField(ProxyObj<?> proxy, int varWidth) {
if (!enabled || !(proxy instanceof CodeUnitProxy cuProxy)) {
return null;
}
CodeUnit cu = cuProxy.getObject();
Address address = cu.getAddress();
Function function = cu.getProgram().getListing().getFunctionContaining(address);
if (function == null) {
return null;
}
String text = "<Collapsed: " + function.getName() + "()>";
AttributedString s = new AttributedString(text, ListingColors.COLLAPSED_CODE, getMetrics());
FieldElement element = new TextFieldElement(s, 0, 0);
return ListingTextField.createSingleLineTextField(this, cuProxy, element, startX + varWidth,
width, hlProvider);
}
@Override
public ProgramLocation getProgramLocation(int row, int col, ListingField bf) {
Object obj = bf.getProxy().getObject();
if (obj instanceof CodeUnit cu) {
Address address = cu.getAddress();
ListingModel layoutModel = bf.getProxy().getListingLayoutModel();
Program program = layoutModel.getProgram();
return new CollapsedCodeLocation(program, address);
}
return null;
}
@Override
public FieldLocation getFieldLocation(ListingField bf, BigInteger index, int fieldNum,
ProgramLocation programLoc) {
if (programLoc instanceof CollapsedCodeLocation) {
return new FieldLocation(index, fieldNum, 0, 0);
}
return null;
}
@Override
public boolean acceptsType(int category, Class<?> proxyObjectClass) {
// this factory only appears in the header DIVIDER tab (which is called "Address Break" in
// the gui)
return category == FieldFormatModel.DIVIDER;
}
@Override
public FieldFactory newInstance(FieldFormatModel formatModel, ListingHighlightProvider provider,
ToolOptions displayOptions, ToolOptions fieldOptions) {
return new CollapsedCodeFieldFactory(formatModel, provider, displayOptions, fieldOptions);
}
}

View File

@@ -0,0 +1,69 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.viewer.field;
import java.awt.event.MouseEvent;
import ghidra.app.nav.Navigatable;
import ghidra.app.services.GoToService;
import ghidra.framework.plugintool.ServiceProvider;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Program;
import ghidra.program.util.*;
/**
* A handler to process {@link CollapsedCodeLocation} mouse clicks.
*/
public class CollapsedCodeFieldMouseHandler implements FieldMouseHandlerExtension {
private final static Class<?>[] SUPPORTED_CLASSES = new Class[] {
CollapsedCodeLocation.class };
@Override
public boolean fieldElementClicked(Object clickedObject, Navigatable navigatable,
ProgramLocation location, MouseEvent mouseEvent, ServiceProvider serviceProvider) {
if (mouseEvent.getClickCount() != 2 || mouseEvent.getButton() != MouseEvent.BUTTON1) {
return false;
}
GoToService goToService = serviceProvider.getService(GoToService.class);
if (goToService == null) {
return false;
}
Program program = location.getProgram();
if (location instanceof CollapsedCodeLocation loc) {
Address address = loc.getAddress();
Function function = program.getListing().getFunctionContaining(address);
if (function != null) {
FunctionSignatureFieldLocation gotoLocation =
new FunctionSignatureFieldLocation(program, function.getEntryPoint());
goToService.goTo(navigatable, gotoLocation, program);
return true;
}
}
return false;
}
@Override
public Class<?>[] getSupportedProgramLocations() {
return SUPPORTED_CLASSES;
}
}

View File

@@ -0,0 +1,93 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.viewer.field;
import java.awt.*;
import javax.swing.JComponent;
import docking.widgets.fieldpanel.internal.FieldBackgroundColorManager;
import docking.widgets.fieldpanel.internal.PaintContext;
import docking.widgets.fieldpanel.support.RowColLocation;
import ghidra.app.util.viewer.proxy.FunctionProxy;
import ghidra.app.util.viewer.proxy.ProxyObj;
import ghidra.program.model.address.Address;
/**
* Field class for displaying the open/close field widget for function variables.
*/
public class FunctionOpenCloseField extends AbstractOpenCloseField {
/**
* Constructor
* @param factory the FieldFactory that created this field.
* @param proxy the object associated with this field.
* @param metrics the FontMetrics used to render this field.
* @param x the starting x position of this field.
* @param width the width of this field.
*/
public FunctionOpenCloseField(FieldFactory factory, ProxyObj<?> proxy,
FontMetrics metrics, int x, int width) {
super(factory, proxy, metrics, x, width);
if (proxy instanceof FunctionProxy functionProxy) {
Address functionAddress = functionProxy.getFunctionAddress();
this.isOpen = proxy.getListingLayoutModel().isFunctionOpen(functionAddress);
}
}
@Override
public int getWidth() {
return fieldWidth;
}
@Override
public void paint(JComponent c, Graphics g, PaintContext context,
Rectangle clip, FieldBackgroundColorManager map, RowColLocation cursorLoc,
int rowHeight) {
// center in the heightAbove area (negative, since 0 is the baseline of text, which is at
// the bottom of the heightAbove)
int toggleHandleStartY = -((heightAbove / 2) + (toggleHandleSize / 2));
int toggleHandleStartX = startX;
// If we're in printing mode, trying to render these open/close images
// causes the JVM to bomb. We'd like to eventually figure out why but in
// the meantime we can safely comment this out and still generate an acceptable
// image.
if (!context.isPrinting()) {
if (isOpen) {
g.drawImage(OPEN_ICON.getImageIcon().getImage(), toggleHandleStartX,
toggleHandleStartY, context.getBackground(), null);
}
else {
g.drawImage(CLOSED_ICON.getImageIcon().getImage(), toggleHandleStartX,
toggleHandleStartY, context.getBackground(), null);
}
}
paintCursor(g, context.getCursorColor(), cursorLoc);
}
/**
* Toggles the open state of this field.
*/
@Override
public void toggleOpenCloseState() {
if (proxy instanceof FunctionProxy functionProxy) {
Address functionAddress = functionProxy.getFunctionAddress();
proxy.getListingLayoutModel().setFunctionOpen(functionAddress, !isOpen);
}
}
}

View File

@@ -0,0 +1,110 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.viewer.field;
import java.math.BigInteger;
import docking.widgets.fieldpanel.support.FieldLocation;
import ghidra.app.util.ListingHighlightProvider;
import ghidra.app.util.viewer.format.FieldFormatModel;
import ghidra.app.util.viewer.proxy.FunctionProxy;
import ghidra.app.util.viewer.proxy.ProxyObj;
import ghidra.framework.options.Options;
import ghidra.framework.options.ToolOptions;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Program;
import ghidra.program.util.FunctionOpenCloseLocation;
import ghidra.program.util.ProgramLocation;
/**
* Generates Open/Close Fields for variables under functions.
*/
public class FunctionOpenCloseFieldFactory extends FieldFactory {
public static final String FIELD_NAME = "+";
public FunctionOpenCloseFieldFactory() {
super(FIELD_NAME);
}
/**
* Constructor
* @param model the model that the field belongs to.
* @param hlProvider the HightLightStringProvider.
* @param displayOptions the Options for display properties.
* @param fieldOptions the Options for field specific properties.
*/
private FunctionOpenCloseFieldFactory(FieldFormatModel model,
ListingHighlightProvider hlProvider,
Options displayOptions, Options fieldOptions) {
super(FIELD_NAME, model, hlProvider, displayOptions, fieldOptions);
servicesChanged();
}
/**
* Returns the FactoryField for the given object at index index.
* @param varWidth the amount of variable width spacing for any fields
* before this one.
* @param proxy the object whose properties should be displayed.
*/
@Override
public ListingField getField(ProxyObj<?> proxy, int varWidth) {
if (!enabled) {
return null;
}
if (proxy instanceof FunctionProxy) {
return new FunctionOpenCloseField(this, proxy, getMetrics(), startX + varWidth, width);
}
return null;
}
@Override
public ProgramLocation getProgramLocation(int row, int col, ListingField bf) {
ProxyObj<?> proxy = bf.getProxy();
if (proxy instanceof FunctionProxy functionProxy) {
Program program = functionProxy.getProgram();
Address functionAddress = functionProxy.getFunctionAddress();
return new FunctionOpenCloseLocation(program, functionAddress);
}
return null;
}
@Override
public FieldLocation getFieldLocation(ListingField bf, BigInteger index, int fieldNum,
ProgramLocation programLoc) {
if (!(programLoc instanceof FunctionOpenCloseLocation)) {
return null;
}
return new FieldLocation(index, fieldNum, 0, 0);
}
@Override
public boolean acceptsType(int category, Class<?> proxyObjectClass) {
if (!Function.class.isAssignableFrom(proxyObjectClass)) {
return false;
}
return (category == FieldFormatModel.FUNCTION);
}
@Override
public FieldFactory newInstance(FieldFormatModel fieldModel, ListingHighlightProvider provider,
ToolOptions displayOptions, ToolOptions fieldOptions) {
return new FunctionOpenCloseFieldFactory(fieldModel, provider, displayOptions,
fieldOptions);
}
}

View File

@@ -38,6 +38,7 @@ public class ListingColors {
public static final GColor EXT_REF_RESOLVED = new GColor("color.fg.listing.ext.ref.resolved");
public static final GColor REGISTER = new GColor("color.fg.listing.register");
public static final GColor PARALLEL_INSTRUCTION = new GColor("color.fg.listing.instruction.parallel");
public static final GColor COLLAPSED_CODE = new GColor("color.fg.listing.collapsed");
public static class XrefColors {

View File

@@ -21,9 +21,13 @@ import ghidra.app.nav.Navigatable;
import ghidra.framework.plugintool.ServiceProvider;
import ghidra.program.util.ProgramLocation;
/**
* Mouse handlers for open/close widgets
*/
public class OpenCloseFieldMouseHandler implements FieldMouseHandlerExtension {
private final static Class<?>[] SUPPORTED_CLASSES =
new Class[] { OpenCloseField.class, VariableOpenCloseField.class };
new Class[] { OpenCloseField.class, VariableOpenCloseField.class,
FunctionOpenCloseField.class };
@Override
public boolean fieldElementClicked(Object clickedObject, Navigatable sourceNavigatable,

View File

@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -22,6 +22,7 @@ import docking.widgets.fieldpanel.support.FieldLocation;
import ghidra.app.util.ListingHighlightProvider;
import ghidra.app.util.viewer.format.FieldFormatModel;
import ghidra.app.util.viewer.listingpanel.ListingModel;
import ghidra.app.util.viewer.proxy.AddressProxy;
import ghidra.app.util.viewer.proxy.ProxyObj;
import ghidra.framework.options.Options;
import ghidra.framework.options.ToolOptions;
@@ -65,6 +66,9 @@ public class SeparatorFieldFactory extends FieldFactory {
if (!enabled) {
return null;
}
if (!(proxy instanceof AddressProxy)) {
return null;
}
int numChars = width / getMetrics().charWidth(sepChar);
sb.setLength(0);
for (int i = 0; i < numChars; i++) {

View File

@@ -23,7 +23,6 @@ import docking.widgets.fieldpanel.internal.FieldBackgroundColorManager;
import docking.widgets.fieldpanel.internal.PaintContext;
import docking.widgets.fieldpanel.support.RowColLocation;
import generic.theme.GThemeDefaults.Colors.Palette;
import generic.theme.Gui;
import ghidra.app.util.viewer.proxy.ProxyObj;
import ghidra.app.util.viewer.proxy.VariableProxy;
import ghidra.program.model.address.Address;
@@ -32,7 +31,6 @@ import ghidra.program.model.address.Address;
* FactoryField class for displaying the open/close field widget for function variables.
*/
public class VariableOpenCloseField extends AbstractOpenCloseField {
private static final Font HIDDEN_FONT = Gui.getFont("font.listing.base.hidden.field");
/**
* Constructor

View File

@@ -384,15 +384,26 @@ public class FormatManager implements OptionsChangeListener {
Element rowElem = new Element("ROW");
Element colElem = new Element("FIELD");
colElem.setAttribute("NAME", "Separator");
colElem.setAttribute("WIDTH", "80");
colElem.setAttribute("ENABLED", "true");
rowElem.addContent(colElem);
root.addContent(rowElem);
return root;
rowElem = new Element("ROW");
colElem = new Element("FIELD");
colElem.setAttribute("WIDTH", "150");
colElem.setAttribute("ENABLED", "true");
rowElem.addContent(colElem);
colElem = new Element("FIELD");
colElem.setAttribute("NAME", "Collapsed Code");
colElem.setAttribute("WIDTH", "200");
colElem.setAttribute("ENABLED", "true");
rowElem.addContent(colElem);
root.addContent(rowElem);
return root;
}
private Element getDefaultPlateFormat() {
@@ -434,6 +445,12 @@ public class FormatManager implements OptionsChangeListener {
Element rowElem = new Element("ROW");
Element colElem = new Element("FIELD");
colElem.setAttribute("NAME", "+");
colElem.setAttribute("WIDTH", "20");
colElem.setAttribute("ENABLED", "true");
rowElem.addContent(colElem);
colElem = new Element("FIELD");
colElem.setAttribute("WIDTH", "200");
colElem.setAttribute("ENABLED", "true");
rowElem.addContent(colElem);
@@ -961,7 +978,7 @@ public class FormatManager implements OptionsChangeListener {
models[i].restoreFromXml(saveState.getXmlElement(models[i].getName()));
// hack to make sure the new open/close variables field is present
// If missing, we are just going to reset it to the default format
checkForMissingOpenCloseField(models[i]);
checkForMissingNewCriticalFields(models[i]);
}
else {
models[i].restoreFromXml(getDefaultModel(i));
@@ -971,21 +988,30 @@ public class FormatManager implements OptionsChangeListener {
modelChanged(null);
}
// This is a hack to make sure the new variables open/close field is present.
// This was added in version 12.0 and can probably be removed in a few releases.
private void checkForMissingOpenCloseField(FieldFormatModel model) {
if (!model.getName().equals("Variable")) {
return;
// This is a hack to make sure the new variables and functions open/close field is present.
// This was added in version 12.0/12.1 and can probably be removed in a few releases.
private void checkForMissingNewCriticalFields(FieldFormatModel model) {
if (model.getName().equals("Variable")) {
if (!hasField(model, "+")) {
model.restoreFromXml(getDefaultVariableFormat());
}
}
if (!hasField(model, "+")) {
model.restoreFromXml(getDefaultVariableFormat());
if (model.getName().equals("Function")) {
if (!hasField(model, "+")) {
model.restoreFromXml(getDefaultFunctionFormat());
}
}
if (model.getName().equals("Address Break")) {
if (!hasField(model, "Collapsed Code")) {
model.restoreFromXml(getDefaultDividerFormat());
}
}
}
private boolean hasField(FieldFormatModel model, String fieldName) {
FieldFactory[] unusedFactories = model.getUnusedFactories();
for (FieldFactory fieldFactory : unusedFactories) {
if (fieldFactory.getFieldName().equals("+")) {
if (fieldFactory.getFieldName().equals(fieldName)) {
return false;
}
}

View File

@@ -142,4 +142,19 @@ public class EmptyListingModel implements ListingModel {
public ListingModel copy() {
return new EmptyListingModel();
}
@Override
public boolean isFunctionOpen(Address functionAddress) {
return false;
}
@Override
public void setFunctionOpen(Address functionAddress, boolean b) {
// stub
}
@Override
public void setAllFunctionsOpen(boolean selected) {
// stub
}
}

View File

@@ -38,12 +38,39 @@ public interface ListingModel {
FUNCTION_POINTER_OPTION_GROUP_NAME + Options.DELIMITER +
"Display Non-External Function Pointer Header";
/**
* {@return the address set of all addresses in the model}
*/
public AddressSetView getAddressSet();
/**
* Returns the next address that has displayable information after the given address. This
* allows the listing to efficiently skip over large sections of undisplayable addresses such
* as those consumed by large data or addresses part of a closed function.
* @param address the address from which to find the next address with displayable information
* @return the next address with displayable information
*/
public Address getAddressAfter(Address address);
/**
* Returns the previous address that has displayable information before the given address. This
* allows the listing to efficiently skip over large sections of undisplayable addresses such
* as those consumed by large data or addresses part of a closed function.
* @param address the address from which to find the previous address with displayable
* information
* @return the previous address with displayable information
*/
public Address getAddressBefore(Address address);
/**
* Returns a layout with displayable information for the given address.
* @param address the address to get displayable information
* @param isGapAddress true implies there is a gap of missing addresses before this address.
* Note that this is different from addresses that are hidden due to collapsed functions or
* closed data. These gaps are not even in consideration to display information such as
* undefined memory or a fragmented program view.
* @return a Layout with information to be displayed for the given address.
*/
public Layout getLayout(Address address, boolean isGapAddress);
/**
@@ -181,4 +208,24 @@ public interface ListingModel {
* @return a copy of this model.
*/
public ListingModel copy();
/**
* Checks if the function at the given entry point is open or not.
* @param functionAddress the entry point of the function to check for open
* @return true if the function is open; false otherwise
*/
public boolean isFunctionOpen(Address functionAddress);
/**
* Sets the function at the given address to be open or not
* @param functionAddress the entry point of the function to set open or closed
* @param open true to open the function, false to close it
*/
public void setFunctionOpen(Address functionAddress, boolean open);
/**
* Sets all functions to open or closed.
* @param open if true, opens all function; otherwise closes all functions
*/
public void setAllFunctionsOpen(boolean open);
}

View File

@@ -679,7 +679,7 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
return null;
}
openDataAsNeeded(loc);
openDataOrFunctionAsNeeded(loc);
FieldLocation floc = layoutModel.getFieldLocation(loc);
if (floc != null) {
@@ -719,15 +719,40 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
return layoutModel.getFieldLocation(new ProgramLocation(program, address));
}
private void openDataAsNeeded(ProgramLocation location) {
private void openDataOrFunctionAsNeeded(ProgramLocation location) {
if (location instanceof CollapsedCodeLocation) {
return;
}
Address address = location.getByteAddress();
Program program = getProgram();
CodeUnit cu = program.getListing().getCodeUnitContaining(address);
if (!(cu instanceof Data)) {
return;
if (cu instanceof Data data) {
openData(data, address);
}
else if (cu instanceof Instruction instruction) {
openFunction(instruction);
}
Data data = (Data) cu;
}
private void openFunction(Instruction instruction) {
Address address = instruction.getMinAddress();
Program program = instruction.getProgram();
Function function = program.getFunctionManager().getFunctionContaining(address);
if (function == null) {
return;
}
Address functionAddress = function.getEntryPoint();
// don't auto-open entry point addresses
if (address.equals(functionAddress)) {
return;
}
if (!listingModel.isFunctionOpen(functionAddress)) {
listingModel.setFunctionOpen(functionAddress, true);
}
}
private void openData(Data data, Address address) {
if (data.getComponent(0) == null) {
// not sub data to open
return;
@@ -741,6 +766,7 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
if (openAllData(subData)) {
layoutModel.dataChanged(true);
}
}
private FieldLocation getFieldLocationForDataAndOpenAsNeeded(Data data, Address address) {

View File

@@ -43,7 +43,7 @@ public class ProgramBigListingModel implements ListingModel, FormatModelListener
DomainObjectListener, ChangeListener, OptionsChangeListener {
protected final Program program;
private ProgramOpenCloseManager openCloseMgr = new ProgramOpenCloseManager();
private ProgramOpenCloseManager openCloseMgr;
private FormatManager formatMgr;
private ToolOptions fieldOptions;
private boolean showExternalFunctionPointerFormat;
@@ -59,6 +59,7 @@ public class ProgramBigListingModel implements ListingModel, FormatModelListener
this.program = program;
this.listing = program.getListing();
this.formatMgr = formatMgr;
openCloseMgr = new ProgramOpenCloseManager(program);
dummyFactory = new DummyFieldFactory(formatMgr);
formatMgr.addFormatModelListener(this);
program.addListener(this);
@@ -160,33 +161,43 @@ public class ProgramBigListingModel implements ListingModel, FormatModelListener
addOpenData(dataList, data, addr);
addUnionPostOpenData(dataList, data, addr);
}
boolean isClosedFunctionAddress = isClosedFunctionAddress(function, addr);
if (isGapAddress) {
format = formatMgr.getDividerModel();
format.addLayouts(list, 0, new AddressProxy(this, addr));
}
if (cu != null) {
if (cu != null && function != null) {
format = formatMgr.getPlateFormat();
format.addLayouts(list, 0, new CodeUnitProxy(this, program, cu));
}
if (function != null) {
format = formatMgr.getFunctionFormat();
format.addLayouts(list, 0, new FunctionProxy(this, program, addr, function));
format = formatMgr.getFunctionVarFormat();
boolean variablesOpen = openCloseMgr.isFunctionVariablesOpen(function.getEntryPoint());
if (variablesOpen) {
addReturn(addr, list, format, function);
addParameters(addr, list, format, function);
addLocals(addr, list, format, function);
if (!isClosedFunctionAddress) {
format = formatMgr.getFunctionVarFormat();
boolean variablesOpen =
openCloseMgr.isFunctionVariablesOpen(function.getEntryPoint());
if (variablesOpen) {
addReturn(addr, list, format, function);
addParameters(addr, list, format, function);
addLocals(addr, list, format, function);
}
else {
format.addLayouts(list, 0,
new ClosedVariableProxy(this, program, addr, function));
}
}
else {
format.addLayouts(list, 0, new ClosedVariableProxy(this, program, addr, function));
}
}
if (cu != null) {
format = formatMgr.getCodeUnitFormat();
format.addLayouts(list, 0, new CodeUnitProxy(this, program, cu));
if (isClosedFunctionAddress) {
format = formatMgr.getDividerModel();
}
else {
format = formatMgr.getCodeUnitFormat();
}
CodeUnitProxy proxy = new CodeUnitProxy(this, program, cu);
format.addLayouts(list, 0, proxy);
}
if (dataList != null) {
@@ -213,6 +224,16 @@ public class ProgramBigListingModel implements ListingModel, FormatModelListener
}
private boolean isClosedFunctionAddress(Function function, Address addr) {
if (function == null) {
function = listing.getFunctionContaining(addr);
}
if (function == null) {
return false;
}
return !openCloseMgr.isFunctionOpen(function.getEntryPoint());
}
private void addReturn(Address addr, List<RowLayout> list, FieldFormatModel format,
Function function) {
format.addLayouts(list, 0,
@@ -257,6 +278,35 @@ public class ProgramBigListingModel implements ListingModel, FormatModelListener
return formatMgr.getMaxWidth();
}
@Override
public Address getAddressBefore(Address addr) {
CodeUnit cu = listing.getCodeUnitContaining(addr);
if (cu instanceof Data && !addr.equals(cu.getMinAddress()) && isOpenData(cu)) {
Address prevAddr = findOpenDataBefore(addr, (Data) cu);
if (prevAddr != null) {
return prevAddr;
}
return cu.getMinAddress();
}
cu = listing.getCodeUnitBefore(addr);
if (cu == null) {
return null;
}
if (isOpenData(cu)) {
return cu.getMaxAddress();
}
if (!isInClosedFunction(cu)) {
return cu.getAddress();
}
Address firstAddress = getFirstAddressInClosedFunctionRange(cu);
cu = listing.getCodeUnitAt(firstAddress);
if (!isInClosedFunction(cu)) {
return cu.getMinAddress();
}
return getAddressBefore(firstAddress);
}
@Override
public Address getAddressAfter(Address address) {
CodeUnit cu = listing.getCodeUnitContaining(address);
@@ -272,7 +322,51 @@ public class ProgramBigListingModel implements ListingModel, FormatModelListener
}
}
cu = listing.getCodeUnitAfter(address);
return cu == null ? null : cu.getMinAddress();
if (cu == null) {
return null;
}
if (!isInClosedFunction(cu)) {
return cu.getAddress();
}
Address lastAddress = getLastAddressInClosedFunctionRange(cu);
return getAddressAfter(lastAddress);
}
private Address getLastAddressInClosedFunctionRange(CodeUnit cu) {
Address address = cu.getAddress();
Function f = listing.getFunctionContaining(address);
AddressSetView body = f.getBody();
AddressRange range = body.getRangeContaining(address);
return range.getMaxAddress();
}
private Address getFirstAddressInClosedFunctionRange(CodeUnit cu) {
Address address = cu.getAddress();
Function f = listing.getFunctionContaining(address);
AddressSetView body = f.getBody();
AddressRange range = body.getRangeContaining(address);
return range.getMinAddress();
}
private boolean isInClosedFunction(CodeUnit cu) {
Address a = cu.getAddress();
Function f = listing.getFunctionContaining(a);
if (f == null) {
return false;
}
Address entryPoint = f.getEntryPoint();
if (a.equals(entryPoint)) {
return false;
}
AddressSetView body = f.getBody();
AddressRange rangeContaining = body.getRangeContaining(a);
if (rangeContaining.getMinAddress().equals(a)) {
return false;
}
if (openCloseMgr.isFunctionOpen(entryPoint)) {
return false;
}
return true;
}
private Address findOpenDataAfter(Address address, Data parent) {
@@ -344,25 +438,6 @@ public class ProgramBigListingModel implements ListingModel, FormatModelListener
return null;
}
@Override
public Address getAddressBefore(Address addr) {
CodeUnit cu = listing.getCodeUnitContaining(addr);
if (cu == null || addr.equals(cu.getMinAddress())) {
cu = listing.getCodeUnitBefore(addr);
if (isOpenData(cu)) {
return cu.getMaxAddress();
}
return cu == null ? null : cu.getMinAddress();
}
if (isOpenData(cu)) {
Address prevAddr = findOpenDataBefore(addr, (Data) cu);
if (prevAddr != null) {
return prevAddr;
}
}
return cu.getMinAddress();
}
public boolean isOpenData(CodeUnit cu) {
if (cu instanceof Data) {
Data data = (Data) cu;
@@ -491,8 +566,8 @@ public class ProgramBigListingModel implements ListingModel, FormatModelListener
}
@Override
public void setFunctionVariablesOpen(Address functionAddress, boolean open) {
openCloseMgr.setFunctionVariablesOpen(functionAddress, open);
public void setFunctionVariablesOpen(Address functionEntry, boolean open) {
openCloseMgr.setFunctionVariablesOpen(functionEntry, open);
}
@Override
@@ -501,8 +576,23 @@ public class ProgramBigListingModel implements ListingModel, FormatModelListener
}
@Override
public boolean areFunctionVariablesOpen(Address FunctionAddress) {
return openCloseMgr.isFunctionVariablesOpen(FunctionAddress);
public boolean areFunctionVariablesOpen(Address functionEntry) {
return openCloseMgr.isFunctionVariablesOpen(functionEntry);
}
@Override
public boolean isFunctionOpen(Address functionEntry) {
return openCloseMgr.isFunctionOpen(functionEntry);
}
@Override
public void setFunctionOpen(Address functionEntry, boolean b) {
openCloseMgr.setFunctionOpen(functionEntry, b);
}
@Override
public void setAllFunctionsOpen(boolean selected) {
openCloseMgr.setAllFunctionsOpen(selected);
}
@Override
@@ -647,4 +737,5 @@ public class ProgramBigListingModel implements ListingModel, FormatModelListener
model.openCloseMgr = openCloseMgr;
return model;
}
}

View File

@@ -212,4 +212,19 @@ public class ListingModelConverter implements ListingModel {
return new ListingModelConverter(primaryModel.copy(), model.copy());
}
@Override
public boolean isFunctionOpen(Address functionAddress) {
return model.isFunctionOpen(functionAddress);
}
@Override
public void setFunctionOpen(Address functionAddress, boolean b) {
model.setFunctionOpen(functionAddress, b);
}
@Override
public void setAllFunctionsOpen(boolean selected) {
model.setAllFunctionsOpen(selected);
}
}

View File

@@ -352,6 +352,21 @@ public class MultiListingLayoutModel implements ListingModelListener, FormatMode
return models[modelID].copy();
}
@Override
public boolean isFunctionOpen(Address functionAddress) {
return models[modelID].isFunctionOpen(functionAddress);
}
@Override
public void setFunctionOpen(Address functionAddress, boolean b) {
models[modelID].setFunctionOpen(functionAddress, b);
}
@Override
public void setAllFunctionsOpen(boolean selected) {
models[modelID].setAllFunctionsOpen(selected);
}
}
@Override

View File

@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -61,7 +61,8 @@ public class ListingDisplayOptionsEditor implements OptionsEditor {
options.registerThemeColorBinding(colorOptionName, element.getThemeColorId(), null,
prefix + colorOptionName);
String styleOptionName = element.getStyleOptionName();
options.registerOption(styleOptionName, -1, null, prefix + styleOptionName);
int style = element.getStyle();
options.registerOption(styleOptionName, style, null, prefix + styleOptionName);
}
}

View File

@@ -83,6 +83,7 @@ public class OptionsGui extends JPanel {
public static final ScreenElement FLOW_ARROW_SELECTED = new ScreenElement("Flow Arrow, Selected", FlowArrowColors.SELECTED);
public static final ScreenElement LABELS_PRIMARY = new ScreenElement("Labels, Primary", LabelColors.PRIMARY);
public static final ScreenElement LABELS_NON_PRIMARY = new ScreenElement("Labels, Non-primary", LabelColors.NON_PRIMARY);
public static final ScreenElement COLLAPSED_CODE = new ScreenElement("Collapsed Code", ListingColors.COLLAPSED_CODE, Font.ITALIC);
public static final ScreenElement COMMENT_PLATE = new ScreenElement("Comment, Plate", "Plate Comment", CommentColors.PLATE);
public static final ScreenElement COMMENT_POST = new ScreenElement("Comment, Post", "Post-Comment", CommentColors.POST);
public static final ScreenElement COMMENT_PRE = new ScreenElement("Comment, Pre", "Pre-Comment", CommentColors.PRE);
@@ -99,10 +100,9 @@ public class OptionsGui extends JPanel {
public static final ScreenElement PCODE_ADDR_SPACE = new ScreenElement("P-code Address Space", PcodeColors.ADDRESS_SPACE);
public static final ScreenElement PCODE_RAW_VARNODE = new ScreenElement("P-code Raw Varnode", PcodeColors.VARNODE);
public static final ScreenElement PCODE_USEROP = new ScreenElement("P-code Userop", PcodeColors.USEROP);
static ScreenElement[] elements = {
ADDRESS,
BACKGROUND, BAD_REF_ADDR, BYTES,
BACKGROUND, BAD_REF_ADDR, BYTES, COLLAPSED_CODE,
COMMENT_AUTO, COMMENT_EOL, COMMENT_PLATE, COMMENT_POST, COMMENT_PRE, COMMENT_REPEATABLE,
COMMENT_REF_REPEAT, CONSTANT,
ENTRY_POINT, EXT_REF_RESOLVED, EXT_REF_UNRESOLVED,
@@ -716,6 +716,26 @@ public class OptionsGui extends JPanel {
lb.add(" ", null);
list.add(lb.getLayout());
lb = new LayoutBuilder(1);
lb.add(" /***********************************/", COMMENT_PLATE);
list.add(lb.getLayout());
lb = new LayoutBuilder(1);
lb.add(" /* PLATE */", COMMENT_PLATE);
list.add(lb.getLayout());
lb = new LayoutBuilder(1);
lb.add(" /***********************************/", COMMENT_PLATE);
list.add(lb.getLayout());
lb = new LayoutBuilder(4);
lb.add(" ", null);
lb.add("Word ", FUN_RET_TYPE);
lb.add("AnotherFunc", FUN_NAME);
lb.add("(", SEPARATOR);
lb.add(")", SEPARATOR);
list.add(lb.getLayout());
lb = new LayoutBuilder(1);
lb.add("<Collapsed Code: AnotherFunc>", COLLAPSED_CODE);
list.add(lb.getLayout());
layouts = new Layout[list.size()];
list.toArray(layouts);

View File

@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -27,15 +27,23 @@ public class ScreenElement {
private int style;
ScreenElement(String name, GColor defaultColor) {
this(name, name, defaultColor);
this(name, name, defaultColor, -1);
}
ScreenElement(String name, GColor defaultColor, int style) {
this(name, name, defaultColor, style);
}
ScreenElement(String name, String optionsName, GColor defaultColor) {
this(name, optionsName, defaultColor, -1);
}
ScreenElement(String name, String optionsName, GColor defaultColor, int style) {
this.name = name;
this.optionsName = optionsName;
this.defaultColor = defaultColor;
this.color = defaultColor;
this.style = -1;
this.style = style;
}
public String getThemeColorId() {

View File

@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -109,4 +109,8 @@ public class FunctionProxy extends ProxyObj<Function> {
}
return f.getBody().contains(a);
}
public Program getProgram() {
return program;
}
}

View File

@@ -25,7 +25,7 @@ import ghidra.program.model.address.Address;
* state can be set and then a set of address is kept for the locations that are the opposite
* of the default.
*/
public class AddressBasedOpenCloseManager {
public class InMemoryOpenCloseManager {
private boolean openByDefault = true;
private Set<Address> addresses = new HashSet<>();

View File

@@ -0,0 +1,161 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.viewer.util;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Program;
import ghidra.program.model.listing.ProgramUserData;
import ghidra.program.model.util.VoidPropertyMap;
/**
* Address based open/close management that uses the {@link ProgramUserData} to persist the
* open/close state for that address. Currently used for persisting the open/close state
* of functions in the listing.
*/
public class PersistentOpenCloseManager {
private boolean openByDefault = true;
private VoidPropertyMap booleanProperty;
private ProgramUserData programUserData;
// Often, isOpen will be called on the same function address many times in a row so cache
// the last address and result
private Address cachedAddress;
private boolean cachedResult;
private String defaultOpenClosePropertyname;
public PersistentOpenCloseManager(Program program, String owner, String propertyName) {
this.defaultOpenClosePropertyname = propertyName + "Default";
programUserData = program.getProgramUserData();
int tx = programUserData.startTransaction();
try {
booleanProperty =
programUserData.getBooleanProperty(owner, propertyName, true);
}
finally {
programUserData.endTransaction(tx);
}
// Get the default open state. Only addresses different from default have properties stored.
String functionState =
programUserData.getStringProperty(defaultOpenClosePropertyname, "Open");
openByDefault = functionState.equals("Open");
}
/**
* Checks if the state is "open" for the given address.
* @param address the address to test
* @return true if the state of the given address is "open"
*/
public boolean isOpen(Address address) {
if (address.equals(cachedAddress)) {
return cachedResult;
}
cachedAddress = address;
boolean contains = booleanProperty.hasProperty(address);
cachedResult = openByDefault ? !contains : contains;
return cachedResult;
}
/**
* Sets the state at the given address to be "open".
* @param address the address to set "open"
*/
public void open(Address address) {
cachedAddress = null;
if (openByDefault) {
removeAddressProperty(address);
}
else {
addAddressProperty(address);
}
}
/**
* Sets the state at the given address to be "closed".
* @param address the address to set "closed"
*/
public void close(Address address) {
cachedAddress = null;
if (openByDefault) {
addAddressProperty(address);
}
else {
removeAddressProperty(address);
}
}
/**
* Checks if the default state is "open".
* @return true if the default state for addresses is "open"
*/
public boolean isOpenByDefault() {
return openByDefault;
}
/**
* Sets all address to "open" (Makes "open" the default state and clears all individual
* settings.
*/
public void openAll() {
cachedAddress = null;
openByDefault = true;
clearProperties();
programUserData.setStringProperty(defaultOpenClosePropertyname, "Open");
}
/**
* Sets all address to "closed" (Makes "closed" the default state and clears all individual
* settings.
*/
public void closeAll() {
cachedAddress = null;
openByDefault = false;
clearProperties();
programUserData.setStringProperty(defaultOpenClosePropertyname, "Closed");
}
private void addAddressProperty(Address address) {
int tx = programUserData.startTransaction();
try {
booleanProperty.add(address);
}
finally {
programUserData.endTransaction(tx);
}
}
private void removeAddressProperty(Address address) {
int tx = programUserData.startTransaction();
try {
booleanProperty.remove(address);
}
finally {
programUserData.endTransaction(tx);
}
}
private void clearProperties() {
int tx = programUserData.startTransaction();
try {
booleanProperty.clear();
}
finally {
programUserData.endTransaction(tx);
}
}
}

View File

@@ -33,30 +33,42 @@ import ghidra.util.task.TaskMonitor;
public class ProgramOpenCloseManager {
private DataOpenCloseManager dataOpenCloseManager = new DataOpenCloseManager();
private List<ChangeListener> listeners = new ArrayList<>();
private AddressBasedOpenCloseManager variablesOpenCloseManager = new AddressBasedOpenCloseManager();
private InMemoryOpenCloseManager variablesOpenCloseManager;
private PersistentOpenCloseManager functionsOpenCloseManager;
public ProgramOpenCloseManager(Program program) {
// open/close functions states are persisted and specific to a program
functionsOpenCloseManager = new PersistentOpenCloseManager(program,
getClass().getSimpleName(), "Function Open State");
// open/close variables states are tool based so users can
// choose whether to see them by default. They can be opened or closed
// for specific functions in specific program, but this is not persisted for the program.
variablesOpenCloseManager = new InMemoryOpenCloseManager();
}
/**
* Sets whether or not to display function variables at the given address.
* @param functionAddress the address of the function
* @param functionEntry the address of the function
* @param open true to display function variables, false to to hide them
*/
public void setFunctionVariablesOpen(Address functionAddress, boolean open) {
public void setFunctionVariablesOpen(Address functionEntry, boolean open) {
if (open) {
variablesOpenCloseManager.open(functionAddress);
variablesOpenCloseManager.open(functionEntry);
}
else {
variablesOpenCloseManager.close(functionAddress);
variablesOpenCloseManager.close(functionEntry);
}
notifyListeners();
}
/**
* Checks if the function variables are being shown at the given function address.
* @param functionAddress the address of the function to check
* @param functionEntry the address of the function to check
* @return true if the variables are being displayed
*/
public boolean isFunctionVariablesOpen(Address functionAddress) {
return variablesOpenCloseManager.isOpen(functionAddress);
public boolean isFunctionVariablesOpen(Address functionEntry) {
return variablesOpenCloseManager.isOpen(functionEntry);
}
/**
@@ -74,8 +86,42 @@ public class ProgramOpenCloseManager {
notifyListeners();
}
public boolean isAllFunctionVariablesOpen() {
return variablesOpenCloseManager.isOpenByDefault();
/**
* Returns true if the function at the given address is open.
* @param functionEntry the function address to test if the function is open
* @return true if the function at the given address is open
*/
public boolean isFunctionOpen(Address functionEntry) {
return functionsOpenCloseManager.isOpen(functionEntry);
}
/**
* Sets the function at the given address to be open or closed.
* @param functionEntry the address of the function to open or close
* @param open true to open the function, false to close it
*/
public void setFunctionOpen(Address functionEntry, boolean open) {
if (open) {
functionsOpenCloseManager.open(functionEntry);
}
else {
functionsOpenCloseManager.close(functionEntry);
}
notifyListeners();
}
/**
* Sets all functions to be open or closed.
* @param open true to open all functions, false to close them
*/
public void setAllFunctionsOpen(boolean open) {
if (open) {
functionsOpenCloseManager.openAll();
}
else {
functionsOpenCloseManager.closeAll();
}
notifyListeners();
}
/**
@@ -164,4 +210,5 @@ public class ProgramOpenCloseManager {
l.stateChanged(null);
}
}
}

View File

@@ -0,0 +1,134 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.viewer.util;
import static org.junit.Assert.*;
import org.junit.*;
import ghidra.framework.model.*;
import ghidra.program.database.ProgramBuilder;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.listing.Program;
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
import ghidra.test.TestEnv;
import ghidra.util.task.TaskMonitor;
public class PersistentOpenCloseManagerTest extends AbstractGhidraHeadedIntegrationTest {
private DomainFile df;
private AddressSpace space;
private Program program;
private PersistentOpenCloseManager openCloseMgr;
private TestEnv env;
@Before
public void setUp() throws Exception {
env = new TestEnv();
Project project = env.getProject();
ProgramBuilder builder = new ProgramBuilder("Test", ProgramBuilder._TOY, this);
ProjectData projectData = project.getProjectData();
DomainFolder rootFolder = projectData.getRootFolder();
program = builder.getProgram();
df = rootFolder.createFile("test", program, TaskMonitor.DUMMY);
space = program.getAddressFactory().getDefaultAddressSpace();
openCloseMgr = new PersistentOpenCloseManager(program, "test", "test");
}
@After
public void tearDown() {
env.dispose();
}
@Test
public void testOpenByDefaultCloseAllOpenAll() throws Exception {
assertTrue(openCloseMgr.isOpen(addr(0)));
assertTrue(openCloseMgr.isOpen(addr(100)));
assertTrue(openCloseMgr.isOpenByDefault());
openCloseMgr.closeAll();
assertFalse(openCloseMgr.isOpen(addr(0)));
assertFalse(openCloseMgr.isOpen(addr(100)));
assertFalse(openCloseMgr.isOpenByDefault());
openCloseMgr.openAll();
assertTrue(openCloseMgr.isOpen(addr(0)));
assertTrue(openCloseMgr.isOpen(addr(100)));
assertTrue(openCloseMgr.isOpenByDefault());
}
@Test
public void testCloseAndOpenSpecificAddresses() throws Exception {
assertTrue(openCloseMgr.isOpen(addr(0)));
assertTrue(openCloseMgr.isOpen(addr(100)));
openCloseMgr.close(addr(0));
assertFalse(openCloseMgr.isOpen(addr(0)));
assertTrue(openCloseMgr.isOpen(addr(100)));
openCloseMgr.open(addr(0));
assertTrue(openCloseMgr.isOpen(addr(0)));
assertTrue(openCloseMgr.isOpen(addr(100)));
}
@Test
public void testOpenAndCloseSpecificAddressesWithDefaultClosed() throws Exception {
openCloseMgr.closeAll();
assertFalse(openCloseMgr.isOpen(addr(0)));
assertFalse(openCloseMgr.isOpen(addr(100)));
openCloseMgr.open(addr(0));
assertTrue(openCloseMgr.isOpen(addr(0)));
assertFalse(openCloseMgr.isOpen(addr(100)));
openCloseMgr.close(addr(0));
assertFalse(openCloseMgr.isOpen(addr(0)));
assertFalse(openCloseMgr.isOpen(addr(100)));
}
@Test
public void testPersistence() throws Exception {
openCloseMgr.closeAll();
assertFalse(openCloseMgr.isOpen(addr(0)));
assertFalse(openCloseMgr.isOpen(addr(100)));
openCloseMgr.open(addr(0));
assertTrue(openCloseMgr.isOpen(addr(0)));
assertFalse(openCloseMgr.isOpen(addr(100)));
program.release(this);
program = (Program) df.getDomainObject(this, false, false, TaskMonitor.DUMMY);
space = program.getAddressFactory().getDefaultAddressSpace();
openCloseMgr = new PersistentOpenCloseManager(program, "test", "test");
assertTrue(openCloseMgr.isOpen(addr(0)));
assertFalse(openCloseMgr.isOpen(addr(100)));
assertFalse(openCloseMgr.isOpenByDefault());
}
private Address addr(long offset) {
return space.getAddress(offset);
}
}

View File

@@ -0,0 +1,162 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.viewer.listingpanel;
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;
import generic.test.AbstractGenericTest;
import ghidra.app.util.viewer.format.FormatManager;
import ghidra.framework.options.SaveState;
import ghidra.framework.options.ToolOptions;
import ghidra.program.database.ProgramBuilder;
import ghidra.program.database.ProgramDB;
import ghidra.program.database.function.OverlappingFunctionException;
import ghidra.program.model.address.*;
import ghidra.program.model.listing.Function;
public class ProgramBigListingModelTest extends AbstractGenericTest {
private ProgramDB program;
private ProgramBigListingModel model;
private AddressSpace space;
@Before
public void setUp() throws Exception {
ProgramBuilder builder = new ProgramBuilder("Test", ProgramBuilder._X86);
program = builder.getProgram();
space = program.getAddressFactory().getDefaultAddressSpace();
builder.createMemory("block1", "0", 18);
buildFun0(builder);
FormatManager formatManager =
new FormatManager(new ToolOptions("display"), new ToolOptions("Listing Fields"));
formatManager.readState(new SaveState()); // this will use default format
model = new ProgramBigListingModel(program, formatManager);
}
private void buildFun0(ProgramBuilder builder) throws OverlappingFunctionException {
Function fun0 = builder.createFunction("0");
AddressSet addrs = new AddressSet();
addrs.add(addr(0), addr(3));
addrs.add(addr(6), addr(9));
addrs.add(addr(12), addr(15));
program.withTransaction("test", () -> fun0.setBody(addrs));
}
private Address addr(long offset) {
return space.getAddress(offset);
}
@Test
public void testGetNextAddressWithFunctionOpen() {
assertEquals(addr(1), model.getAddressAfter(addr(0)));
assertEquals(addr(2), model.getAddressAfter(addr(1)));
assertEquals(addr(3), model.getAddressAfter(addr(2)));
assertEquals(addr(4), model.getAddressAfter(addr(3)));
assertEquals(addr(5), model.getAddressAfter(addr(4)));
assertEquals(addr(6), model.getAddressAfter(addr(5)));
assertEquals(addr(7), model.getAddressAfter(addr(6)));
assertEquals(addr(8), model.getAddressAfter(addr(7)));
assertEquals(addr(9), model.getAddressAfter(addr(8)));
assertEquals(addr(10), model.getAddressAfter(addr(9)));
assertEquals(addr(11), model.getAddressAfter(addr(10)));
assertEquals(addr(12), model.getAddressAfter(addr(11)));
assertEquals(addr(13), model.getAddressAfter(addr(12)));
assertEquals(addr(14), model.getAddressAfter(addr(13)));
assertEquals(addr(15), model.getAddressAfter(addr(14)));
assertEquals(addr(16), model.getAddressAfter(addr(15)));
assertEquals(addr(17), model.getAddressAfter(addr(16)));
assertNull(model.getAddressAfter(addr(17)));
}
@Test
public void testGetNextAddressWithFunctionClosed() {
model.setFunctionOpen(addr(0), false);
assertEquals(addr(4), model.getAddressAfter(addr(0)));
assertEquals(addr(4), model.getAddressAfter(addr(1)));
assertEquals(addr(4), model.getAddressAfter(addr(2)));
assertEquals(addr(4), model.getAddressAfter(addr(3)));
assertEquals(addr(5), model.getAddressAfter(addr(4)));
assertEquals(addr(6), model.getAddressAfter(addr(5)));
assertEquals(addr(10), model.getAddressAfter(addr(6)));
assertEquals(addr(10), model.getAddressAfter(addr(7)));
assertEquals(addr(10), model.getAddressAfter(addr(8)));
assertEquals(addr(10), model.getAddressAfter(addr(9)));
assertEquals(addr(11), model.getAddressAfter(addr(10)));
assertEquals(addr(12), model.getAddressAfter(addr(11)));
assertEquals(addr(16), model.getAddressAfter(addr(12)));
assertEquals(addr(16), model.getAddressAfter(addr(13)));
assertEquals(addr(16), model.getAddressAfter(addr(14)));
assertEquals(addr(16), model.getAddressAfter(addr(15)));
assertEquals(addr(17), model.getAddressAfter(addr(16)));
assertNull(model.getAddressAfter(addr(17)));
}
@Test
public void testGetPreviousAddressWithFunctionOpen() {
assertNull(model.getAddressBefore(addr(0)));
assertEquals(addr(0), model.getAddressBefore(addr(1)));
assertEquals(addr(1), model.getAddressBefore(addr(2)));
assertEquals(addr(2), model.getAddressBefore(addr(3)));
assertEquals(addr(3), model.getAddressBefore(addr(4)));
assertEquals(addr(4), model.getAddressBefore(addr(5)));
assertEquals(addr(5), model.getAddressBefore(addr(6)));
assertEquals(addr(6), model.getAddressBefore(addr(7)));
assertEquals(addr(7), model.getAddressBefore(addr(8)));
assertEquals(addr(8), model.getAddressBefore(addr(9)));
assertEquals(addr(9), model.getAddressBefore(addr(10)));
assertEquals(addr(10), model.getAddressBefore(addr(11)));
assertEquals(addr(11), model.getAddressBefore(addr(12)));
assertEquals(addr(12), model.getAddressBefore(addr(13)));
assertEquals(addr(13), model.getAddressBefore(addr(14)));
assertEquals(addr(14), model.getAddressBefore(addr(15)));
assertEquals(addr(15), model.getAddressBefore(addr(16)));
assertEquals(addr(16), model.getAddressBefore(addr(17)));
assertEquals(addr(17), model.getAddressBefore(addr(18)));
assertEquals(addr(17), model.getAddressBefore(addr(19)));
}
@Test
public void testGetPreviousAddressWithFunctionClosed() {
model.setFunctionOpen(addr(0), false);
assertNull(model.getAddressBefore(addr(0)));
assertEquals(addr(0), model.getAddressBefore(addr(1)));
assertEquals(addr(0), model.getAddressBefore(addr(2)));
assertEquals(addr(0), model.getAddressBefore(addr(3)));
assertEquals(addr(0), model.getAddressBefore(addr(4)));
assertEquals(addr(4), model.getAddressBefore(addr(5)));
assertEquals(addr(5), model.getAddressBefore(addr(6)));
assertEquals(addr(6), model.getAddressBefore(addr(7)));
assertEquals(addr(6), model.getAddressBefore(addr(8)));
assertEquals(addr(6), model.getAddressBefore(addr(9)));
assertEquals(addr(6), model.getAddressBefore(addr(10)));
assertEquals(addr(10), model.getAddressBefore(addr(11)));
assertEquals(addr(11), model.getAddressBefore(addr(12)));
assertEquals(addr(12), model.getAddressBefore(addr(13)));
assertEquals(addr(12), model.getAddressBefore(addr(14)));
assertEquals(addr(12), model.getAddressBefore(addr(15)));
assertEquals(addr(12), model.getAddressBefore(addr(16)));
assertEquals(addr(16), model.getAddressBefore(addr(17)));
assertEquals(addr(17), model.getAddressBefore(addr(18)));
assertEquals(addr(17), model.getAddressBefore(addr(19)));
}
}

View File

@@ -213,6 +213,20 @@ public abstract class PropertyMapDB<T> extends DatabaseObject implements Propert
return name;
}
@Override
public void clear() {
if (propertyTable == null) {
return;
}
cache = new ObjectCache<>(DEFAULT_CACHE_SIZE);
try {
propertyTable.deleteAll();
}
catch (IOException e) {
errHandler.dbError(e);
}
}
/**
* Adjust the size of the underlying read cache.
* @param size the size of the cache.
@@ -225,7 +239,6 @@ public abstract class PropertyMapDB<T> extends DatabaseObject implements Propert
finally {
lock.release();
}
}
/**

View File

@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -79,6 +79,21 @@ public abstract class DefaultPropertyMap<T> implements PropertyMap<T> {
return description;
}
@Override
public void clear() {
if (getSize() == 0) {
return;
}
try {
long first = propertyMgr.getFirstPropertyIndex();
long last = propertyMgr.getLastPropertyIndex();
propertyMgr.removeRange(first, last);
}
catch (NoSuchIndexException e) {
// can't happen since we checked it isn't empty
}
}
@Override
public boolean intersects(Address start, Address end) {
return propertyMgr.intersects(addrMap.getKey(start), addrMap.getKey(end));

View File

@@ -34,6 +34,11 @@ public interface PropertyMap<T> {
*/
public Class<T> getValueClass();
/**
* Removes all properties from this map
*/
public void clear();
/**
* Given two addresses, indicate whether there is an address in
* that range (inclusive) having the property.
@@ -45,7 +50,7 @@ public interface PropertyMap<T> {
* has the property, false otherwise.
*/
public boolean intersects(Address start, Address end);
/**
* Indicate whether there is an address within
* the set which exists within this map.
@@ -55,7 +60,7 @@ public interface PropertyMap<T> {
* has the property, false otherwise.
*/
public boolean intersects(AddressSetView set);
/**
* Removes all property values within a given range.
* @param start begin range
@@ -64,6 +69,7 @@ public interface PropertyMap<T> {
* false otherwise.
*/
public boolean removeRange(Address start, Address end);
/**
* Remove the property value at the given address.
* @return true if the property value was removed, false
@@ -71,7 +77,7 @@ public interface PropertyMap<T> {
* @param addr the address where the property should be removed
*/
public boolean remove(Address addr);
/**
* returns whether there is a property value at addr.
* @param addr the address in question
@@ -86,7 +92,7 @@ public interface PropertyMap<T> {
* @throws IllegalArgumentException if property value type is inappropriate for this map
*/
public void add(Address addr, Object value);
/**
* Returns the property value stored at the specified
* address or null if no property found.
@@ -94,39 +100,39 @@ public interface PropertyMap<T> {
* @return property value
*/
public T get(Address addr);
/**
* Get the next address where the property value exists.
* @param addr the address from which to begin the search (exclusive).
* @return property value location after specified {@code addr} or null if none found
*/
public Address getNextPropertyAddress(Address addr);
/**
* Get the previous Address where a property value exists.
* @param addr the address from which to begin the search (exclusive).
* @return property value location after specified {@code addr} or null if none found
*/
public Address getPreviousPropertyAddress(Address addr);
/**
* Get the first Address where a property value exists.
* @return first property value location or null if none found
*/
public Address getFirstPropertyAddress();
/**
* Get the last Address where a property value exists.
* @return last property value location or null if none found
*/
public Address getLastPropertyAddress();
/**
* Get the number of properties in the map.
* @return number of stored property values
*/
public int getSize();
/**
* Returns an iterator over the indices having a property value.
* @param start minimum address
@@ -134,7 +140,7 @@ public interface PropertyMap<T> {
* @return forward property address iterator
*/
public AddressIterator getPropertyIterator(Address start, Address end);
/**
* Returns an iterator over addresses that have a property value.
* @param start minimum address
@@ -144,7 +150,7 @@ public interface PropertyMap<T> {
* @return property address iterator
*/
public AddressIterator getPropertyIterator(Address start, Address end, boolean forward);
/**
* Returns an iterator over the addresses that a property value.
* @return forward property address iterator
@@ -157,7 +163,7 @@ public interface PropertyMap<T> {
* @param asv the set of addresses to iterate over.
* @return forward property address iterator
*/
public AddressIterator getPropertyIterator(AddressSetView asv);
public AddressIterator getPropertyIterator(AddressSetView asv);
/**
* Returns an iterator over the addresses that have a property value and
@@ -168,7 +174,7 @@ public interface PropertyMap<T> {
* @return property address iterator
*/
public AddressIterator getPropertyIterator(AddressSetView asv, boolean forward);
/**
* Returns an iterator over the address having a property value.
* @param start the starting address
@@ -177,7 +183,7 @@ public interface PropertyMap<T> {
* @return property address iterator
*/
public AddressIterator getPropertyIterator(Address start, boolean forward);
/**
* Moves the properties defined in the range from the start address thru the
* end address to now be located beginning at the newStart address.

View File

@@ -0,0 +1,46 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.program.util;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Program;
/**
* <CODE>CollapsedCodeLocation</CODE> is program location generated when the cursor is on a field
* representing code from a collapsed function.
*/
public class CollapsedCodeLocation extends ProgramLocation {
/**
* Create a new DividerLocation.
*
* @param program the program of the location
* @param addr address of bookmark
* by its hierarchy names; this parameter may be null
*/
public CollapsedCodeLocation(Program program, Address addr) {
super(program, addr, addr, null, null, 0, 0, 0);
}
/**
* Default constructor needed for restoring
* a program location from XML
*/
public CollapsedCodeLocation() {
}
}

View File

@@ -0,0 +1,41 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.program.util;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Program;
/**
* ProgramLocation that represents the cursor being on the function's open/close widget
*/
public class FunctionOpenCloseLocation extends CodeUnitLocation {
/**
* Constructor
*
* @param program the program of the location
* @param addr address of the location
*/
public FunctionOpenCloseLocation(Program program, Address addr) {
super(program, addr, null, 0, 0, 0);
}
/**
* Required constructor for serialization
*/
public FunctionOpenCloseLocation() {
}
}