mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-02-18 00:20:10 +00:00
Merge remote-tracking branch 'origin/GP-3531-dragonmacher-update-auto-comments--SQUASHED'
This commit is contained in:
commit
5aedf25ee6
@ -518,24 +518,46 @@
|
||||
<H3><A name="EOL_Comments_Field"></A>EOL Comments Field</H3>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P>The EOL Comments field displays the end-of-line comment at this address. If there is no
|
||||
EOL comment, it displays the repeatable comment. If there is no repeatable comment, it
|
||||
displays the repeatable comments from all referenced addresses. If there aren't any
|
||||
referenced repeatable comments, it displays an automatic comment if it can.</P>
|
||||
<P>The EOL Comments field displays the end-of-line comment at this address. By default,
|
||||
if there is no EOL comment, then other comment types may be displayed.
|
||||
</P>
|
||||
|
||||
<P><B>Always Show the Automatic Comment -</B> Normally automatic comments are not shown if
|
||||
there is an EOL comment, repeatable comment, or referenced repeatable comment. By selecting
|
||||
this option, the automatic comment will be shown even if there is an EOL comment,
|
||||
repeatable comment, or referenced repeatable comment.</P>
|
||||
<P><B>Additional Comment Types -</B> The following comment types may be optionally displayed
|
||||
in the EOL Comments field. For each type below, the following setting may be applied:
|
||||
<UL>
|
||||
<LI><B>ALWAYS -</B> Always show the comment type, regardless of other comment types
|
||||
that are showing. The appearance of any comment will be limited by the maximum
|
||||
number of lines currently set on the EOL Comments field.
|
||||
</LI>
|
||||
<LI><B>DEFAULT -</B> Show the comment type only when there is no other comment type
|
||||
of a higher precedence. When all fields are set to this option, then the EOL
|
||||
Comments field will only display one comment type at a time.
|
||||
</LI>
|
||||
<LI><B>NEVER -</B> Do not show this comment type.
|
||||
</LI>
|
||||
</UL>
|
||||
</P>
|
||||
|
||||
<P>
|
||||
Each of the comments below is listed in order of precedence. The default behavior is to
|
||||
show one comment in the EOL Comments field at a time, based on this precedence, with the EOL
|
||||
Comment being the highest.
|
||||
</P>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P><B>Repeatable -</B> The Repeatable Comment defined <I>at the current
|
||||
code unit address</I>.</P>
|
||||
|
||||
<P><B>Referenced Repeatable -</B> The Repeatable Comment that is defined at the
|
||||
target reference address that the code unit at this address refers to.</P>
|
||||
|
||||
<P><B>Automatic Function -</B> A preview of the referenced function.</P>
|
||||
|
||||
<P><B>Automatic Data -</B> A preview of the referenced data. For example, a String
|
||||
reference will show a preview of the target String.</P>
|
||||
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<P><B>Always Show the Referenced Repeatable Comment -</B> Normally referenced repeatable
|
||||
comments are not shown if there is an EOL comment or repeatable comment. By selecting this
|
||||
option, the referenced repeatable comment will be shown even if there is an EOL comment or
|
||||
repeatable comment.</P>
|
||||
|
||||
<P><B>Always Show the Repeatable Comment -</B> Normally repeatable comments are not shown
|
||||
if there is an EOL comment. By selecting this option, the repeatable comment will be shown
|
||||
even if there is an EOL comment.</P>
|
||||
|
||||
<P><B>Enable Word Wrapping -</B> If this option is not set, each comment line is displayed
|
||||
on a line by itself. By turning on word-wrapping, comment lines are displayed in paragraph
|
||||
|
@ -1,927 +0,0 @@
|
||||
/* ###
|
||||
* 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;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import docking.widgets.fieldpanel.support.RowColLocation;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.mem.*;
|
||||
import ghidra.program.model.scalar.Scalar;
|
||||
import ghidra.program.model.symbol.*;
|
||||
import ghidra.program.util.*;
|
||||
import ghidra.util.StringUtilities;
|
||||
|
||||
/**
|
||||
* Utility class with methods to get comment information that can be displayed in the
|
||||
* end of line comment field. A DisplayableEol is associated with a code unit.
|
||||
* The DisplayableEol gets information for the EOL comment field, which can show the
|
||||
* End of Line comment for the code unit, the Repeatable comment for the code unit,
|
||||
* any repeatable comments for the code units that this code unit has references to, and
|
||||
* possibly a comment indicating the data at a code unit that is referenced by this code unit.
|
||||
*/
|
||||
public class DisplayableEol {
|
||||
|
||||
private static final String POINTER_ARROW = "-> ";
|
||||
|
||||
public static final int MY_EOLS = 0;
|
||||
public static final int MY_REPEATABLES = 1;
|
||||
public static final int REF_REPEATABLES = 2;
|
||||
public static final int MY_AUTOMATIC = 3;
|
||||
private CodeUnit codeUnit;
|
||||
private Object[][] displayCommentArrays = { null, null, null, null };
|
||||
private boolean alwaysShowRepeatable = false;
|
||||
private boolean alwaysShowRefRepeats = false;
|
||||
private boolean alwaysShowAutomatic = false;
|
||||
private boolean showAutomaticFunctions;
|
||||
private boolean operandsFollowPointerRefs = false;
|
||||
private int maxDisplayLines;
|
||||
private int totalCommentsFound;
|
||||
|
||||
private boolean useAbbreviatedAutomatic;
|
||||
|
||||
public DisplayableEol(CodeUnit cu, boolean alwaysShowRepeatable, boolean alwaysShowRefRepeats,
|
||||
boolean alwaysShowAutomatic, boolean operandsFollowPointerRefs, int maxDisplayLines,
|
||||
boolean useAbbreviatedAutomatic, boolean showAutomaticFunctions) {
|
||||
this.codeUnit = cu;
|
||||
this.alwaysShowRepeatable = alwaysShowRepeatable;
|
||||
this.alwaysShowRefRepeats = alwaysShowRefRepeats;
|
||||
this.alwaysShowAutomatic = alwaysShowAutomatic;
|
||||
this.operandsFollowPointerRefs = operandsFollowPointerRefs;
|
||||
this.maxDisplayLines = maxDisplayLines;
|
||||
this.useAbbreviatedAutomatic = useAbbreviatedAutomatic;
|
||||
this.showAutomaticFunctions = showAutomaticFunctions;
|
||||
|
||||
initComments();
|
||||
}
|
||||
|
||||
private void initComments() {
|
||||
displayCommentArrays[MY_EOLS] = codeUnit.getCommentAsArray(CodeUnit.EOL_COMMENT);
|
||||
totalCommentsFound += displayCommentArrays[MY_EOLS].length;
|
||||
|
||||
displayCommentArrays[MY_REPEATABLES] =
|
||||
codeUnit.getCommentAsArray(CodeUnit.REPEATABLE_COMMENT);
|
||||
totalCommentsFound += displayCommentArrays[MY_REPEATABLES].length;
|
||||
|
||||
displayCommentArrays[REF_REPEATABLES] = new RefRepeatComment[0];
|
||||
displayCommentArrays[MY_AUTOMATIC] = new String[0];
|
||||
if (totalCommentsFound > maxDisplayLines) {
|
||||
// no more room to display the comments below; don't process them
|
||||
return;
|
||||
}
|
||||
|
||||
// cap the number of references we get (we don't want to process 500000....)
|
||||
Reference[] refs = getReferencesFrom(codeUnit, 100);
|
||||
Arrays.sort(refs);
|
||||
|
||||
Program program = codeUnit.getProgram();
|
||||
displayCommentArrays[REF_REPEATABLES] =
|
||||
getRepeatableComments(program.getListing(), refs, true);
|
||||
totalCommentsFound += displayCommentArrays[REF_REPEATABLES].length;
|
||||
|
||||
displayCommentArrays[MY_AUTOMATIC] = getReferencePreviews(program, refs);
|
||||
totalCommentsFound += displayCommentArrays[MY_AUTOMATIC].length;
|
||||
}
|
||||
|
||||
private Reference[] getReferencesFrom(CodeUnit cu, int maxReferences) {
|
||||
ArrayList<Reference> list = new ArrayList<>();
|
||||
|
||||
Program program = cu.getProgram();
|
||||
ReferenceManager referenceManager = program.getReferenceManager();
|
||||
AddressSet set = new AddressSet(cu.getMinAddress(), cu.getMaxAddress());
|
||||
AddressIterator iter = referenceManager.getReferenceSourceIterator(set, true);
|
||||
while (iter.hasNext() && list.size() < maxReferences) {
|
||||
Address fromAddress = iter.next();
|
||||
Reference[] refs = referenceManager.getReferencesFrom(fromAddress);
|
||||
for (Reference element : refs) {
|
||||
list.add(element);
|
||||
}
|
||||
}
|
||||
return list.toArray(new Reference[list.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the associated code unit has an end of line comment
|
||||
* @return whether the associated code unit has an end of line comment
|
||||
*/
|
||||
public boolean hasEOL() {
|
||||
return (displayCommentArrays[MY_EOLS] != null) &&
|
||||
(((String[]) displayCommentArrays[MY_EOLS]).length > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the associated code unit has a repeatable comment
|
||||
* @return whether the associated code unit has a repeatable comment
|
||||
*/
|
||||
public boolean hasRepeatable() {
|
||||
return (displayCommentArrays[MY_REPEATABLES] != null) &&
|
||||
(((String[]) displayCommentArrays[MY_REPEATABLES]).length > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether any memory reference from this code unit has a repeatable
|
||||
* comment at the reference's to address
|
||||
* @return whether any memory reference from this code unit has a repeatable
|
||||
* comment at the reference's to address
|
||||
*/
|
||||
public boolean hasReferencedRepeatable() {
|
||||
return (displayCommentArrays[REF_REPEATABLES] != null) &&
|
||||
(displayCommentArrays[REF_REPEATABLES].length > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether this code unit has an automatic comment. For example, a memory reference
|
||||
* from this code unit has a function defined at the reference's to address, or if the to
|
||||
* address is a pointer.
|
||||
* @return whether this code unit has an automatic comment
|
||||
*/
|
||||
public boolean hasAutomatic() {
|
||||
return (displayCommentArrays[MY_AUTOMATIC] != null) &&
|
||||
(displayCommentArrays[MY_AUTOMATIC].length > 0);
|
||||
}
|
||||
|
||||
private String[] getReferencePreviews(Program program, Reference[] refs) {
|
||||
|
||||
if (refs.length == 0) {
|
||||
return getPreviewForNoReferences();
|
||||
}
|
||||
|
||||
Set<String> set = new LinkedHashSet<>();
|
||||
for (Reference reference : refs) {
|
||||
|
||||
if (reachedMaximumResults(set.size())) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!isValidReference(program, reference)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
addReferencePreview(set, program, reference);
|
||||
}
|
||||
|
||||
String[] array = new String[set.size()];
|
||||
set.toArray(array);
|
||||
return array;
|
||||
}
|
||||
|
||||
private String[] getPreviewForNoReferences() {
|
||||
String translatedStringValue = getPreviewForString();
|
||||
if (translatedStringValue != null) {
|
||||
return new String[] { translatedStringValue };
|
||||
}
|
||||
String undefinedPointerText = getUndefinedPointer(codeUnit);
|
||||
if (undefinedPointerText != null) {
|
||||
return new String[] { undefinedPointerText };
|
||||
}
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
private String getPreviewForString() {
|
||||
if (codeUnit instanceof Data data && StringDataInstance.isString(data)) {
|
||||
StringDataInstance sdi = StringDataInstance.getStringDataInstance(data);
|
||||
if (sdi.hasTranslatedValue()) {
|
||||
// show the opposite value
|
||||
return sdi.getStringRepresentation(sdi.isShowTranslation());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean isValidReference(Program program, Reference reference) {
|
||||
|
||||
if (!reference.isMemoryReference()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Address toAddr = reference.getToAddress();
|
||||
return isGoodAddress(program, toAddr);
|
||||
}
|
||||
|
||||
private boolean reachedMaximumResults(int newCount) {
|
||||
return (totalCommentsFound + newCount) >= maxDisplayLines;
|
||||
}
|
||||
|
||||
private void addReferencePreview(Set<String> results, Program program, Reference reference) {
|
||||
|
||||
Address toAddr = reference.getToAddress();
|
||||
if (handleDirectFlow(results, reference, program, toAddr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Data data = getData(program, toAddr);
|
||||
if (data == null) {
|
||||
return; // nothing there!
|
||||
}
|
||||
|
||||
if (handleIndirectDataReference(results, reference, program, toAddr, data)) {
|
||||
return;
|
||||
}
|
||||
|
||||
handleDirectDataReference(results, toAddr, data);
|
||||
}
|
||||
|
||||
private Data getData(Program program, Address toAddr) {
|
||||
|
||||
Data data = program.getListing().getDataAt(toAddr);
|
||||
if (data == null) {
|
||||
// could be slower
|
||||
data = program.getListing().getDataContaining(toAddr);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
private void handleDirectDataReference(Set<String> set, Address dataAccessAddress, Data data) {
|
||||
|
||||
Object value = data.getValue();
|
||||
if (value instanceof Scalar) {
|
||||
Scalar scalar = (Scalar) value;
|
||||
if (scalar.getSignedValue() == 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
String dataRepresentation = getDataValueRepresentation(dataAccessAddress, data);
|
||||
if (!StringUtils.isBlank(dataRepresentation)) {
|
||||
set.add("= " + dataRepresentation);
|
||||
}
|
||||
}
|
||||
|
||||
private String getDataValueRepresentation(Address dataAccessAddress, Data data) {
|
||||
if (!useAbbreviatedAutomatic) {
|
||||
return data.getDefaultValueRepresentation();
|
||||
}
|
||||
|
||||
if (isOffcut(dataAccessAddress, data)) {
|
||||
return getOffcutDataString(dataAccessAddress, data);
|
||||
}
|
||||
|
||||
return data.getDefaultValueRepresentation();
|
||||
}
|
||||
|
||||
private boolean isOffcut(Address address, CodeUnit cu) {
|
||||
if (cu == null) {
|
||||
return false;
|
||||
}
|
||||
return !cu.getMinAddress().equals(address);
|
||||
}
|
||||
|
||||
private String getOffcutDataString(Address offcutAddress, Data data) {
|
||||
Address dataAddress = data.getMinAddress();
|
||||
int diff = (int) offcutAddress.subtract(dataAddress);
|
||||
|
||||
DataType dt = data.getBaseDataType();
|
||||
return getOffcutForStringData(data, dataAddress, diff, dt);
|
||||
}
|
||||
|
||||
private String getOffcutForStringData(Data data, Address dataAddress, int diff, DataType dt) {
|
||||
if (StringDataInstance.isString(data)) {
|
||||
StringDataInstance string = StringDataInstance.getStringDataInstance(data);
|
||||
string = string.getByteOffcut(diff);
|
||||
return string.getStringRepresentation();
|
||||
}
|
||||
if (!data.hasStringValue()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int len = data.getLength();
|
||||
|
||||
if (diff >= len) {
|
||||
// not sure if this can happen--just use the default
|
||||
return data.getDefaultValueRepresentation();
|
||||
}
|
||||
|
||||
DumbMemBufferImpl mb = new DumbMemBufferImpl(data.getMemory(), dataAddress.add(diff));
|
||||
String s = dt.getRepresentation(mb, data, len - diff);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
private boolean handleIndirectDataReference(Set<String> set, Reference reference,
|
||||
Program program, Address toAddress, Data data) {
|
||||
|
||||
RefType type = reference.getReferenceType();
|
||||
if (!type.isIndirect()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (handlePointer(set, program, reference, data)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
handlePotentialPointer(set, program, toAddress, data);
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean handlePointer(Set<String> set, Program program, Reference reference,
|
||||
Data data) {
|
||||
|
||||
if (!data.isPointer()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SymbolTable symbolTable = program.getSymbolTable();
|
||||
ReferenceManager referenceManager = program.getReferenceManager();
|
||||
Reference pointerReference =
|
||||
referenceManager.getPrimaryReferenceFrom(reference.getToAddress(), 0);
|
||||
if (pointerReference != null) {
|
||||
Address addr = pointerReference.getToAddress();
|
||||
Symbol sym = symbolTable.getPrimarySymbol(addr);
|
||||
if (operandsFollowPointerRefs && reference.getOperandIndex() != CodeUnit.MNEMONIC) {
|
||||
if (!sym.isDynamic()) {
|
||||
return true; // already displayed by operand
|
||||
}
|
||||
}
|
||||
set.add(POINTER_ARROW + sym.getName());
|
||||
return true;
|
||||
}
|
||||
|
||||
Address address = (Address) data.getValue();
|
||||
if (address != null && address.getOffset() != 0) {
|
||||
set.add(POINTER_ARROW + address);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void handlePotentialPointer(Set<String> list, Program program, Address toAddress,
|
||||
Data data) {
|
||||
|
||||
if (data.isDefined()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if no data is defined at the address, see if it is a pointer
|
||||
SymbolTable symbolTable = program.getSymbolTable();
|
||||
PseudoDisassembler dis = new PseudoDisassembler(program);
|
||||
Address pointerAddress = dis.getIndirectAddr(toAddress);
|
||||
if (!isGoodAddress(program, pointerAddress)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Symbol symbol = symbolTable.getPrimarySymbol(pointerAddress);
|
||||
if (symbol != null) {
|
||||
list.add(POINTER_ARROW + symbol.getName());
|
||||
}
|
||||
else {
|
||||
list.add(POINTER_ARROW + pointerAddress);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean handleDirectFlow(Set<String> set, Reference reference, Program program,
|
||||
Address toAddr) {
|
||||
|
||||
if (!showAutomaticFunctions) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RefType type = reference.getReferenceType();
|
||||
if (!type.isFlow()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (type.isIndirect()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (type.isCall()) {
|
||||
boolean showName = reference.isMnemonicReference();
|
||||
String signature = getFunctionSignature(program, toAddr, showName);
|
||||
if (signature != null) {
|
||||
set.add(signature);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private String getUndefinedPointer(CodeUnit cu) {
|
||||
if (!(cu instanceof Data)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Data data = (Data) cu;
|
||||
DataType dataType = data.getDataType();
|
||||
if (!(dataType instanceof Undefined || dataType instanceof DefaultDataType)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Program program = cu.getProgram();
|
||||
if (programIsEntireMemorySpace(program)) {
|
||||
// this prevents the case where a program represents the entire address space
|
||||
// in which everything looks like a pointer
|
||||
return null;
|
||||
}
|
||||
|
||||
int align = program.getLanguage().getInstructionAlignment();
|
||||
Address codeUnitAddress = cu.getAddress();
|
||||
long codeUnitOffset = codeUnitAddress.getOffset();
|
||||
if ((codeUnitOffset % align) != 0) {
|
||||
// not aligned
|
||||
return null;
|
||||
}
|
||||
|
||||
int pointerSize = program.getDefaultPointerSize();
|
||||
long addrLong = 0;
|
||||
Memory memory = program.getMemory();
|
||||
try {
|
||||
switch (pointerSize) {
|
||||
case 4:
|
||||
int addrInt = memory.getInt(codeUnitAddress);
|
||||
addrLong = (addrInt & 0xffffffffL);
|
||||
addrLong *= codeUnitAddress.getAddressSpace().getAddressableUnitSize();
|
||||
break;
|
||||
case 8:
|
||||
addrLong = memory.getLong(codeUnitAddress);
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (MemoryAccessException e) {
|
||||
// handled below
|
||||
}
|
||||
|
||||
if (addrLong != 0) {
|
||||
try {
|
||||
Address potentialAddr = codeUnitAddress.getNewAddress(addrLong);
|
||||
if (memory.contains(potentialAddr)) {
|
||||
return "? -> " + potentialAddr.toString();
|
||||
}
|
||||
}
|
||||
catch (AddressOutOfBoundsException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean programIsEntireMemorySpace(Program program) {
|
||||
Address minAddress = program.getMinAddress();
|
||||
Address maxAddress = program.getMaxAddress();
|
||||
AddressSpace addressSpace = maxAddress.getAddressSpace();
|
||||
Address spaceMaxAddress = addressSpace.getMaxAddress();
|
||||
long minOffset = minAddress.getOffset();
|
||||
if (minOffset == 0 && maxAddress.equals(spaceMaxAddress)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private String getFunctionSignature(Program program, Address funcAddr,
|
||||
boolean displayFuncName) {
|
||||
Function function = program.getFunctionManager().getFunctionAt(funcAddr);
|
||||
if (function == null) {
|
||||
return null;
|
||||
}
|
||||
return function.getPrototypeString(false, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this address could really be a good address in the program.
|
||||
* Never accept 0 as a valid address.
|
||||
*
|
||||
* @param program program to check if address is valid within.
|
||||
* @param addr address in program to be checked
|
||||
* @return true if this is a valid address
|
||||
*/
|
||||
private boolean isGoodAddress(Program program, Address addr) {
|
||||
if (addr == null) {
|
||||
return false;
|
||||
}
|
||||
if (!program.getMemory().contains(addr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
long offset = addr.getOffset();
|
||||
if (offset == 0x0 || offset == 0xffffffff || offset == 0xffff || offset == 0xff) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an array of objects that indicate the repeatable comments at the "to addresses" of the
|
||||
* references.
|
||||
* @param listing the program listing
|
||||
* @param memRefs the references whose repeatable comments we are interested in.
|
||||
* @param showAll true indicates to show all referenced repeatable comments and not just the
|
||||
* primary reference's repeatable comment.
|
||||
* @return an array of objects, where each object is a RefRepeatComment containing an
|
||||
* address and a String array of the repeatable comments for a reference.
|
||||
*/
|
||||
private RefRepeatComment[] getRepeatableComments(Listing listing, Reference[] memRefs,
|
||||
boolean showAll) {
|
||||
|
||||
Set<RefRepeatComment> set = new LinkedHashSet<>();
|
||||
for (int i = 0; i < memRefs.length && totalCommentsFound < maxDisplayLines; ++i) {
|
||||
if (!showAll && !memRefs[i].isPrimary()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Address address = memRefs[i].getToAddress();
|
||||
String[] comment = getComment(listing, address);
|
||||
if (comment != null && comment.length > 0) {
|
||||
set.add(new RefRepeatComment(address, comment));
|
||||
totalCommentsFound++;
|
||||
}
|
||||
}
|
||||
|
||||
return set.toArray(new RefRepeatComment[set.size()]);
|
||||
}
|
||||
|
||||
private String[] getComment(Listing listing, Address address) {
|
||||
|
||||
// prefer listing comments first since there may not be a code unit at this address
|
||||
String repeatableComment = listing.getComment(CodeUnit.REPEATABLE_COMMENT, address);
|
||||
if (repeatableComment != null) {
|
||||
return StringUtilities.toLines(repeatableComment);
|
||||
}
|
||||
|
||||
CodeUnit cu = listing.getCodeUnitAt(address);
|
||||
if (cu == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Function func = listing.getFunctionAt(address);
|
||||
if (func != null) {
|
||||
return func.getRepeatableCommentAsArray();
|
||||
}
|
||||
|
||||
return cu.getCommentAsArray(CodeUnit.REPEATABLE_COMMENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all the comments
|
||||
* @return the comments
|
||||
*/
|
||||
public String[] getComments() {
|
||||
ArrayList<String> list = new ArrayList<>();
|
||||
boolean hasEol = hasEOL();
|
||||
boolean hasRepeatable = hasRepeatable();
|
||||
boolean hasRefRepeats = hasReferencedRepeatable();
|
||||
|
||||
list.addAll(Arrays.asList((String[]) displayCommentArrays[MY_EOLS]));
|
||||
if (alwaysShowRepeatable || !hasEol) {
|
||||
list.addAll(Arrays.asList((String[]) displayCommentArrays[MY_REPEATABLES]));
|
||||
}
|
||||
|
||||
if (alwaysShowRefRepeats || !(hasEol || hasRepeatable)) {
|
||||
RefRepeatComment[] refRepeatComments =
|
||||
(RefRepeatComment[]) displayCommentArrays[REF_REPEATABLES];
|
||||
for (RefRepeatComment refRepeatComment : refRepeatComments) {
|
||||
// Address addr = refRepeatComments[j].getAddress();
|
||||
list.addAll(Arrays.asList(refRepeatComment.getCommentLines()));
|
||||
}
|
||||
}
|
||||
|
||||
if (alwaysShowAutomatic || !(hasEol || hasRepeatable || hasRefRepeats)) {
|
||||
list.addAll(Arrays.asList((String[]) displayCommentArrays[MY_AUTOMATIC]));
|
||||
}
|
||||
|
||||
return list.toArray(new String[list.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the end of line comment as an array.
|
||||
*
|
||||
* @return the EOL comment
|
||||
*/
|
||||
public String[] getEOLComments() {
|
||||
return (String[]) displayCommentArrays[MY_EOLS];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the repeatable comment as an array.
|
||||
* @return the repeatable comment.
|
||||
*/
|
||||
public String[] getRepeatableComments() {
|
||||
return (String[]) displayCommentArrays[MY_REPEATABLES];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of repeatable comments at the "to reference"s
|
||||
* @return the number of reference repeatable comments
|
||||
*/
|
||||
public int getReferencedRepeatableCommentsCount() {
|
||||
return displayCommentArrays[REF_REPEATABLES].length;
|
||||
}
|
||||
|
||||
public String[] getReferencedRepeatableComments() {
|
||||
ArrayList<String> stringList = new ArrayList<>();
|
||||
int refRepeatCount = getReferencedRepeatableCommentsCount();
|
||||
for (int i = 0; i < refRepeatCount; i++) {
|
||||
RefRepeatComment refRepeatComment = getReferencedRepeatableComments(i);
|
||||
String[] refRepeatComments = refRepeatComment.getCommentLines();
|
||||
stringList.addAll(Arrays.asList(refRepeatComments));
|
||||
}
|
||||
return stringList.toArray(new String[stringList.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a referenced repeatable comment as a RefRepeatComment object.
|
||||
* @param index indicator of which referenced repeatable comment is desired.
|
||||
* The value is 0 thru one less than the number of referenced repeatable comments.
|
||||
* @return the RefRepeatComment containing the referenced address and its referenced repeatable comment
|
||||
*/
|
||||
public RefRepeatComment getReferencedRepeatableComments(int index) {
|
||||
return (RefRepeatComment) displayCommentArrays[REF_REPEATABLES][index];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a referenced repeatable comment as a RefRepeatComment object.
|
||||
* @param refAddress the reference address whose repeatable comment is desired.
|
||||
* Note: there must be a reference from the address for this displayableEol to the refAddress.
|
||||
* @return the comment lines for the referenced address's repeatable comment or null.
|
||||
*/
|
||||
public String[] getReferencedRepeatableComments(Address refAddress) {
|
||||
Object[] refRepeatArray = displayCommentArrays[REF_REPEATABLES];
|
||||
for (Object element : refRepeatArray) {
|
||||
RefRepeatComment refRepeatComment = (RefRepeatComment) element;
|
||||
if (refRepeatComment.getAddress().equals(refAddress)) {
|
||||
return refRepeatComment.getCommentLines();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the automatic comment as an array.
|
||||
* @return the automatic comment
|
||||
*/
|
||||
public String[] getAutomaticComment() {
|
||||
return (String[]) displayCommentArrays[MY_AUTOMATIC];
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
StringBuilder buffy = new StringBuilder();
|
||||
String[] eols = (String[]) displayCommentArrays[MY_EOLS];
|
||||
if (eols.length != 0) {
|
||||
buffy.append("EOLs: ").append(Arrays.toString(eols));
|
||||
}
|
||||
|
||||
String[] myRepeatables = (String[]) displayCommentArrays[MY_REPEATABLES];
|
||||
if (myRepeatables.length != 0) {
|
||||
buffy.append("My Repeatables: ").append(Arrays.toString(myRepeatables));
|
||||
}
|
||||
|
||||
Object[] refRepeatables = displayCommentArrays[REF_REPEATABLES];
|
||||
if (refRepeatables.length != 0) {
|
||||
buffy.append("Ref Repeatables: ").append(Arrays.toString(refRepeatables));
|
||||
}
|
||||
|
||||
String[] myAutomatic = (String[]) displayCommentArrays[MY_AUTOMATIC];
|
||||
if (myAutomatic.length != 0) {
|
||||
buffy.append("My Automatic: ").append(Arrays.toString(myAutomatic));
|
||||
}
|
||||
|
||||
return buffy.toString();
|
||||
}
|
||||
|
||||
public int getCommentLineCount(int subType) {
|
||||
switch (subType) {
|
||||
case MY_EOLS:
|
||||
return ((String[]) displayCommentArrays[MY_EOLS]).length;
|
||||
case MY_REPEATABLES:
|
||||
return ((String[]) displayCommentArrays[MY_REPEATABLES]).length;
|
||||
case REF_REPEATABLES:
|
||||
int count = 0;
|
||||
Object[] refRepeatArray = displayCommentArrays[REF_REPEATABLES];
|
||||
for (Object element : refRepeatArray) {
|
||||
count += ((RefRepeatComment) element).getCommentLines().length;
|
||||
}
|
||||
return count;
|
||||
case MY_AUTOMATIC:
|
||||
return ((String[]) displayCommentArrays[MY_AUTOMATIC]).length;
|
||||
default:
|
||||
throw new IllegalArgumentException(
|
||||
subType + " is not a valid Eol Comment subType indicator.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public int getRefRepeatableCommentLineCount(Address refAddress) {
|
||||
Object[] refRepeatArray = displayCommentArrays[REF_REPEATABLES];
|
||||
for (Object element : refRepeatArray) {
|
||||
RefRepeatComment refRepeatComment = (RefRepeatComment) element;
|
||||
if (refRepeatComment.getAddress().equals(refAddress)) {
|
||||
return refRepeatComment.getCommentLines().length;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private int getEolRow(ProgramLocation loc) {
|
||||
int numBefore = 0;
|
||||
boolean hasEol = hasEOL();
|
||||
boolean hasRepeatable = hasRepeatable();
|
||||
boolean hasRefRepeats = hasReferencedRepeatable();
|
||||
|
||||
if (loc instanceof EolCommentFieldLocation) {
|
||||
EolCommentFieldLocation commentLoc = (EolCommentFieldLocation) loc;
|
||||
return numBefore + commentLoc.getCurrentCommentRow();
|
||||
}
|
||||
numBefore += getCommentLineCount(DisplayableEol.MY_EOLS);
|
||||
|
||||
if (loc instanceof RepeatableCommentFieldLocation) {
|
||||
RepeatableCommentFieldLocation commentLoc = (RepeatableCommentFieldLocation) loc;
|
||||
return numBefore + commentLoc.getCurrentCommentRow();
|
||||
}
|
||||
|
||||
if (alwaysShowRepeatable || !hasEol) {
|
||||
numBefore += getCommentLineCount(DisplayableEol.MY_REPEATABLES);
|
||||
}
|
||||
|
||||
if (loc instanceof RefRepeatCommentFieldLocation) {
|
||||
RefRepeatCommentFieldLocation commentLoc = (RefRepeatCommentFieldLocation) loc;
|
||||
Address desiredAddress = commentLoc.getReferencedRepeatableAddress();
|
||||
int startRowInRefRepeats = getCommentStartRow(desiredAddress);
|
||||
int rowInComment =
|
||||
(hasRefRepeatComment(desiredAddress)) ? commentLoc.getCurrentCommentRow() : 0;
|
||||
return numBefore + startRowInRefRepeats + rowInComment;
|
||||
}
|
||||
|
||||
if (alwaysShowRefRepeats || !(hasEol || hasRepeatable)) {
|
||||
numBefore += getCommentLineCount(DisplayableEol.REF_REPEATABLES);
|
||||
}
|
||||
|
||||
if (loc instanceof AutomaticCommentFieldLocation) {
|
||||
AutomaticCommentFieldLocation commentLoc = (AutomaticCommentFieldLocation) loc;
|
||||
return numBefore + commentLoc.getCurrentCommentRow();
|
||||
}
|
||||
|
||||
if (alwaysShowAutomatic || !(hasEol || hasRepeatable || hasRefRepeats)) {
|
||||
numBefore += getCommentLineCount(DisplayableEol.MY_AUTOMATIC);
|
||||
}
|
||||
|
||||
return numBefore;
|
||||
}
|
||||
|
||||
private boolean hasRefRepeatComment(Address desiredAddress) {
|
||||
RefRepeatComment[] refRepeatComments =
|
||||
(RefRepeatComment[]) displayCommentArrays[REF_REPEATABLES];
|
||||
for (RefRepeatComment comment : refRepeatComments) {
|
||||
Address checkAddress = comment.getAddress();
|
||||
if (desiredAddress.equals(checkAddress)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public RowColLocation getRowCol(CommentFieldLocation cloc) {
|
||||
int strOffset = cloc.getCharOffset();
|
||||
if (cloc instanceof RefRepeatCommentFieldLocation) {
|
||||
RefRepeatCommentFieldLocation commentLoc = (RefRepeatCommentFieldLocation) cloc;
|
||||
Address desiredAddress = commentLoc.getReferencedRepeatableAddress();
|
||||
if (!hasRefRepeatComment(desiredAddress)) {
|
||||
strOffset = 0;
|
||||
}
|
||||
}
|
||||
int eolRow = getEolRow(cloc);
|
||||
return new RowColLocation(eolRow, strOffset);
|
||||
}
|
||||
|
||||
public ProgramLocation getLocation(int eolRow, int eolColumn) {
|
||||
boolean hasEol = hasEOL();
|
||||
boolean hasRepeatable = hasRepeatable();
|
||||
boolean hasRefRepeats = hasReferencedRepeatable();
|
||||
int numEol = getCommentLineCount(MY_EOLS);
|
||||
int numRepeatable = getCommentLineCount(MY_REPEATABLES);
|
||||
int numRefRepeats = getCommentLineCount(REF_REPEATABLES);
|
||||
int numAutomatic = getCommentLineCount(MY_AUTOMATIC);
|
||||
|
||||
int[] cpath = null;
|
||||
if (codeUnit instanceof Data) {
|
||||
cpath = ((Data) codeUnit).getComponentPath();
|
||||
}
|
||||
|
||||
int beforeEol = 0;
|
||||
int beforeRepeatable = beforeEol + numEol;
|
||||
int beforeRefRepeats = beforeRepeatable;
|
||||
if (alwaysShowRepeatable || !hasEol) {
|
||||
beforeRefRepeats += numRepeatable;
|
||||
}
|
||||
|
||||
int beforeAutomatic = beforeRefRepeats;
|
||||
if (alwaysShowRefRepeats || !(hasEol || hasRepeatable)) {
|
||||
beforeAutomatic += numRefRepeats;
|
||||
}
|
||||
|
||||
int numTotal = beforeAutomatic;
|
||||
if (alwaysShowAutomatic || !(hasEol || hasRepeatable || hasRefRepeats)) {
|
||||
numTotal += numAutomatic;
|
||||
}
|
||||
|
||||
if (eolRow < 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Program program = codeUnit.getProgram();
|
||||
if (eolRow < beforeRepeatable) {
|
||||
return new EolCommentFieldLocation(program, codeUnit.getMinAddress(), cpath,
|
||||
getComments(), eolRow, eolColumn, eolRow);
|
||||
}
|
||||
|
||||
if (eolRow < beforeRefRepeats) {
|
||||
return new RepeatableCommentFieldLocation(program, codeUnit.getMinAddress(), cpath,
|
||||
getComments(), eolRow, eolColumn, eolRow - beforeRepeatable);
|
||||
}
|
||||
|
||||
if (eolRow < beforeAutomatic) {
|
||||
int rowInAllRefRepeats = eolRow - beforeRefRepeats;
|
||||
return new RefRepeatCommentFieldLocation(program, codeUnit.getMinAddress(), cpath,
|
||||
getComments(), eolRow, eolColumn, getRefRepeatRow(rowInAllRefRepeats),
|
||||
getRefRepeatAddress(rowInAllRefRepeats));
|
||||
}
|
||||
|
||||
if (eolRow < numTotal) {
|
||||
return new AutomaticCommentFieldLocation(program, codeUnit.getMinAddress(), cpath,
|
||||
getComments(), eolRow, eolColumn, eolRow - beforeAutomatic);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private Address getRefRepeatAddress(int rowInAllRefRepeats) {
|
||||
RefRepeatComment[] refRepeatComments =
|
||||
(RefRepeatComment[]) displayCommentArrays[REF_REPEATABLES];
|
||||
int currentStartRow = 0;
|
||||
for (RefRepeatComment comment : refRepeatComments) {
|
||||
int numRows = comment.getCommentLineCount();
|
||||
if (rowInAllRefRepeats < (currentStartRow + numRows)) {
|
||||
return comment.getAddress();
|
||||
}
|
||||
currentStartRow += numRows;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private int getRefRepeatRow(int rowInAllRefRepeats) {
|
||||
RefRepeatComment[] refRepeatComments =
|
||||
(RefRepeatComment[]) displayCommentArrays[REF_REPEATABLES];
|
||||
int currentStartRow = 0;
|
||||
for (RefRepeatComment comment : refRepeatComments) {
|
||||
int numRows = comment.getCommentLineCount();
|
||||
if (rowInAllRefRepeats < (currentStartRow + numRows)) {
|
||||
return rowInAllRefRepeats - currentStartRow;
|
||||
}
|
||||
currentStartRow += numRows;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private int getCommentStartRow(Address refAddress) {
|
||||
RefRepeatComment[] refRepeatComments =
|
||||
(RefRepeatComment[]) displayCommentArrays[REF_REPEATABLES];
|
||||
int currentStartRow = 0;
|
||||
for (RefRepeatComment comment : refRepeatComments) {
|
||||
Address checkAddress = comment.getAddress();
|
||||
if (refAddress.compareTo(checkAddress) <= 0) {
|
||||
return currentStartRow;
|
||||
}
|
||||
currentStartRow += comment.getCommentLineCount();
|
||||
}
|
||||
return currentStartRow;
|
||||
}
|
||||
|
||||
public boolean isRefRepeatRow(int eolRow) {
|
||||
boolean hasEol = hasEOL();
|
||||
boolean hasRepeatable = hasRepeatable();
|
||||
int numEol = getCommentLineCount(MY_EOLS);
|
||||
int numRepeatable = getCommentLineCount(MY_REPEATABLES);
|
||||
int numRefRepeats = getCommentLineCount(REF_REPEATABLES);
|
||||
|
||||
int beforeEol = 0;
|
||||
int beforeRepeatable = beforeEol + numEol;
|
||||
int beforeRefRepeats = beforeRepeatable;
|
||||
if (alwaysShowRepeatable || !hasEol) {
|
||||
beforeRefRepeats += numRepeatable;
|
||||
}
|
||||
int beforeAutomatic = beforeRefRepeats;
|
||||
if (alwaysShowRefRepeats || !(hasEol || hasRepeatable)) {
|
||||
beforeAutomatic += numRefRepeats;
|
||||
}
|
||||
|
||||
return ((eolRow >= beforeRefRepeats) && (eolRow < beforeAutomatic));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,819 @@
|
||||
/* ###
|
||||
* 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;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import docking.widgets.fieldpanel.support.RowColLocation;
|
||||
import ghidra.app.util.viewer.field.EolEnablement;
|
||||
import ghidra.app.util.viewer.field.EolExtraCommentsOption;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.mem.*;
|
||||
import ghidra.program.model.scalar.Scalar;
|
||||
import ghidra.program.model.symbol.*;
|
||||
import ghidra.program.util.*;
|
||||
import ghidra.util.StringUtilities;
|
||||
import util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* Utility class with methods to get comment information that can be displayed in the end of line
|
||||
* comment field. Each instance of this class is associated with a code unit. This class uses the
|
||||
* provided options to decide how to load and filter existing comments.
|
||||
*
|
||||
* <p>Comment types that can be shown include the End of Line comment for the code unit, the
|
||||
* Repeatable comment for the code unit, any repeatable comments for the code units that this code
|
||||
* unit has references to, and possibly a comment indicating the data at a code unit that is
|
||||
* referenced by this code unit.
|
||||
*/
|
||||
public class EolComments {
|
||||
|
||||
private static final String POINTER_ARROW = "-> ";
|
||||
|
||||
private CodeUnit codeUnit;
|
||||
|
||||
private List<String> eols = new ArrayList<>();
|
||||
private List<String> repeatables = new ArrayList<>();
|
||||
private List<RefRepeatComment> refRepeatables = new ArrayList<>();
|
||||
private List<String> autos = new ArrayList<>();
|
||||
private List<Reference> references = new ArrayList<>();
|
||||
|
||||
// used to signal the operand is already displaying a pointer reference, so there is no need for
|
||||
// this class to create a comment to do the same
|
||||
private boolean operandsShowReferences = false;
|
||||
|
||||
private int maxDisplayComments;
|
||||
private EolExtraCommentsOption extraCommentsOption;
|
||||
|
||||
public EolComments(CodeUnit cu, boolean operandsShowReferences, int maxDisplayComments,
|
||||
EolExtraCommentsOption extraCommentsOption) {
|
||||
this.codeUnit = cu;
|
||||
this.operandsShowReferences = operandsShowReferences;
|
||||
this.maxDisplayComments = maxDisplayComments;
|
||||
this.extraCommentsOption = extraCommentsOption;
|
||||
loadComments();
|
||||
}
|
||||
|
||||
private void loadComments() {
|
||||
loadEols();
|
||||
loadRepeatables();
|
||||
loadRefRepeatables();
|
||||
loadAutos();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current number of comments in this class. The value of this method will change
|
||||
* as this class is loading comments. After loading, this value will be fixed.
|
||||
* @return the size
|
||||
*/
|
||||
private int size() {
|
||||
int refRepeatablesSize = 0;
|
||||
for (RefRepeatComment item : refRepeatables) {
|
||||
refRepeatablesSize += item.getCommentLineCount();
|
||||
}
|
||||
return eols.size() + repeatables.size() + refRepeatablesSize + autos.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of comments that can be added before reaching the maximum number of
|
||||
* comments
|
||||
* @return the number of comments
|
||||
*/
|
||||
private int getAvailableSpace() {
|
||||
return maxDisplayComments - size();
|
||||
}
|
||||
|
||||
private void loadEols() {
|
||||
Collection<String> comments =
|
||||
Arrays.asList(codeUnit.getCommentAsArray(CodeUnit.EOL_COMMENT));
|
||||
addStrings(comments, eols);
|
||||
}
|
||||
|
||||
private void loadRepeatables() {
|
||||
boolean hasOtherComments = !eols.isEmpty();
|
||||
if (!extraCommentsOption.isShowingRepeatables(hasOtherComments)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Collection<String> comments =
|
||||
Arrays.asList(codeUnit.getCommentAsArray(CodeUnit.REPEATABLE_COMMENT));
|
||||
addStrings(comments, repeatables);
|
||||
}
|
||||
|
||||
private void loadRefRepeatables() {
|
||||
boolean hasOtherComments = !(eols.isEmpty() && repeatables.isEmpty());
|
||||
if (!extraCommentsOption.isShowingRefRepeatables(hasOtherComments)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Collection<RefRepeatComment> refRepeatableComments = getRepeatableComments(true);
|
||||
addRefRepeatables(refRepeatableComments, refRepeatables);
|
||||
}
|
||||
|
||||
private void loadAutos() {
|
||||
boolean hasOtherComments =
|
||||
!(eols.isEmpty() && repeatables.isEmpty() && refRepeatables.isEmpty());
|
||||
if (!extraCommentsOption.isShowingAutoComments(hasOtherComments)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Collection<String> comments = getReferencePreviews();
|
||||
addStrings(comments, autos);
|
||||
}
|
||||
|
||||
private void addRefRepeatables(Collection<RefRepeatComment> from,
|
||||
Collection<RefRepeatComment> to) {
|
||||
|
||||
int space = getAvailableSpace();
|
||||
int total = 0;
|
||||
for (RefRepeatComment item : from) {
|
||||
to.add(item);
|
||||
total += item.getCommentLineCount();
|
||||
if (total == space) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addStrings(Collection<String> from, Collection<String> to) {
|
||||
int space = getAvailableSpace();
|
||||
for (String item : from) {
|
||||
to.add(item);
|
||||
if (to.size() == space) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void loadReferences() {
|
||||
if (!references.isEmpty()) {
|
||||
return; // already loaded
|
||||
}
|
||||
|
||||
// arbitrary limit to prevent excessive consumption of resources
|
||||
int space = getAvailableSpace();
|
||||
int max = Math.min(100, space);
|
||||
Program program = codeUnit.getProgram();
|
||||
ReferenceManager referenceManager = program.getReferenceManager();
|
||||
AddressSet addresses = new AddressSet(codeUnit.getMinAddress(), codeUnit.getMaxAddress());
|
||||
AddressIterator it = referenceManager.getReferenceSourceIterator(addresses, true);
|
||||
while (it.hasNext() && references.size() < max) {
|
||||
Address fromAddress = it.next();
|
||||
Reference[] refs = referenceManager.getReferencesFrom(fromAddress);
|
||||
for (Reference r : refs) {
|
||||
references.add(r);
|
||||
}
|
||||
}
|
||||
Collections.sort(references);
|
||||
}
|
||||
|
||||
public boolean isShowingRepeatables() {
|
||||
return !repeatables.isEmpty();
|
||||
}
|
||||
|
||||
public boolean isShowingRefRepeatables() {
|
||||
return !refRepeatables.isEmpty();
|
||||
}
|
||||
|
||||
public boolean isShowingAutoComments() {
|
||||
return !autos.isEmpty();
|
||||
}
|
||||
|
||||
private Collection<String> getReferencePreviews() {
|
||||
|
||||
loadReferences();
|
||||
if (references.isEmpty()) {
|
||||
return getPreviewForNoReferences();
|
||||
}
|
||||
|
||||
int space = getAvailableSpace();
|
||||
Program program = codeUnit.getProgram();
|
||||
Set<String> set = new LinkedHashSet<>();
|
||||
for (Reference reference : references) {
|
||||
|
||||
if (set.size() >= space) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!isValidReference(program, reference)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
createAutoCommentFromReference(set, program, reference);
|
||||
}
|
||||
|
||||
return set;
|
||||
}
|
||||
|
||||
private Collection<String> getPreviewForNoReferences() {
|
||||
Set<String> set = new HashSet<>();
|
||||
String translatedString = getTranslatedString();
|
||||
if (translatedString != null) {
|
||||
set.add(translatedString);
|
||||
return set;
|
||||
}
|
||||
String pointerText = getUndefinedPointer(codeUnit);
|
||||
if (pointerText != null) {
|
||||
set.add(pointerText);
|
||||
return set;
|
||||
}
|
||||
return set;
|
||||
}
|
||||
|
||||
private String getTranslatedString() {
|
||||
if (codeUnit instanceof Data data && StringDataInstance.isString(data)) {
|
||||
StringDataInstance sdi = StringDataInstance.getStringDataInstance(data);
|
||||
if (sdi.hasTranslatedValue()) {
|
||||
// show the translated value
|
||||
return sdi.getStringRepresentation(sdi.isShowTranslation());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean isValidReference(Program program, Reference reference) {
|
||||
if (!reference.isMemoryReference()) {
|
||||
return false;
|
||||
}
|
||||
Address toAddress = reference.getToAddress();
|
||||
return isValidAddress(program, toAddress);
|
||||
}
|
||||
|
||||
private void createAutoCommentFromReference(Set<String> results, Program program,
|
||||
Reference reference) {
|
||||
|
||||
Address toAddress = reference.getToAddress();
|
||||
if (createFunctionCallPreview(results, reference, program, toAddress)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Data data = getData(program, toAddress);
|
||||
if (data == null) {
|
||||
return; // nothing there
|
||||
}
|
||||
|
||||
if (createIndirectDataReferencePreview(results, reference, program, toAddress, data)) {
|
||||
return;
|
||||
}
|
||||
|
||||
handleDirectDataReferencePreview(results, toAddress, data);
|
||||
}
|
||||
|
||||
private Data getData(Program program, Address toAddr) {
|
||||
Data data = program.getListing().getDataAt(toAddr);
|
||||
if (data == null) {
|
||||
data = program.getListing().getDataContaining(toAddr);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
private void handleDirectDataReferencePreview(Set<String> set, Address address, Data data) {
|
||||
|
||||
Object value = data.getValue();
|
||||
if (value instanceof Scalar) {
|
||||
Scalar scalar = (Scalar) value;
|
||||
if (scalar.getSignedValue() == 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
String dataRepresentation = getDataValueRepresentation(address, data);
|
||||
if (!StringUtils.isBlank(dataRepresentation)) {
|
||||
set.add("= " + dataRepresentation);
|
||||
}
|
||||
}
|
||||
|
||||
private String getDataValueRepresentation(Address dataAccessAddress, Data data) {
|
||||
if (extraCommentsOption.useAbbreviatedComments()) {
|
||||
if (isOffcut(dataAccessAddress, data)) {
|
||||
return getOffcutString(dataAccessAddress, data);
|
||||
}
|
||||
}
|
||||
return data.getDefaultValueRepresentation();
|
||||
}
|
||||
|
||||
private boolean isOffcut(Address address, CodeUnit cu) {
|
||||
if (cu == null) {
|
||||
return false;
|
||||
}
|
||||
return !cu.getMinAddress().equals(address);
|
||||
}
|
||||
|
||||
private String getOffcutString(Address offcutAddress, Data data) {
|
||||
Address dataAddress = data.getMinAddress();
|
||||
int diff = (int) offcutAddress.subtract(dataAddress);
|
||||
DataType dt = data.getBaseDataType();
|
||||
return getOffcutString(data, dataAddress, diff, dt);
|
||||
}
|
||||
|
||||
private String getOffcutString(Data data, Address dataAddress, int diff, DataType dt) {
|
||||
if (StringDataInstance.isString(data)) {
|
||||
StringDataInstance string = StringDataInstance.getStringDataInstance(data);
|
||||
string = string.getByteOffcut(diff);
|
||||
return string.getStringRepresentation();
|
||||
}
|
||||
if (!data.hasStringValue()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int length = data.getLength();
|
||||
if (diff >= length) {
|
||||
// not sure if this can happen--just use the default
|
||||
return data.getDefaultValueRepresentation();
|
||||
}
|
||||
|
||||
DumbMemBufferImpl mb = new DumbMemBufferImpl(data.getMemory(), dataAddress.add(diff));
|
||||
return dt.getRepresentation(mb, data, length - diff);
|
||||
}
|
||||
|
||||
private boolean createIndirectDataReferencePreview(Set<String> set, Reference reference,
|
||||
Program program, Address toAddress, Data data) {
|
||||
|
||||
RefType type = reference.getReferenceType();
|
||||
if (!type.isIndirect()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (createDefinedDataPointerPreview(set, program, reference, data)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
createUndefinedPointerPreview(set, program, toAddress, data);
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean createDefinedDataPointerPreview(Set<String> set, Program program,
|
||||
Reference reference, Data data) {
|
||||
|
||||
if (!data.isPointer()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SymbolTable symbolTable = program.getSymbolTable();
|
||||
ReferenceManager referenceManager = program.getReferenceManager();
|
||||
Reference pointerReference =
|
||||
referenceManager.getPrimaryReferenceFrom(reference.getToAddress(), 0);
|
||||
if (pointerReference != null) {
|
||||
Symbol symbol = symbolTable.getPrimarySymbol(pointerReference.getToAddress());
|
||||
if (operandIsShowingSymbolReference(symbol, reference)) {
|
||||
return true; // already displayed by operand
|
||||
}
|
||||
set.add(POINTER_ARROW + symbol.getName());
|
||||
return true;
|
||||
}
|
||||
|
||||
Address address = (Address) data.getValue();
|
||||
if (address != null && address.getOffset() != 0) {
|
||||
set.add(POINTER_ARROW + address);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean operandIsShowingSymbolReference(Symbol symbol, Reference reference) {
|
||||
if (operandsShowReferences && reference.getOperandIndex() != CodeUnit.MNEMONIC) {
|
||||
if (!symbol.isDynamic()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void createUndefinedPointerPreview(Set<String> list, Program program, Address toAddress,
|
||||
Data data) {
|
||||
|
||||
if (data.isDefined()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if no data is defined at the address, see if it is a pointer
|
||||
SymbolTable symbolTable = program.getSymbolTable();
|
||||
PseudoDisassembler dis = new PseudoDisassembler(program);
|
||||
Address pointerAddress = dis.getIndirectAddr(toAddress);
|
||||
if (!isValidAddress(program, pointerAddress)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Symbol symbol = symbolTable.getPrimarySymbol(pointerAddress);
|
||||
if (symbol != null) {
|
||||
list.add(POINTER_ARROW + symbol.getName());
|
||||
}
|
||||
else {
|
||||
list.add(POINTER_ARROW + pointerAddress);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean createFunctionCallPreview(Set<String> set, Reference reference, Program program,
|
||||
Address toAddress) {
|
||||
|
||||
if (extraCommentsOption.getAutoFunction() == EolEnablement.NEVER) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RefType type = reference.getReferenceType();
|
||||
if (!type.isFlow()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (type.isIndirect()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (type.isCall()) {
|
||||
String signature = getFunctionSignature(program, toAddress);
|
||||
if (signature != null) {
|
||||
set.add(signature);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private String getUndefinedPointer(CodeUnit cu) {
|
||||
if (!(cu instanceof Data)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Data data = (Data) cu;
|
||||
DataType dataType = data.getDataType();
|
||||
if (!(dataType instanceof Undefined || dataType instanceof DefaultDataType)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Program program = cu.getProgram();
|
||||
if (isEntireMemorySpace(program)) {
|
||||
// everything looks like a pointer when a program represents the entire address space
|
||||
return null;
|
||||
}
|
||||
|
||||
int align = program.getLanguage().getInstructionAlignment();
|
||||
Address codeUnitAddress = cu.getAddress();
|
||||
long codeUnitOffset = codeUnitAddress.getOffset();
|
||||
if ((codeUnitOffset % align) != 0) {
|
||||
return null; // not aligned
|
||||
}
|
||||
|
||||
return createPointerString(program, codeUnitAddress);
|
||||
}
|
||||
|
||||
private String createPointerString(Program program, Address codeUnitAddress) {
|
||||
int pointerSize = program.getDefaultPointerSize();
|
||||
long offset = 0;
|
||||
Memory memory = program.getMemory();
|
||||
try {
|
||||
switch (pointerSize) {
|
||||
case 4:
|
||||
int addrInt = memory.getInt(codeUnitAddress);
|
||||
offset = (addrInt & 0xffffffffL);
|
||||
offset *= codeUnitAddress.getAddressSpace().getAddressableUnitSize();
|
||||
break;
|
||||
case 8:
|
||||
offset = memory.getLong(codeUnitAddress);
|
||||
break;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
Address potentialAddr = codeUnitAddress.getNewAddress(offset);
|
||||
if (memory.contains(potentialAddr)) {
|
||||
return "? -> " + potentialAddr.toString();
|
||||
}
|
||||
}
|
||||
catch (MemoryAccessException | AddressOutOfBoundsException e) {
|
||||
// handled below
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean isEntireMemorySpace(Program program) {
|
||||
Address min = program.getMinAddress();
|
||||
Address max = program.getMaxAddress();
|
||||
AddressSpace space = max.getAddressSpace();
|
||||
return min.getOffset() == 0 && max.equals(space.getMaxAddress());
|
||||
}
|
||||
|
||||
private String getFunctionSignature(Program program, Address a) {
|
||||
|
||||
// Note: Users have complained the 'undefined' return type clutters the display. Update
|
||||
// signature to omit return type if it is undefined.
|
||||
Function f = program.getFunctionManager().getFunctionAt(a);
|
||||
if (f != null) {
|
||||
return f.getPrototypeString(false, false);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this address could be a valid address in the program. 0 id not a valid address.
|
||||
*
|
||||
* @param program program to check if address is valid within
|
||||
* @param address address in program to be checked
|
||||
* @return true if this is a valid address
|
||||
*/
|
||||
private boolean isValidAddress(Program program, Address address) {
|
||||
if (address == null) {
|
||||
return false;
|
||||
}
|
||||
if (!program.getMemory().contains(address)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
long offset = address.getOffset();
|
||||
if (offset == 0x0 || offset == 0xffffffff || offset == 0xffff || offset == 0xff) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private Collection<RefRepeatComment> getRepeatableComments(boolean showAll) {
|
||||
|
||||
loadReferences();
|
||||
int space = getAvailableSpace();
|
||||
Set<RefRepeatComment> set = new LinkedHashSet<>();
|
||||
for (int i = 0; i < references.size() && set.size() < space; ++i) {
|
||||
Reference reference = references.get(i);
|
||||
if (!showAll && !reference.isPrimary()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Address address = reference.getToAddress();
|
||||
String[] comment = getComment(address);
|
||||
if (!CollectionUtils.isBlank(comment)) {
|
||||
set.add(new RefRepeatComment(address, comment));
|
||||
}
|
||||
}
|
||||
|
||||
return set;
|
||||
}
|
||||
|
||||
private String[] getComment(Address address) {
|
||||
|
||||
Program program = codeUnit.getProgram();
|
||||
Listing listing = program.getListing();
|
||||
|
||||
// prefer listing comments first since there may not be a code unit at this address
|
||||
String repeatable = listing.getComment(CodeUnit.REPEATABLE_COMMENT, address);
|
||||
if (repeatable != null) {
|
||||
return StringUtilities.toLines(repeatable);
|
||||
}
|
||||
|
||||
CodeUnit cu = listing.getCodeUnitAt(address);
|
||||
if (cu == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Function f = listing.getFunctionAt(address);
|
||||
if (f != null) {
|
||||
return f.getRepeatableCommentAsArray();
|
||||
}
|
||||
|
||||
return cu.getCommentAsArray(CodeUnit.REPEATABLE_COMMENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all comments loaded by this class
|
||||
* @return the comments
|
||||
*/
|
||||
public List<String> getComments() {
|
||||
List<String> list = new ArrayList<>();
|
||||
list.addAll(eols);
|
||||
list.addAll(repeatables);
|
||||
for (RefRepeatComment comment : refRepeatables) {
|
||||
list.addAll(Arrays.asList(comment.getCommentLines()));
|
||||
}
|
||||
list.addAll(autos);
|
||||
return list;
|
||||
}
|
||||
|
||||
private String[] getCommentsArray() {
|
||||
List<String> comments = getComments();
|
||||
return comments.toArray(new String[comments.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the End of Line comments
|
||||
* @return the comments
|
||||
*/
|
||||
public List<String> getEOLComments() {
|
||||
return Collections.unmodifiableList(eols);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the repeatable comments
|
||||
* @return the comments
|
||||
*/
|
||||
public List<String> getRepeatableComments() {
|
||||
return Collections.unmodifiableList(repeatables);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the repeatable comments at the "to reference"s
|
||||
* @return the comments
|
||||
*/
|
||||
public List<RefRepeatComment> getReferencedRepeatableComments() {
|
||||
return Collections.unmodifiableList(refRepeatables);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the automatic comments
|
||||
* @return the comments
|
||||
*/
|
||||
public List<String> getAutomaticComment() {
|
||||
return Collections.unmodifiableList(autos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
StringBuilder buffy = new StringBuilder();
|
||||
if (eols.isEmpty()) {
|
||||
buffy.append("EOLs: ").append(eols);
|
||||
}
|
||||
|
||||
if (!repeatables.isEmpty()) {
|
||||
buffy.append("My Repeatables: ").append(repeatables);
|
||||
}
|
||||
|
||||
if (!refRepeatables.isEmpty()) {
|
||||
buffy.append("Ref Repeatables: ").append(refRepeatables);
|
||||
}
|
||||
|
||||
if (!autos.isEmpty()) {
|
||||
buffy.append("My Automatic: ").append(autos);
|
||||
}
|
||||
|
||||
return buffy.toString();
|
||||
}
|
||||
|
||||
private int getEolRow(ProgramLocation loc) {
|
||||
int numBefore = 0;
|
||||
if (loc instanceof EolCommentFieldLocation) {
|
||||
EolCommentFieldLocation commentLoc = (EolCommentFieldLocation) loc;
|
||||
return numBefore + commentLoc.getCurrentCommentRow();
|
||||
}
|
||||
|
||||
numBefore += eols.size();
|
||||
|
||||
if (loc instanceof RepeatableCommentFieldLocation) {
|
||||
RepeatableCommentFieldLocation commentLoc = (RepeatableCommentFieldLocation) loc;
|
||||
return numBefore + commentLoc.getCurrentCommentRow();
|
||||
}
|
||||
|
||||
numBefore += repeatables.size();
|
||||
|
||||
if (loc instanceof RefRepeatCommentFieldLocation) {
|
||||
RefRepeatCommentFieldLocation commentLoc = (RefRepeatCommentFieldLocation) loc;
|
||||
Address desiredAddress = commentLoc.getReferencedRepeatableAddress();
|
||||
int startRowInRefRepeats = getCommentStartRow(desiredAddress);
|
||||
int rowInComment =
|
||||
(hasRefRepeatComment(desiredAddress)) ? commentLoc.getCurrentCommentRow() : 0;
|
||||
return numBefore + startRowInRefRepeats + rowInComment;
|
||||
}
|
||||
|
||||
numBefore += refRepeatables.size();
|
||||
|
||||
if (loc instanceof AutomaticCommentFieldLocation) {
|
||||
AutomaticCommentFieldLocation commentLoc = (AutomaticCommentFieldLocation) loc;
|
||||
return numBefore + commentLoc.getCurrentCommentRow();
|
||||
}
|
||||
|
||||
numBefore += autos.size();
|
||||
|
||||
return numBefore;
|
||||
}
|
||||
|
||||
private boolean hasRefRepeatComment(Address desiredAddress) {
|
||||
for (RefRepeatComment comment : refRepeatables) {
|
||||
Address checkAddress = comment.getAddress();
|
||||
if (desiredAddress.equals(checkAddress)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public RowColLocation getRowCol(CommentFieldLocation cloc) {
|
||||
int offset = cloc.getCharOffset();
|
||||
if (cloc instanceof RefRepeatCommentFieldLocation) {
|
||||
RefRepeatCommentFieldLocation commentLoc = (RefRepeatCommentFieldLocation) cloc;
|
||||
Address desiredAddress = commentLoc.getReferencedRepeatableAddress();
|
||||
if (!hasRefRepeatComment(desiredAddress)) {
|
||||
offset = 0;
|
||||
}
|
||||
}
|
||||
int eolRow = getEolRow(cloc);
|
||||
return new RowColLocation(eolRow, offset);
|
||||
}
|
||||
|
||||
public ProgramLocation getLocation(int eolRow, int eolColumn) {
|
||||
|
||||
if (eolRow < 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int numEol = eols.size();
|
||||
int numRepeatable = repeatables.size();
|
||||
int numRefRepeats = refRepeatables.size();
|
||||
int numAutomatic = autos.size();
|
||||
|
||||
int beforeRepeatable = numEol;
|
||||
int beforeRefRepeats = beforeRepeatable;
|
||||
if (!repeatables.isEmpty()) {
|
||||
beforeRefRepeats += numRepeatable;
|
||||
}
|
||||
|
||||
int beforeAutomatic = beforeRefRepeats;
|
||||
if (!refRepeatables.isEmpty()) {
|
||||
beforeAutomatic += numRefRepeats;
|
||||
}
|
||||
|
||||
int numTotal = beforeAutomatic;
|
||||
if (!autos.isEmpty()) {
|
||||
numTotal += numAutomatic;
|
||||
}
|
||||
|
||||
Program program = codeUnit.getProgram();
|
||||
Address minAddress = codeUnit.getMinAddress();
|
||||
int[] cpath = null;
|
||||
if (codeUnit instanceof Data) {
|
||||
cpath = ((Data) codeUnit).getComponentPath();
|
||||
}
|
||||
if (eolRow < beforeRepeatable) {
|
||||
return new EolCommentFieldLocation(program, minAddress, cpath,
|
||||
getCommentsArray(), eolRow, eolColumn, eolRow);
|
||||
}
|
||||
|
||||
if (eolRow < beforeRefRepeats) {
|
||||
return new RepeatableCommentFieldLocation(program, minAddress, cpath,
|
||||
getCommentsArray(), eolRow, eolColumn, eolRow - beforeRepeatable);
|
||||
}
|
||||
|
||||
if (eolRow < beforeAutomatic) {
|
||||
int rowInAllRefRepeats = eolRow - beforeRefRepeats;
|
||||
return new RefRepeatCommentFieldLocation(program, minAddress, cpath,
|
||||
getCommentsArray(), eolRow, eolColumn, getRefRepeatRow(rowInAllRefRepeats),
|
||||
getRefRepeatAddress(rowInAllRefRepeats));
|
||||
}
|
||||
|
||||
if (eolRow < numTotal) {
|
||||
return new AutomaticCommentFieldLocation(program, minAddress, cpath,
|
||||
getCommentsArray(), eolRow, eolColumn, eolRow - beforeAutomatic);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private Address getRefRepeatAddress(int row) {
|
||||
int currentRow = 0;
|
||||
for (RefRepeatComment comment : refRepeatables) {
|
||||
int lineCount = comment.getCommentLineCount();
|
||||
if (row < (currentRow + lineCount)) {
|
||||
return comment.getAddress();
|
||||
}
|
||||
currentRow += lineCount;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private int getRefRepeatRow(int row) {
|
||||
int currentRow = 0;
|
||||
for (RefRepeatComment comment : refRepeatables) {
|
||||
int numRows = comment.getCommentLineCount();
|
||||
if (row < (currentRow + numRows)) {
|
||||
return row - currentRow;
|
||||
}
|
||||
currentRow += numRows;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private int getCommentStartRow(Address address) {
|
||||
int currentRow = 0;
|
||||
for (RefRepeatComment comment : refRepeatables) {
|
||||
Address commentAddress = comment.getAddress();
|
||||
if (address.compareTo(commentAddress) <= 0) {
|
||||
return currentRow;
|
||||
}
|
||||
currentRow += comment.getCommentLineCount();
|
||||
}
|
||||
return currentRow;
|
||||
}
|
||||
}
|
@ -17,10 +17,12 @@ package ghidra.app.util.exporter;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import generic.theme.GThemeDefaults.Colors.Messages;
|
||||
import ghidra.app.util.DisplayableEol;
|
||||
import ghidra.app.util.EolComments;
|
||||
import ghidra.app.util.template.TemplateSimplifier;
|
||||
import ghidra.app.util.viewer.field.EolExtraCommentsOption;
|
||||
import ghidra.framework.plugintool.ServiceProvider;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSetView;
|
||||
@ -56,10 +58,6 @@ class ProgramTextWriter {
|
||||
ProgramTextOptions options, ServiceProvider provider) throws FileNotFoundException {
|
||||
|
||||
this.options = options;
|
||||
// Exit if options are INVALID
|
||||
int len = options.getAddrWidth() + options.getBytesWidth() + options.getPreMnemonicWidth() +
|
||||
options.getMnemonicWidth() + options.getOperandWidth() + options.getEolWidth();
|
||||
|
||||
this.program = program;
|
||||
this.listing = program.getListing();
|
||||
this.memory = program.getMemory();
|
||||
@ -293,29 +291,7 @@ class ProgramTextWriter {
|
||||
//// End of Line Area //////////////////////////////////////////
|
||||
|
||||
if (options.isShowComments()) {
|
||||
DisplayableEol displayableEol = new DisplayableEol(currentCodeUnit, false, false,
|
||||
false, true, 6 /* arbitrary! */, true, true);
|
||||
String[] eol = displayableEol.getComments();
|
||||
if (eol != null && eol.length > 0) {
|
||||
len = options.getAddrWidth() + options.getBytesWidth() +
|
||||
options.getPreMnemonicWidth() + options.getMnemonicWidth() +
|
||||
options.getOperandWidth();
|
||||
|
||||
String fill = genFill(len);
|
||||
|
||||
for (int i = 0; i < eol.length; ++i) {
|
||||
if (i > 0) {
|
||||
buffy.append(fill);
|
||||
}
|
||||
String eolcmt = options.getCommentPrefix() + eol[i];
|
||||
if (eolcmt.length() > options.getEolWidth()) {
|
||||
eolcmt = clip(eolcmt, options.getEolWidth(), true, true);
|
||||
}
|
||||
buffy.append(eolcmt);
|
||||
writer.println(buffy.toString());
|
||||
buffy = new StringBuilder();
|
||||
}
|
||||
}
|
||||
addComments(currentCodeUnit);
|
||||
}
|
||||
|
||||
if (buffy.length() > 0) {
|
||||
@ -369,6 +345,35 @@ class ProgramTextWriter {
|
||||
writer.close();
|
||||
}
|
||||
|
||||
private void addComments(CodeUnit currentCodeUnit) {
|
||||
|
||||
EolExtraCommentsOption eolOption = new EolExtraCommentsOption();
|
||||
EolComments eolComments =
|
||||
new EolComments(currentCodeUnit, true, 6 /* arbitrary */, eolOption);
|
||||
List<String> comments = eolComments.getComments();
|
||||
if (comments.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int len = options.getAddrWidth() + options.getBytesWidth() +
|
||||
options.getPreMnemonicWidth() + options.getMnemonicWidth() +
|
||||
options.getOperandWidth();
|
||||
|
||||
String fill = genFill(len);
|
||||
for (int i = 0; i < comments.size(); ++i) {
|
||||
if (i > 0) {
|
||||
buffy.append(fill);
|
||||
}
|
||||
String text = options.getCommentPrefix() + comments.get(i);
|
||||
if (text.length() > options.getEolWidth()) {
|
||||
text = clip(text, options.getEolWidth(), true, true);
|
||||
}
|
||||
buffy.append(text);
|
||||
writer.println(buffy.toString());
|
||||
buffy = new StringBuilder();
|
||||
}
|
||||
}
|
||||
|
||||
private void insertUndefinedBytesRemovedMarker(Address bytesRemovedRangeStart,
|
||||
Address bytesRemovedRangeEnd) {
|
||||
|
||||
|
@ -16,6 +16,7 @@
|
||||
package ghidra.app.util.viewer.field;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.beans.PropertyEditor;
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@ -28,57 +29,49 @@ import ghidra.app.util.viewer.field.ListingColors.CommentColors;
|
||||
import ghidra.app.util.viewer.format.FieldFormatModel;
|
||||
import ghidra.app.util.viewer.options.OptionsGui;
|
||||
import ghidra.app.util.viewer.proxy.ProxyObj;
|
||||
import ghidra.framework.options.Options;
|
||||
import ghidra.framework.options.ToolOptions;
|
||||
import ghidra.framework.options.*;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.util.*;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.bean.field.AnnotatedTextFieldElement;
|
||||
import ghidra.util.exception.AssertException;
|
||||
|
||||
/**
|
||||
* Generates End of line comment Fields.
|
||||
*/
|
||||
* Generates End of line comment Fields.
|
||||
*/
|
||||
public class EolCommentFieldFactory extends FieldFactory {
|
||||
public static final String FIELD_NAME = "EOL Comment";
|
||||
private static final String GROUP_TITLE = "EOL Comments Field";
|
||||
private static final String SEMICOLON_PREFIX = "; ";
|
||||
public static final String ENABLE_WORD_WRAP_MSG =
|
||||
|
||||
public static final String ENABLE_WORD_WRAP_KEY =
|
||||
GROUP_TITLE + Options.DELIMITER + FieldUtils.WORD_WRAP_OPTION_NAME;
|
||||
public static final String MAX_DISPLAY_LINES_MSG =
|
||||
GROUP_TITLE + Options.DELIMITER + "Maximum Lines To Display";
|
||||
public static final String ENABLE_SHOW_SEMICOLON_MSG =
|
||||
GROUP_TITLE + Options.DELIMITER + "Show Semicolon at Start of Each Line";
|
||||
public static final String ENABLE_ALWAYS_SHOW_REPEATABLE_MSG =
|
||||
GROUP_TITLE + Options.DELIMITER + "Always Show the Repeatable Comment";
|
||||
public static final String ENABLE_ALWAYS_SHOW_REF_REPEATABLE_MSG =
|
||||
GROUP_TITLE + Options.DELIMITER + "Always Show the Referenced Repeatable Comments";
|
||||
public static final String ENABLE_ALWAYS_SHOW_AUTOMATIC_MSG =
|
||||
GROUP_TITLE + Options.DELIMITER + "Always Show the Automatic Comment";
|
||||
public static final String USE_ABBREVIATED_AUTOMITIC_COMMENT_MSG =
|
||||
GROUP_TITLE + Options.DELIMITER + "Use Abbreviated Automatic Comments";
|
||||
public static final String SHOW_FUNCTION_AUTOMITIC_COMMENT_MSG =
|
||||
GROUP_TITLE + Options.DELIMITER + "Show Function Reference Automatic Comments";
|
||||
public static final String ENABLE_PREPEND_REF_ADDRESS_MSG =
|
||||
GROUP_TITLE + Options.DELIMITER + "Prepend the Address to Each Referenced Comment";
|
||||
public static final String MAX_DISPLAY_LINES_KEY =
|
||||
GROUP_TITLE + Options.DELIMITER + "Maximum Lines";
|
||||
public static final String ENABLE_SHOW_SEMICOLON_KEY =
|
||||
GROUP_TITLE + Options.DELIMITER + "Prepend Semicolon";
|
||||
public static final String ENABLE_PREPEND_REF_ADDRESS_KEY =
|
||||
GROUP_TITLE + Options.DELIMITER + "Prepend Address to References";
|
||||
public static final String EXTRA_COMMENT_KEY =
|
||||
GROUP_TITLE + Options.DELIMITER + "Auto Comments";
|
||||
|
||||
public static final Color DEFAULT_COLOR = Palette.BLUE;
|
||||
|
||||
private boolean isWordWrap;
|
||||
private int maxDisplayLines;
|
||||
private boolean showSemicolon;
|
||||
private boolean alwaysShowRepeatable;
|
||||
private boolean alwaysShowRefRepeatables;
|
||||
private boolean alwaysShowAutomatic;
|
||||
private boolean useAbbreviatedAutomatic;
|
||||
private boolean showAutomaticFunctions;
|
||||
private boolean prependRefAddress;
|
||||
private int repeatableCommentStyle;
|
||||
private int automaticCommentStyle;
|
||||
private int refRepeatableCommentStyle;
|
||||
|
||||
// The codeUnitFormatOptions is used to monitor "follow pointer..." option to avoid
|
||||
// duplication of data within auto-comment. We don't bother adding a listener
|
||||
// to kick the model since this is done by the operand field.
|
||||
private EolExtraCommentsOption extraCommentsOption = new EolExtraCommentsOption();
|
||||
private PropertyEditor extraCommmentsEditor = new EolExtraCommentsPropertyEditor();
|
||||
|
||||
// The codeUnitFormatOptions is used to monitor "follow pointer..." option to avoid duplication
|
||||
// of data within auto-comment. We don't bother adding a listener to kick the model since this
|
||||
// is done by the operand field.
|
||||
private BrowserCodeUnitFormatOptions codeUnitFormatOptions;
|
||||
|
||||
/**
|
||||
@ -100,55 +93,49 @@ public class EolCommentFieldFactory extends FieldFactory {
|
||||
super(FIELD_NAME, model, hlProvider, displayOptions, fieldOptions);
|
||||
HelpLocation hl = new HelpLocation("CodeBrowserPlugin", "EOL_Comments_Field");
|
||||
|
||||
fieldOptions.registerOption(MAX_DISPLAY_LINES_MSG, 6, hl,
|
||||
fieldOptions.registerOption(MAX_DISPLAY_LINES_KEY, 6, hl,
|
||||
"The maximum number of lines used to display the end-of-line comment.");
|
||||
fieldOptions.registerOption(ENABLE_WORD_WRAP_MSG, false, hl,
|
||||
fieldOptions.registerOption(ENABLE_WORD_WRAP_KEY, false, hl,
|
||||
FieldUtils.WORD_WRAP_OPTION_DESCRIPTION);
|
||||
|
||||
fieldOptions.registerOption(ENABLE_SHOW_SEMICOLON_MSG, false, hl,
|
||||
fieldOptions.registerOption(ENABLE_SHOW_SEMICOLON_KEY, false, hl,
|
||||
"Displays a semi-colon before each line in the end-of-line comment. " +
|
||||
"This option is ignored if word wrapping is on.");
|
||||
|
||||
fieldOptions.registerOption(ENABLE_ALWAYS_SHOW_REPEATABLE_MSG, false, hl,
|
||||
"Displays all referenced repeatable comments even if there is an EOL " +
|
||||
"or repeatable comment at the code unit.");
|
||||
|
||||
fieldOptions.registerOption(ENABLE_ALWAYS_SHOW_REF_REPEATABLE_MSG, false, hl,
|
||||
"Displays all referenced repeatable comments even if there is an EOL " +
|
||||
"or repeatable comment at the code unit.");
|
||||
fieldOptions.registerOption(ENABLE_ALWAYS_SHOW_AUTOMATIC_MSG, false, hl,
|
||||
"Displays an automatic comment whenever one exists instead of only if there " +
|
||||
"aren't any EOL or repeatable comments.");
|
||||
fieldOptions.registerOption(USE_ABBREVIATED_AUTOMITIC_COMMENT_MSG, true, hl,
|
||||
"When showing automatic comments, show the smallest amount of information possible");
|
||||
fieldOptions.registerOption(SHOW_FUNCTION_AUTOMITIC_COMMENT_MSG, true, hl,
|
||||
"When showing automatic comments, show direct function references");
|
||||
|
||||
fieldOptions.registerOption(ENABLE_PREPEND_REF_ADDRESS_MSG, false, hl,
|
||||
fieldOptions.registerOption(ENABLE_PREPEND_REF_ADDRESS_KEY, false, hl,
|
||||
"Displays the address before each referenced repeatable comment.");
|
||||
|
||||
maxDisplayLines = fieldOptions.getInt(MAX_DISPLAY_LINES_MSG, 6);
|
||||
isWordWrap = fieldOptions.getBoolean(ENABLE_WORD_WRAP_MSG, false);
|
||||
maxDisplayLines = fieldOptions.getInt(MAX_DISPLAY_LINES_KEY, 6);
|
||||
isWordWrap = fieldOptions.getBoolean(ENABLE_WORD_WRAP_KEY, false);
|
||||
repeatableCommentStyle =
|
||||
displayOptions.getInt(OptionsGui.COMMENT_REPEATABLE.getStyleOptionName(), -1);
|
||||
automaticCommentStyle =
|
||||
displayOptions.getInt(OptionsGui.COMMENT_AUTO.getStyleOptionName(), -1);
|
||||
refRepeatableCommentStyle =
|
||||
displayOptions.getInt(OptionsGui.COMMENT_REF_REPEAT.getStyleOptionName(), -1);
|
||||
showSemicolon = fieldOptions.getBoolean(ENABLE_SHOW_SEMICOLON_MSG, false);
|
||||
alwaysShowRepeatable = fieldOptions.getBoolean(ENABLE_ALWAYS_SHOW_REPEATABLE_MSG, false);
|
||||
alwaysShowRefRepeatables =
|
||||
fieldOptions.getBoolean(ENABLE_ALWAYS_SHOW_REF_REPEATABLE_MSG, false);
|
||||
alwaysShowAutomatic = fieldOptions.getBoolean(ENABLE_ALWAYS_SHOW_AUTOMATIC_MSG, false);
|
||||
useAbbreviatedAutomatic =
|
||||
fieldOptions.getBoolean(USE_ABBREVIATED_AUTOMITIC_COMMENT_MSG, true);
|
||||
showAutomaticFunctions = fieldOptions.getBoolean(SHOW_FUNCTION_AUTOMITIC_COMMENT_MSG, true);
|
||||
|
||||
prependRefAddress = fieldOptions.getBoolean(ENABLE_PREPEND_REF_ADDRESS_MSG, false);
|
||||
showSemicolon = fieldOptions.getBoolean(ENABLE_SHOW_SEMICOLON_KEY, false);
|
||||
prependRefAddress = fieldOptions.getBoolean(ENABLE_PREPEND_REF_ADDRESS_KEY, false);
|
||||
|
||||
fieldOptions.getOptions(GROUP_TITLE).setOptionsHelpLocation(hl);
|
||||
|
||||
codeUnitFormatOptions = new BrowserCodeUnitFormatOptions(fieldOptions, true);
|
||||
|
||||
setupAutoCommentOptions(fieldOptions, hl);
|
||||
}
|
||||
|
||||
private void setupAutoCommentOptions(Options fieldOptions, HelpLocation hl) {
|
||||
fieldOptions.registerOption(EXTRA_COMMENT_KEY, OptionType.CUSTOM_TYPE,
|
||||
new EolExtraCommentsOption(), hl, "The group of auto comment options",
|
||||
extraCommmentsEditor);
|
||||
CustomOption customOption = fieldOptions.getCustomOption(EXTRA_COMMENT_KEY, null);
|
||||
|
||||
if (!(customOption instanceof EolExtraCommentsOption)) {
|
||||
throw new AssertException("Someone set an option for " + EXTRA_COMMENT_KEY +
|
||||
" that is not the expected " +
|
||||
EolExtraCommentsOption.class.getName() + " type.");
|
||||
}
|
||||
|
||||
extraCommentsOption = (EolExtraCommentsOption) customOption;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -161,28 +148,19 @@ public class EolCommentFieldFactory extends FieldFactory {
|
||||
@Override
|
||||
public void fieldOptionsChanged(Options options, String optionName, Object oldValue,
|
||||
Object newValue) {
|
||||
if (optionName.equals(MAX_DISPLAY_LINES_MSG)) {
|
||||
if (optionName.equals(MAX_DISPLAY_LINES_KEY)) {
|
||||
setMaximumLinesToDisplay(((Integer) newValue).intValue(), options);
|
||||
}
|
||||
else if (optionName.equals(ENABLE_WORD_WRAP_MSG)) {
|
||||
else if (optionName.equals(ENABLE_WORD_WRAP_KEY)) {
|
||||
isWordWrap = ((Boolean) newValue).booleanValue();
|
||||
}
|
||||
else if (optionName.equals(ENABLE_SHOW_SEMICOLON_MSG)) {
|
||||
else if (optionName.equals(ENABLE_SHOW_SEMICOLON_KEY)) {
|
||||
showSemicolon = ((Boolean) newValue).booleanValue();
|
||||
}
|
||||
else if (optionName.equals(ENABLE_ALWAYS_SHOW_REPEATABLE_MSG)) {
|
||||
alwaysShowRepeatable = ((Boolean) newValue).booleanValue();
|
||||
else if (optionName.equals(EXTRA_COMMENT_KEY)) {
|
||||
extraCommentsOption = (EolExtraCommentsOption) newValue;
|
||||
}
|
||||
else if (optionName.equals(ENABLE_ALWAYS_SHOW_REF_REPEATABLE_MSG)) {
|
||||
alwaysShowRefRepeatables = ((Boolean) newValue).booleanValue();
|
||||
}
|
||||
else if (optionName.equals(ENABLE_ALWAYS_SHOW_AUTOMATIC_MSG)) {
|
||||
alwaysShowAutomatic = ((Boolean) newValue).booleanValue();
|
||||
}
|
||||
else if (optionName.equals(USE_ABBREVIATED_AUTOMITIC_COMMENT_MSG)) {
|
||||
useAbbreviatedAutomatic = ((Boolean) newValue).booleanValue();
|
||||
}
|
||||
else if (optionName.equals(ENABLE_PREPEND_REF_ADDRESS_MSG)) {
|
||||
else if (optionName.equals(ENABLE_PREPEND_REF_ADDRESS_KEY)) {
|
||||
prependRefAddress = ((Boolean) newValue).booleanValue();
|
||||
}
|
||||
}
|
||||
@ -249,7 +227,7 @@ public class EolCommentFieldFactory extends FieldFactory {
|
||||
private void setMaximumLinesToDisplay(int maxLines, Options options) {
|
||||
if (maxLines < 1) {
|
||||
maxLines = 1;
|
||||
options.setInt(MAX_DISPLAY_LINES_MSG, maxLines);
|
||||
options.setInt(MAX_DISPLAY_LINES_KEY, maxLines);
|
||||
}
|
||||
maxDisplayLines = maxLines;
|
||||
}
|
||||
@ -279,57 +257,44 @@ public class EolCommentFieldFactory extends FieldFactory {
|
||||
}
|
||||
}
|
||||
|
||||
DisplayableEol displayableEol =
|
||||
new DisplayableEol(cu, alwaysShowRepeatable, alwaysShowRefRepeatables,
|
||||
alwaysShowAutomatic, codeUnitFormatOptions.followReferencedPointers(),
|
||||
maxDisplayLines, useAbbreviatedAutomatic, showAutomaticFunctions);
|
||||
|
||||
List<FieldElement> elementList = new ArrayList<>();
|
||||
EolComments comments =
|
||||
new EolComments(cu, codeUnitFormatOptions.followReferencedPointers(),
|
||||
maxDisplayLines, extraCommentsOption);
|
||||
|
||||
// This Code Unit's End of Line Comment
|
||||
AttributedString myEolPrefixString = new AttributedString(SEMICOLON_PREFIX,
|
||||
CommentColors.EOL, getMetrics(style), false, null);
|
||||
String[] eolComments = displayableEol.getEOLComments();
|
||||
List<FieldElement> eolFieldElements = convertToFieldElements(program, eolComments,
|
||||
myEolPrefixString, showSemicolon, isWordWrap, getNextRow(elementList));
|
||||
elementList.addAll(eolFieldElements);
|
||||
List<FieldElement> elementList = new ArrayList<>();
|
||||
AttributedString prefix = createPrefix(style);
|
||||
List<String> eols = comments.getEOLComments();
|
||||
List<FieldElement> eolElements = convertToFieldElements(program, eols, prefix, 0);
|
||||
elementList.addAll(eolElements);
|
||||
|
||||
// This Code Unit's Repeatable Comment
|
||||
if (alwaysShowRepeatable || elementList.isEmpty()) {
|
||||
AttributedString myRepeatablePrefixString = new AttributedString(SEMICOLON_PREFIX,
|
||||
CommentColors.REPEATABLE, getMetrics(repeatableCommentStyle), false, null);
|
||||
String[] repeatableComments = displayableEol.getRepeatableComments();
|
||||
List<FieldElement> repeatableFieldElements =
|
||||
convertToFieldElements(program, repeatableComments, myRepeatablePrefixString,
|
||||
showSemicolon, isWordWrap, getNextRow(elementList));
|
||||
elementList.addAll(repeatableFieldElements);
|
||||
if (comments.isShowingRepeatables()) {
|
||||
prefix = createPrefix(repeatableCommentStyle);
|
||||
int row = getNextRow(elementList);
|
||||
List<String> repeatables = comments.getRepeatableComments();
|
||||
List<FieldElement> elements = convertToFieldElements(program, repeatables, prefix, row);
|
||||
elementList.addAll(elements);
|
||||
}
|
||||
|
||||
// Referenced Repeatable Comments
|
||||
if (alwaysShowRefRepeatables || elementList.isEmpty()) {
|
||||
AttributedString refRepeatPrefixString = new AttributedString(SEMICOLON_PREFIX,
|
||||
CommentColors.REF_REPEATABLE, getMetrics(refRepeatableCommentStyle), false, null);
|
||||
int refRepeatCount = displayableEol.getReferencedRepeatableCommentsCount();
|
||||
for (int subTypeIndex = 0; subTypeIndex < refRepeatCount; subTypeIndex++) {
|
||||
RefRepeatComment refRepeatComment =
|
||||
displayableEol.getReferencedRepeatableComments(subTypeIndex);
|
||||
String[] refRepeatComments = refRepeatComment.getCommentLines();
|
||||
List<FieldElement> refRepeatFieldElements = convertToRefFieldElements(
|
||||
refRepeatComments, program, refRepeatPrefixString, showSemicolon, isWordWrap,
|
||||
prependRefAddress, refRepeatComment.getAddress(), getNextRow(elementList));
|
||||
elementList.addAll(refRepeatFieldElements);
|
||||
if (comments.isShowingRefRepeatables()) {
|
||||
prefix = createPrefix(refRepeatableCommentStyle);
|
||||
List<RefRepeatComment> refRepeatables =
|
||||
comments.getReferencedRepeatableComments();
|
||||
for (RefRepeatComment comment : refRepeatables) {
|
||||
int row = getNextRow(elementList);
|
||||
String[] lines = comment.getCommentLines();
|
||||
List<FieldElement> elements = convertToRefFieldElements(
|
||||
lines, program, prefix, comment.getAddress(), row);
|
||||
elementList.addAll(elements);
|
||||
}
|
||||
}
|
||||
|
||||
// Automatic Comment
|
||||
if (alwaysShowAutomatic || elementList.isEmpty()) {
|
||||
AttributedString autoCommentPrefixString = new AttributedString(SEMICOLON_PREFIX,
|
||||
CommentColors.AUTO, getMetrics(automaticCommentStyle), false, null);
|
||||
String[] autoComment = displayableEol.getAutomaticComment();
|
||||
List<FieldElement> autoCommentFieldElements =
|
||||
convertToFieldElements(program, autoComment, autoCommentPrefixString, showSemicolon,
|
||||
isWordWrap, getNextRow(elementList));
|
||||
elementList.addAll(autoCommentFieldElements);
|
||||
if (comments.isShowingAutoComments()) {
|
||||
prefix = createPrefix(automaticCommentStyle);
|
||||
int row = getNextRow(elementList);
|
||||
List<String> autos = comments.getAutomaticComment();
|
||||
List<FieldElement> elements = convertToFieldElements(program, autos, prefix, row);
|
||||
elementList.addAll(elements);
|
||||
}
|
||||
|
||||
FieldElement[] fieldElements = elementList.toArray(new FieldElement[elementList.size()]);
|
||||
@ -340,6 +305,27 @@ public class EolCommentFieldFactory extends FieldFactory {
|
||||
maxDisplayLines, hlProvider);
|
||||
}
|
||||
|
||||
private AttributedString createPrefix(int commentStyle) {
|
||||
if (commentStyle == style) {
|
||||
return new AttributedString(SEMICOLON_PREFIX, CommentColors.EOL, getMetrics(style),
|
||||
false,
|
||||
null);
|
||||
}
|
||||
if (commentStyle == repeatableCommentStyle) {
|
||||
return new AttributedString(SEMICOLON_PREFIX,
|
||||
CommentColors.REPEATABLE, getMetrics(repeatableCommentStyle), false, null);
|
||||
}
|
||||
if (commentStyle == refRepeatableCommentStyle) {
|
||||
return new AttributedString(SEMICOLON_PREFIX,
|
||||
CommentColors.REF_REPEATABLE, getMetrics(refRepeatableCommentStyle), false, null);
|
||||
}
|
||||
if (commentStyle == automaticCommentStyle) {
|
||||
return new AttributedString(SEMICOLON_PREFIX,
|
||||
CommentColors.AUTO, getMetrics(automaticCommentStyle), false, null);
|
||||
}
|
||||
throw new AssertException("Unexected comment style: " + commentStyle);
|
||||
}
|
||||
|
||||
private int getNextRow(List<FieldElement> elementList) {
|
||||
int elementIndex = elementList.size() - 1;
|
||||
if (elementIndex >= 0) {
|
||||
@ -352,26 +338,25 @@ public class EolCommentFieldFactory extends FieldFactory {
|
||||
return 0;
|
||||
}
|
||||
|
||||
private List<FieldElement> convertToFieldElements(Program program, String[] comments,
|
||||
AttributedString currentPrefixString, boolean showPrefix, boolean wordWrap,
|
||||
int nextRow) {
|
||||
private List<FieldElement> convertToFieldElements(Program program, List<String> comments,
|
||||
AttributedString currentPrefixString, int nextRow) {
|
||||
|
||||
List<FieldElement> fieldElements = new ArrayList<>();
|
||||
if (comments.length == 0) {
|
||||
if (comments.isEmpty()) {
|
||||
return fieldElements;
|
||||
}
|
||||
for (int rowIndex = 0; rowIndex < comments.length; rowIndex++) {
|
||||
for (int rowIndex = 0; rowIndex < comments.size(); rowIndex++) {
|
||||
int encodedRow = nextRow + rowIndex;
|
||||
fieldElements.add(CommentUtils.parseTextForAnnotations(comments[rowIndex], program,
|
||||
fieldElements.add(CommentUtils.parseTextForAnnotations(comments.get(rowIndex), program,
|
||||
currentPrefixString, encodedRow));
|
||||
}
|
||||
|
||||
if (wordWrap) {
|
||||
int lineWidth = showPrefix ? width - currentPrefixString.getStringWidth() : width;
|
||||
if (isWordWrap) {
|
||||
int lineWidth = showSemicolon ? width - currentPrefixString.getStringWidth() : width;
|
||||
fieldElements = FieldUtils.wrap(fieldElements, lineWidth);
|
||||
}
|
||||
|
||||
if (showPrefix) {
|
||||
if (showSemicolon) {
|
||||
for (int i = 0; i < fieldElements.size(); i++) {
|
||||
RowColLocation startRowCol =
|
||||
fieldElements.get(i).getDataLocationForCharacterIndex(0);
|
||||
@ -387,8 +372,7 @@ public class EolCommentFieldFactory extends FieldFactory {
|
||||
}
|
||||
|
||||
private List<FieldElement> convertToRefFieldElements(String[] comments, Program program,
|
||||
AttributedString currentPrefixString, boolean showPrefix, boolean wordWrap,
|
||||
boolean showRefAddress, Address refAddress, int nextRow) {
|
||||
AttributedString currentPrefixString, Address refAddress, int nextRow) {
|
||||
|
||||
int numCommentLines = comments.length;
|
||||
List<FieldElement> fieldElements = new ArrayList<>();
|
||||
@ -400,7 +384,7 @@ public class EolCommentFieldFactory extends FieldFactory {
|
||||
fieldElements.add(CommentUtils.parseTextForAnnotations(comments[rowIndex], program,
|
||||
currentPrefixString, encodedRow));
|
||||
}
|
||||
if (showRefAddress) {
|
||||
if (prependRefAddress) {
|
||||
FieldElement commentElement = fieldElements.get(0);
|
||||
// Address
|
||||
String refAddrComment = "{@address " + refAddress.toString() + "}";
|
||||
@ -418,12 +402,12 @@ public class EolCommentFieldFactory extends FieldFactory {
|
||||
new FieldElement[] { addressElement, spacerElement, commentElement }));
|
||||
}
|
||||
|
||||
if (wordWrap) {
|
||||
int lineWidth = showPrefix ? width - currentPrefixString.getStringWidth() : width;
|
||||
if (isWordWrap) {
|
||||
int lineWidth = showSemicolon ? width - currentPrefixString.getStringWidth() : width;
|
||||
fieldElements = FieldUtils.wrap(fieldElements, lineWidth);
|
||||
}
|
||||
|
||||
if (showPrefix) {
|
||||
if (showSemicolon) {
|
||||
for (int i = 0; i < fieldElements.size(); i++) {
|
||||
RowColLocation startRowCol =
|
||||
fieldElements.get(i).getDataLocationForCharacterIndex(0);
|
||||
@ -451,10 +435,9 @@ public class EolCommentFieldFactory extends FieldFactory {
|
||||
return null;
|
||||
}
|
||||
CodeUnit cu = (CodeUnit) obj;
|
||||
DisplayableEol displayableEol =
|
||||
new DisplayableEol(cu, alwaysShowRepeatable, alwaysShowRefRepeatables,
|
||||
alwaysShowAutomatic, codeUnitFormatOptions.followReferencedPointers(),
|
||||
maxDisplayLines, useAbbreviatedAutomatic, showAutomaticFunctions);
|
||||
EolComments displayableEol =
|
||||
new EolComments(cu, codeUnitFormatOptions.followReferencedPointers(),
|
||||
maxDisplayLines, extraCommentsOption);
|
||||
|
||||
// Hold position in connected tool if navigating within semicolon.
|
||||
int numLeadColumns = 0;
|
||||
@ -489,10 +472,9 @@ public class EolCommentFieldFactory extends FieldFactory {
|
||||
return null;
|
||||
}
|
||||
|
||||
DisplayableEol displayableEol =
|
||||
new DisplayableEol((CodeUnit) obj, alwaysShowRepeatable, alwaysShowRefRepeatables,
|
||||
alwaysShowAutomatic, codeUnitFormatOptions.followReferencedPointers(),
|
||||
maxDisplayLines, useAbbreviatedAutomatic, showAutomaticFunctions);
|
||||
EolComments displayableEol =
|
||||
new EolComments((CodeUnit) obj, codeUnitFormatOptions.followReferencedPointers(),
|
||||
maxDisplayLines, extraCommentsOption);
|
||||
|
||||
ListingTextField btf = (ListingTextField) bf;
|
||||
RowColLocation eolRowCol = displayableEol.getRowCol((CommentFieldLocation) loc);
|
||||
|
@ -0,0 +1,22 @@
|
||||
/* ###
|
||||
* 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;
|
||||
|
||||
public enum EolEnablement {
|
||||
ALWAYS,
|
||||
DEFAULT,
|
||||
NEVER;
|
||||
}
|
@ -0,0 +1,175 @@
|
||||
/* ###
|
||||
* 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 ghidra.framework.options.CustomOption;
|
||||
import ghidra.framework.options.GProperties;
|
||||
|
||||
/**
|
||||
* An option class that is used by the {@link EolExtraCommentsPropertyEditor} to load and save
|
||||
* option settings.
|
||||
*/
|
||||
public class EolExtraCommentsOption implements CustomOption {
|
||||
|
||||
private static final String BASE_KEY = "extraComment";
|
||||
private static final String KEY_REPETABLE = BASE_KEY + "Repeatable";
|
||||
private static final String KEY_REF_REPETABLE = BASE_KEY + "RefRepeatable";
|
||||
private static final String KEY_AUTO_DATA = BASE_KEY + "AutoData";
|
||||
private static final String KEY_AUTO_FUNCTION = BASE_KEY + "AutoFunction";
|
||||
private static final String KEY_USE_ABBRAVIATED = BASE_KEY + "UseAbbreviated";
|
||||
|
||||
private EolEnablement repeatable = EolEnablement.DEFAULT;
|
||||
private EolEnablement refRepeatable = EolEnablement.DEFAULT;
|
||||
private EolEnablement autoFunction = EolEnablement.DEFAULT;
|
||||
private EolEnablement autoData = EolEnablement.DEFAULT;
|
||||
|
||||
private boolean useAbbreviatedComments = true;
|
||||
|
||||
public EolExtraCommentsOption() {
|
||||
// required for persistence
|
||||
}
|
||||
|
||||
public EolEnablement getRepeatable() {
|
||||
return repeatable;
|
||||
}
|
||||
|
||||
public void setRepeatable(EolEnablement priority) {
|
||||
repeatable = priority;
|
||||
}
|
||||
|
||||
public EolEnablement getRefRepeatable() {
|
||||
return refRepeatable;
|
||||
}
|
||||
|
||||
public void setRefRepeatable(EolEnablement priority) {
|
||||
refRepeatable = priority;
|
||||
}
|
||||
|
||||
public EolEnablement getAutoData() {
|
||||
return autoData;
|
||||
}
|
||||
|
||||
public void setAutoData(EolEnablement priority) {
|
||||
autoData = priority;
|
||||
}
|
||||
|
||||
public EolEnablement getAutoFunction() {
|
||||
return autoFunction;
|
||||
}
|
||||
|
||||
public void setAutoFunction(EolEnablement priority) {
|
||||
autoFunction = priority;
|
||||
}
|
||||
|
||||
public boolean useAbbreviatedComments() {
|
||||
return useAbbreviatedComments;
|
||||
}
|
||||
|
||||
public void setUseAbbreviatedComments(boolean b) {
|
||||
useAbbreviatedComments = b;
|
||||
}
|
||||
|
||||
public boolean alwaysShowAutoComments() {
|
||||
return autoData == EolEnablement.ALWAYS || autoFunction == EolEnablement.ALWAYS;
|
||||
}
|
||||
|
||||
public boolean isShowingRefRepeatables(boolean hasOtherComments) {
|
||||
return isShowing(refRepeatable, hasOtherComments);
|
||||
}
|
||||
|
||||
public boolean isShowingRepeatables(boolean hasOtherComments) {
|
||||
return isShowing(repeatable, hasOtherComments);
|
||||
}
|
||||
|
||||
public boolean isShowingAutoComments(boolean hasOtherComments) {
|
||||
|
||||
if (alwaysShowAutoComments()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isShowing(autoData, hasOtherComments)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return isShowing(autoFunction, hasOtherComments);
|
||||
}
|
||||
|
||||
private boolean isShowing(EolEnablement enablement, boolean hasExistingComments) {
|
||||
return enablement == EolEnablement.ALWAYS ||
|
||||
(enablement == EolEnablement.DEFAULT && !hasExistingComments);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readState(GProperties properties) {
|
||||
repeatable = properties.getEnum(KEY_REPETABLE, EolEnablement.DEFAULT);
|
||||
refRepeatable = properties.getEnum(KEY_REF_REPETABLE, EolEnablement.DEFAULT);
|
||||
autoData = properties.getEnum(KEY_AUTO_DATA, EolEnablement.DEFAULT);
|
||||
autoFunction = properties.getEnum(KEY_AUTO_FUNCTION, EolEnablement.DEFAULT);
|
||||
useAbbreviatedComments = properties.getBoolean(KEY_USE_ABBRAVIATED, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeState(GProperties properties) {
|
||||
properties.putEnum(KEY_REPETABLE, repeatable);
|
||||
properties.putEnum(KEY_REF_REPETABLE, refRepeatable);
|
||||
properties.putEnum(KEY_AUTO_DATA, autoData);
|
||||
properties.putEnum(KEY_AUTO_FUNCTION, autoFunction);
|
||||
properties.putBoolean(KEY_USE_ABBRAVIATED, useAbbreviatedComments);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((autoData == null) ? 0 : autoData.hashCode());
|
||||
result = prime * result + ((autoFunction == null) ? 0 : autoFunction.hashCode());
|
||||
result = prime * result + ((refRepeatable == null) ? 0 : refRepeatable.hashCode());
|
||||
result = prime * result + ((repeatable == null) ? 0 : repeatable.hashCode());
|
||||
result = prime * result + (useAbbreviatedComments ? 1231 : 1237);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
EolExtraCommentsOption other = (EolExtraCommentsOption) obj;
|
||||
if (autoData != other.autoData) {
|
||||
return false;
|
||||
}
|
||||
if (autoFunction != other.autoFunction) {
|
||||
return false;
|
||||
}
|
||||
if (refRepeatable != other.refRepeatable) {
|
||||
return false;
|
||||
}
|
||||
if (repeatable != other.repeatable) {
|
||||
return false;
|
||||
}
|
||||
if (useAbbreviatedComments != other.useAbbreviatedComments) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,222 @@
|
||||
/* ###
|
||||
* 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.Component;
|
||||
import java.awt.event.ItemListener;
|
||||
import java.beans.PropertyEditorSupport;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.TitledBorder;
|
||||
|
||||
import docking.widgets.checkbox.GCheckBox;
|
||||
import docking.widgets.combobox.GComboBox;
|
||||
import ghidra.framework.options.CustomOptionsEditor;
|
||||
import ghidra.util.layout.PairLayout;
|
||||
|
||||
public class EolExtraCommentsPropertyEditor extends PropertyEditorSupport
|
||||
implements CustomOptionsEditor {
|
||||
|
||||
private static final String REPEATABLE_LABEL = "Repeatable Comment";
|
||||
private static final String REF_REPEATABLE_LABEL = "Referenced Repeatable Comments";
|
||||
private static final String AUTO_DATA_LABEL = "Auto Data Comment";
|
||||
private static final String AUTO_FUNCTION_LABEL = "Auto Function Comment";
|
||||
private static final String ABBREVIATED_LABEL = "Use Abbreviated Comments";
|
||||
|
||||
private static final String REPEATABLE_TOOLTIP =
|
||||
"<HTML>For repeatable comments:" +
|
||||
"<UL>" +
|
||||
" <LI>ALWAYS - show even if an EOL comment exists</LI>" +
|
||||
" <LI>DEFAULT - show only when no EOL comment exists</LI>" +
|
||||
" <LI>NEVER - do not show</LI>" +
|
||||
"</UL>";
|
||||
|
||||
private static final String REF_REPEATABLE_TOOLTIP =
|
||||
"<HTML>For referenced repeatable comments:" +
|
||||
"<UL>" +
|
||||
" <LI>ALWAYS - show even if a higher priority comment exists</LI>" +
|
||||
" <LI>DEFAULT - show only when no higher priority comment exists</LI>" +
|
||||
" <LI>NEVER - do not show</LI>" +
|
||||
"</UL>";
|
||||
|
||||
private static final String AUTO_TOOLTIP =
|
||||
"<HTML>For automatic comments:" +
|
||||
"<UL>" +
|
||||
" <LI>ALWAYS - show even if a higher priority comment exists</LI>" +
|
||||
" <LI>DEFAULT - show only when no higher priority comment exists</LI>" +
|
||||
" <LI>NEVER - do not show</LI>" +
|
||||
"</UL>";
|
||||
|
||||
private static final String ABBREVIATED_TOOLTIP =
|
||||
"When showing automatic comments, show the smallest amount of information possible";
|
||||
|
||||
private static final String[] NAMES =
|
||||
{ REPEATABLE_LABEL, REF_REPEATABLE_LABEL, AUTO_DATA_LABEL, AUTO_FUNCTION_LABEL,
|
||||
ABBREVIATED_LABEL };
|
||||
|
||||
private static final String[] DESCRIPTIONS = {
|
||||
REPEATABLE_TOOLTIP, REF_REPEATABLE_TOOLTIP, AUTO_TOOLTIP, AUTO_TOOLTIP,
|
||||
ABBREVIATED_TOOLTIP
|
||||
};
|
||||
|
||||
private Component editorComponent;
|
||||
|
||||
private GComboBox<EolEnablement> repeatableCombo;
|
||||
private GComboBox<EolEnablement> refRepeatableCombo;
|
||||
private GComboBox<EolEnablement> autoDataCombo;
|
||||
private GComboBox<EolEnablement> autoFunctionCombo;
|
||||
private JCheckBox abbreviatedCheckbox;
|
||||
|
||||
private EolExtraCommentsOption commentsOption;
|
||||
|
||||
public EolExtraCommentsPropertyEditor() {
|
||||
editorComponent = buildEditor();
|
||||
}
|
||||
|
||||
private Component buildEditor() {
|
||||
|
||||
// values picked through trial-and-error
|
||||
int vgap = 3;
|
||||
int hgap = 5;
|
||||
int minRightSize = 150; // big enough to match other items in the external options panel
|
||||
JPanel panel = new JPanel(new PairLayout(vgap, hgap, minRightSize));
|
||||
|
||||
JLabel label = new JLabel(REPEATABLE_LABEL);
|
||||
label.setToolTipText(REPEATABLE_TOOLTIP);
|
||||
repeatableCombo = new GComboBox<>(EolEnablement.values());
|
||||
repeatableCombo.setSelectedItem(EolEnablement.DEFAULT);
|
||||
repeatableCombo.addItemListener(e -> firePropertyChange());
|
||||
|
||||
panel.add(label);
|
||||
panel.add(repeatableCombo);
|
||||
|
||||
label = new JLabel(REF_REPEATABLE_LABEL);
|
||||
label.setToolTipText(REF_REPEATABLE_TOOLTIP);
|
||||
refRepeatableCombo = new GComboBox<>(EolEnablement.values());
|
||||
refRepeatableCombo.setSelectedItem(EolEnablement.DEFAULT);
|
||||
refRepeatableCombo.addItemListener(e -> firePropertyChange());
|
||||
|
||||
panel.add(label);
|
||||
panel.add(refRepeatableCombo);
|
||||
|
||||
label = new JLabel(AUTO_DATA_LABEL);
|
||||
label.setToolTipText(AUTO_TOOLTIP);
|
||||
autoDataCombo = new GComboBox<>(EolEnablement.values());
|
||||
autoDataCombo.setSelectedItem(EolEnablement.DEFAULT);
|
||||
autoDataCombo.addItemListener(e -> firePropertyChange());
|
||||
|
||||
panel.add(label);
|
||||
panel.add(autoDataCombo);
|
||||
|
||||
label = new JLabel(AUTO_FUNCTION_LABEL);
|
||||
label.setToolTipText(AUTO_TOOLTIP);
|
||||
autoFunctionCombo = new GComboBox<>(EolEnablement.values());
|
||||
autoFunctionCombo.setSelectedItem(EolEnablement.DEFAULT);
|
||||
autoFunctionCombo.addItemListener(e -> firePropertyChange());
|
||||
|
||||
panel.add(label);
|
||||
panel.add(autoFunctionCombo);
|
||||
|
||||
abbreviatedCheckbox = new GCheckBox(ABBREVIATED_LABEL);
|
||||
abbreviatedCheckbox.setSelected(true);
|
||||
abbreviatedCheckbox.setToolTipText(ABBREVIATED_TOOLTIP);
|
||||
|
||||
ItemListener listener = e -> firePropertyChange();
|
||||
repeatableCombo.addItemListener(listener);
|
||||
refRepeatableCombo.addItemListener(listener);
|
||||
autoDataCombo.addItemListener(listener);
|
||||
autoFunctionCombo.addItemListener(listener);
|
||||
abbreviatedCheckbox.addItemListener(listener);
|
||||
|
||||
panel.setBorder(BorderFactory.createCompoundBorder(
|
||||
new TitledBorder("Additional Comment Types"),
|
||||
BorderFactory.createEmptyBorder(10, 10, 10, 10)));
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
private Object cloneOptionValues() {
|
||||
EolExtraCommentsOption newOption = new EolExtraCommentsOption();
|
||||
newOption.setRepeatable((EolEnablement) repeatableCombo.getSelectedItem());
|
||||
newOption.setRefRepeatable((EolEnablement) refRepeatableCombo.getSelectedItem());
|
||||
newOption.setAutoData((EolEnablement) autoDataCombo.getSelectedItem());
|
||||
newOption.setAutoFunction((EolEnablement) autoFunctionCombo.getSelectedItem());
|
||||
return newOption;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getOptionNames() {
|
||||
return NAMES;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getOptionDescriptions() {
|
||||
return DESCRIPTIONS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue() {
|
||||
return cloneOptionValues();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getCustomEditor() {
|
||||
return editorComponent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsCustomEditor() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(Object value) {
|
||||
if (!(value instanceof EolExtraCommentsOption)) {
|
||||
return;
|
||||
}
|
||||
|
||||
commentsOption = (EolExtraCommentsOption) value;
|
||||
setLocalValues(commentsOption);
|
||||
firePropertyChange();
|
||||
}
|
||||
|
||||
private void setLocalValues(EolExtraCommentsOption sourceOption) {
|
||||
|
||||
EolEnablement currentPriority = (EolEnablement) repeatableCombo.getSelectedItem();
|
||||
EolEnablement newPriority = sourceOption.getRepeatable();
|
||||
if (currentPriority != newPriority) {
|
||||
repeatableCombo.setSelectedItem(newPriority);
|
||||
}
|
||||
|
||||
currentPriority = (EolEnablement) refRepeatableCombo.getSelectedItem();
|
||||
newPriority = sourceOption.getRefRepeatable();
|
||||
if (currentPriority != newPriority) {
|
||||
refRepeatableCombo.setSelectedItem(newPriority);
|
||||
}
|
||||
|
||||
currentPriority = (EolEnablement) autoDataCombo.getSelectedItem();
|
||||
newPriority = sourceOption.getAutoData();
|
||||
if (currentPriority != newPriority) {
|
||||
autoDataCombo.setSelectedItem(newPriority);
|
||||
}
|
||||
|
||||
currentPriority = (EolEnablement) autoFunctionCombo.getSelectedItem();
|
||||
newPriority = sourceOption.getAutoFunction();
|
||||
if (currentPriority != newPriority) {
|
||||
autoFunctionCombo.setSelectedItem(newPriority);
|
||||
}
|
||||
}
|
||||
}
|
@ -167,15 +167,14 @@ public class XRefFieldFactory extends FieldFactory {
|
||||
private void setupNamespaceOptions(Options fieldOptions) {
|
||||
// we need to install a custom editor that allows us to edit a group of related options
|
||||
fieldOptions.registerOption(NAMESPACE_OPTIONS_KEY, OptionType.CUSTOM_TYPE,
|
||||
new NamespaceWrappedOption(), null, "Adjusts the XREFs Field namespace display",
|
||||
namespaceOptionsEditor);
|
||||
new NamespaceWrappedOption(), new HelpLocation("CodeBrowserPlugin", "XREFs_Field"),
|
||||
"Adjusts the XREFs Field namespace display", namespaceOptionsEditor);
|
||||
CustomOption customOption = fieldOptions.getCustomOption(NAMESPACE_OPTIONS_KEY, null);
|
||||
fieldOptions.getOptions(NAMESPACE_OPTIONS_KEY)
|
||||
.setOptionsHelpLocation(new HelpLocation("CodeBrowserPlugin", "XREFs_Field"));
|
||||
|
||||
if (!(customOption instanceof NamespaceWrappedOption)) {
|
||||
throw new AssertException("Someone set an option for " + NAMESPACE_OPTIONS_KEY +
|
||||
" that is not the expected " +
|
||||
"ghidra.app.util.viewer.field.NamespaceWrappedOption type.");
|
||||
NamespaceWrappedOption.class.getName() + " type.");
|
||||
}
|
||||
|
||||
NamespaceWrappedOption namespaceOption = (NamespaceWrappedOption) customOption;
|
||||
|
@ -578,29 +578,16 @@ public class CodeBrowserOptionsTest extends AbstractGhidraHeadedIntegrationTest
|
||||
@Test
|
||||
public void testEOLCommentsOptions() throws Exception {
|
||||
|
||||
final int SHOW_AUTO = 0;
|
||||
final int SHOW_REF_REPEAT = 1;
|
||||
final int SHOW_REPEATABLE = 2;
|
||||
final int WORD_WRAP = 3;
|
||||
final int MAX_LINES = 4;
|
||||
final int SHOW_REF_ADDR = 5;
|
||||
//final int SHOW_FUNCTION_AUTO = 6;
|
||||
final int SHOW_SEMICOLON = 7;
|
||||
// for readability
|
||||
String EXTRA_COMMENTS = EolCommentFieldFactory.EXTRA_COMMENT_KEY;
|
||||
String WORD_WRAP = EolCommentFieldFactory.ENABLE_WORD_WRAP_KEY;
|
||||
String MAX_LINES = EolCommentFieldFactory.MAX_DISPLAY_LINES_KEY;
|
||||
String SHOW_REF_ADDR = EolCommentFieldFactory.ENABLE_PREPEND_REF_ADDRESS_KEY;
|
||||
String SHOW_SEMICOLON = EolCommentFieldFactory.ENABLE_SHOW_SEMICOLON_KEY;
|
||||
|
||||
showTool(tool);
|
||||
loadProgram();
|
||||
Options options = tool.getOptions(GhidraOptions.CATEGORY_BROWSER_FIELDS);
|
||||
List<String> names = getOptionNames(options, "EOL Comments Field");
|
||||
assertEquals(9, names.size());
|
||||
assertEquals(EolCommentFieldFactory.ENABLE_ALWAYS_SHOW_AUTOMATIC_MSG, names.get(0));
|
||||
assertEquals(EolCommentFieldFactory.ENABLE_ALWAYS_SHOW_REF_REPEATABLE_MSG, names.get(1));
|
||||
assertEquals(EolCommentFieldFactory.ENABLE_ALWAYS_SHOW_REPEATABLE_MSG, names.get(2));
|
||||
assertEquals(EolCommentFieldFactory.ENABLE_WORD_WRAP_MSG, names.get(3));
|
||||
assertEquals(EolCommentFieldFactory.MAX_DISPLAY_LINES_MSG, names.get(4));
|
||||
assertEquals(EolCommentFieldFactory.ENABLE_PREPEND_REF_ADDRESS_MSG, names.get(5));
|
||||
assertEquals(EolCommentFieldFactory.SHOW_FUNCTION_AUTOMITIC_COMMENT_MSG, names.get(6));
|
||||
assertEquals(EolCommentFieldFactory.ENABLE_SHOW_SEMICOLON_MSG, names.get(7));
|
||||
assertEquals(EolCommentFieldFactory.USE_ABBREVIATED_AUTOMITIC_COMMENT_MSG, names.get(8));
|
||||
|
||||
Address callAddress = addr("0x1003fcc");
|
||||
Address callRefAddress = addr("0x1006642");
|
||||
Address otherRefAddress = addr("0x1003fa1");
|
||||
@ -637,66 +624,77 @@ public class CodeBrowserOptionsTest extends AbstractGhidraHeadedIntegrationTest
|
||||
CodeUnit.REPEATABLE_COMMENT, "Mem ref line1.\n" + "");
|
||||
tool.execute(commentRefCmd, program);
|
||||
|
||||
options.setBoolean(names.get(SHOW_AUTO), false);
|
||||
options.setBoolean(names.get(SHOW_REF_REPEAT), false);
|
||||
options.setBoolean(names.get(SHOW_REPEATABLE), false);
|
||||
options.setBoolean(names.get(WORD_WRAP), false);
|
||||
options.setInt(names.get(MAX_LINES), 20);
|
||||
options.setBoolean(names.get(SHOW_REF_ADDR), false);
|
||||
options.setBoolean(names.get(SHOW_SEMICOLON), false);
|
||||
// these values are all DEFAULT, by default; set them in case that changes in the future
|
||||
EolExtraCommentsOption extraCommentsOption = new EolExtraCommentsOption();
|
||||
extraCommentsOption.setRepeatable(EolEnablement.DEFAULT);
|
||||
extraCommentsOption.setRefRepeatable(EolEnablement.DEFAULT);
|
||||
extraCommentsOption.setAutoData(EolEnablement.DEFAULT);
|
||||
extraCommentsOption.setAutoFunction(EolEnablement.DEFAULT);
|
||||
|
||||
options.setCustomOption(EXTRA_COMMENTS, extraCommentsOption);
|
||||
options.setBoolean(WORD_WRAP, false);
|
||||
options.setInt(MAX_LINES, 20);
|
||||
options.setBoolean(SHOW_REF_ADDR, false);
|
||||
options.setBoolean(SHOW_SEMICOLON, false);
|
||||
|
||||
cb.updateNow();
|
||||
cb.goToField(callAddress, "EOL Comment", 0, 0);
|
||||
ListingTextField btf = (ListingTextField) cb.getCurrentField();
|
||||
assertEquals(9, getNumberOfLines(btf));
|
||||
|
||||
options.setBoolean(names.get(WORD_WRAP), true);
|
||||
options.setBoolean(WORD_WRAP, true);
|
||||
cb.updateNow();
|
||||
btf = (ListingTextField) cb.getCurrentField();
|
||||
assertEquals(18, getNumberOfLines(btf));
|
||||
|
||||
options.setBoolean(names.get(SHOW_SEMICOLON), true);
|
||||
options.setBoolean(SHOW_SEMICOLON, true);
|
||||
cb.updateNow();
|
||||
btf = (ListingTextField) cb.getCurrentField();
|
||||
assertEquals(20, getNumberOfLines(btf));
|
||||
assertEquals("; ", btf.getFieldElement(1, 0).getText());
|
||||
|
||||
options.setBoolean(names.get(SHOW_REPEATABLE), true);
|
||||
extraCommentsOption.setRepeatable(EolEnablement.ALWAYS);
|
||||
options.setCustomOption(EXTRA_COMMENTS, extraCommentsOption);
|
||||
cb.updateNow();
|
||||
btf = (ListingTextField) cb.getCurrentField();
|
||||
assertEquals(20, getNumberOfLines(btf));
|
||||
assertEquals("; ", btf.getFieldElement(1, 0).getText());
|
||||
|
||||
options.setBoolean(names.get(SHOW_AUTO), true);
|
||||
extraCommentsOption.setAutoData(EolEnablement.ALWAYS);
|
||||
extraCommentsOption.setAutoFunction(EolEnablement.ALWAYS);
|
||||
options.setCustomOption(EXTRA_COMMENTS, extraCommentsOption);
|
||||
cb.updateNow();
|
||||
btf = (ListingTextField) cb.getCurrentField();
|
||||
assertEquals(20, getNumberOfLines(btf));
|
||||
assertEquals("; ", btf.getFieldElement(1, 0).getText());
|
||||
|
||||
options.setBoolean(names.get(SHOW_REF_REPEAT), true);
|
||||
extraCommentsOption.setRefRepeatable(EolEnablement.ALWAYS);
|
||||
options.setCustomOption(EXTRA_COMMENTS, extraCommentsOption);
|
||||
cb.updateNow();
|
||||
btf = (ListingTextField) cb.getCurrentField();
|
||||
assertEquals(20, getNumberOfLines(btf));
|
||||
assertEquals("; ", btf.getFieldElement(1, 0).getText());
|
||||
|
||||
options.setBoolean(names.get(SHOW_REF_ADDR), true);
|
||||
options.setBoolean(EolCommentFieldFactory.ENABLE_PREPEND_REF_ADDRESS_KEY, true);
|
||||
cb.updateNow();
|
||||
btf = (ListingTextField) cb.getCurrentField();
|
||||
assertEquals(20, getNumberOfLines(btf));
|
||||
assertEquals("; ", btf.getFieldElement(1, 0).getText());
|
||||
|
||||
options.setBoolean(names.get(SHOW_REPEATABLE), false);
|
||||
extraCommentsOption.setRepeatable(EolEnablement.DEFAULT);
|
||||
options.setCustomOption(EXTRA_COMMENTS, extraCommentsOption);
|
||||
cb.updateNow();
|
||||
btf = (ListingTextField) cb.getCurrentField();
|
||||
assertEquals(20, getNumberOfLines(btf));
|
||||
assertEquals("; ", btf.getFieldElement(1, 0).getText());
|
||||
|
||||
options.setBoolean(names.get(WORD_WRAP), false);
|
||||
options.setBoolean(WORD_WRAP, false);
|
||||
cb.updateNow();
|
||||
btf = (ListingTextField) cb.getCurrentField();
|
||||
assertEquals(12, getNumberOfLines(btf));
|
||||
assertTrue("; ".equals(btf.getFieldElement(5, 0).getText()));
|
||||
|
||||
options.setBoolean(names.get(SHOW_SEMICOLON), false);
|
||||
options.setBoolean(SHOW_SEMICOLON, false);
|
||||
cb.updateNow();
|
||||
btf = (ListingTextField) cb.getCurrentField();
|
||||
assertEquals(12, getNumberOfLines(btf));
|
||||
@ -704,7 +702,7 @@ public class CodeBrowserOptionsTest extends AbstractGhidraHeadedIntegrationTest
|
||||
assertEquals("01003fa1", btf.getFieldElement(11, 4).getText());
|
||||
assertEquals("Mem ref line1.", btf.getFieldElement(11, 11).getText());
|
||||
|
||||
options.setBoolean(names.get(SHOW_REF_ADDR), false);
|
||||
options.setBoolean(SHOW_REF_ADDR, false);
|
||||
cb.updateNow();
|
||||
btf = (ListingTextField) cb.getCurrentField();
|
||||
assertEquals(11, getNumberOfLines(btf));
|
||||
|
@ -247,8 +247,8 @@ public class CommentsPluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
setFieldWidth(browser, EolCommentFieldFactory.FIELD_NAME, 100);
|
||||
|
||||
Options options = tool.getOptions(GhidraOptions.CATEGORY_BROWSER_FIELDS);
|
||||
options.setBoolean(EolCommentFieldFactory.ENABLE_WORD_WRAP_MSG, true);
|
||||
options.setInt(EolCommentFieldFactory.MAX_DISPLAY_LINES_MSG, 100);
|
||||
options.setBoolean(EolCommentFieldFactory.ENABLE_WORD_WRAP_KEY, true);
|
||||
options.setInt(EolCommentFieldFactory.MAX_DISPLAY_LINES_KEY, 100);
|
||||
|
||||
runSwing(() -> tool.getToolFrame().setSize(800, 800));
|
||||
|
||||
|
@ -65,7 +65,7 @@ public class EolCommentFieldFactoryTest extends AbstractGhidraHeadedIntegrationT
|
||||
ListingTextField tf = getFieldText(function);
|
||||
assertEquals(2, tf.getNumRows());
|
||||
|
||||
setBooleanOption(EolCommentFieldFactory.ENABLE_WORD_WRAP_MSG, true);
|
||||
setBooleanOption(EolCommentFieldFactory.ENABLE_WORD_WRAP_KEY, true);
|
||||
|
||||
tf = getFieldText(function);
|
||||
assertEquals(4, tf.getNumRows());
|
||||
|
@ -243,7 +243,8 @@ public class OptionsDialogTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
// skip options that are "not simple", i.e. have custom editors
|
||||
if (simpleName.equals("Display Namespace") ||
|
||||
simpleName.equals("Array Display Options") ||
|
||||
simpleName.equals("Address Display Options")) {
|
||||
simpleName.equals("Address Display Options") ||
|
||||
simpleName.equals("Auto Comments")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -18,13 +18,16 @@ package ghidra.app.plugin.core.comments;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import generic.test.AbstractGenericTest;
|
||||
import ghidra.app.cmd.refs.AddMemRefCmd;
|
||||
import ghidra.app.util.DisplayableEol;
|
||||
import ghidra.app.util.EolComments;
|
||||
import ghidra.app.util.viewer.field.EolEnablement;
|
||||
import ghidra.app.util.viewer.field.EolExtraCommentsOption;
|
||||
import ghidra.framework.cmd.Command;
|
||||
import ghidra.program.database.ProgramBuilder;
|
||||
import ghidra.program.database.ProgramDB;
|
||||
@ -37,11 +40,11 @@ import ghidra.program.model.symbol.SourceType;
|
||||
import ghidra.test.AbstractGhidraHeadlessIntegrationTest;
|
||||
import ghidra.util.exception.RollbackException;
|
||||
|
||||
public class DisplayableEolTest extends AbstractGenericTest {
|
||||
public class EolCommentsTest extends AbstractGenericTest {
|
||||
|
||||
private ProgramDB program;
|
||||
|
||||
public DisplayableEolTest() {
|
||||
public EolCommentsTest() {
|
||||
super();
|
||||
}
|
||||
|
||||
@ -72,12 +75,22 @@ public class DisplayableEolTest extends AbstractGenericTest {
|
||||
|
||||
Listing listing = program.getListing();
|
||||
CodeUnit cu = listing.getCodeUnitAt(addr("0x110"));
|
||||
DisplayableEol displayableEol =
|
||||
new DisplayableEol(cu, true, true, true, false, 5, true, true);
|
||||
|
||||
String[] comments = displayableEol.getAutomaticComment();
|
||||
assertEquals(1, comments.length);
|
||||
assertEquals("? -> 00000120", comments[0]);
|
||||
EolExtraCommentsOption eolOption = createShowAllOption();
|
||||
EolComments eolComments = new EolComments(cu, false, 5, eolOption);
|
||||
|
||||
List<String> comments = eolComments.getAutomaticComment();
|
||||
assertEquals(1, comments.size());
|
||||
assertEquals("? -> 00000120", comments.get(0));
|
||||
}
|
||||
|
||||
private EolExtraCommentsOption createShowAllOption() {
|
||||
EolExtraCommentsOption eolOption = new EolExtraCommentsOption();
|
||||
eolOption.setRepeatable(EolEnablement.ALWAYS);
|
||||
eolOption.setRefRepeatable(EolEnablement.ALWAYS);
|
||||
eolOption.setAutoData(EolEnablement.ALWAYS);
|
||||
eolOption.setAutoFunction(EolEnablement.ALWAYS);
|
||||
return eolOption;
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -89,12 +102,12 @@ public class DisplayableEolTest extends AbstractGenericTest {
|
||||
|
||||
Listing listing = program.getListing();
|
||||
CodeUnit cu = listing.getCodeUnitAt(addr("0x1001000"));
|
||||
DisplayableEol displayableEol =
|
||||
new DisplayableEol(cu, true, true, true, false, 5, true, true);
|
||||
EolExtraCommentsOption eolOption = createShowAllOption();
|
||||
EolComments eolComments = new EolComments(cu, false, 5, eolOption);
|
||||
|
||||
String[] comments = displayableEol.getAutomaticComment();
|
||||
assertEquals(1, comments.length);
|
||||
assertEquals("= \"one.two\"", comments[0]);
|
||||
List<String> comments = eolComments.getAutomaticComment();
|
||||
assertEquals(1, comments.size());
|
||||
assertEquals("= \"one.two\"", comments.get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -109,15 +122,15 @@ public class DisplayableEolTest extends AbstractGenericTest {
|
||||
Listing listing = program.getListing();
|
||||
CodeUnit cu = listing.getCodeUnitAt(addr("0x1001000"));
|
||||
|
||||
EolExtraCommentsOption eolOption = createShowAllOption();
|
||||
|
||||
// with this at false, all of the string will be rendered
|
||||
boolean useAbbreviatedComments = false;
|
||||
eolOption.setUseAbbreviatedComments(false);
|
||||
EolComments eolComments = new EolComments(cu, false, 5, eolOption);
|
||||
|
||||
DisplayableEol displayableEol =
|
||||
new DisplayableEol(cu, true, true, true, false, 5, useAbbreviatedComments, true);
|
||||
|
||||
String[] comments = displayableEol.getAutomaticComment();
|
||||
assertEquals(1, comments.length);
|
||||
assertEquals("= \"one.two\"", comments[0]);
|
||||
List<String> comments = eolComments.getAutomaticComment();
|
||||
assertEquals(1, comments.size());
|
||||
assertEquals("= \"one.two\"", comments.get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -132,16 +145,16 @@ public class DisplayableEolTest extends AbstractGenericTest {
|
||||
Listing listing = program.getListing();
|
||||
CodeUnit cu = listing.getCodeUnitAt(addr("0x1001000"));
|
||||
|
||||
// with this at false, all of the string will be rendered
|
||||
boolean useAbbreviatedComments = false;
|
||||
boolean showAutoFunctions = false;
|
||||
DisplayableEol displayableEol =
|
||||
new DisplayableEol(cu, true, true, true, false, 5, useAbbreviatedComments,
|
||||
showAutoFunctions);
|
||||
EolExtraCommentsOption eolOption = createShowAllOption();
|
||||
|
||||
String[] comments = displayableEol.getAutomaticComment();
|
||||
assertEquals(1, comments.length);
|
||||
assertEquals("= \"one.two\"", comments[0]);
|
||||
// with this at false, all of the string will be rendered
|
||||
eolOption.setUseAbbreviatedComments(false);
|
||||
eolOption.setAutoFunction(EolEnablement.NEVER);
|
||||
EolComments eolComments = new EolComments(cu, false, 5, eolOption);
|
||||
|
||||
List<String> comments = eolComments.getAutomaticComment();
|
||||
assertEquals(1, comments.size());
|
||||
assertEquals("= \"one.two\"", comments.get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -159,15 +172,15 @@ public class DisplayableEolTest extends AbstractGenericTest {
|
||||
Listing listing = program.getListing();
|
||||
CodeUnit cu = listing.getCodeUnitAt(addr("0x1001000"));
|
||||
|
||||
EolExtraCommentsOption eolOption = createShowAllOption();
|
||||
|
||||
// with this at true, only the used part of the string will be rendered
|
||||
boolean useAbbreviatedComments = true;
|
||||
eolOption.setUseAbbreviatedComments(true);
|
||||
EolComments eolComments = new EolComments(cu, false, 5, eolOption);
|
||||
|
||||
DisplayableEol displayableEol =
|
||||
new DisplayableEol(cu, true, true, true, false, 5, useAbbreviatedComments, true);
|
||||
|
||||
String[] comments = displayableEol.getAutomaticComment();
|
||||
assertEquals(1, comments.length);
|
||||
assertEquals("= \"two\"", comments[0]);// full string is one.two
|
||||
List<String> comments = eolComments.getAutomaticComment();
|
||||
assertEquals(1, comments.size());
|
||||
assertEquals("= \"two\"", comments.get(0));// full string is one.two
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -181,13 +194,13 @@ public class DisplayableEolTest extends AbstractGenericTest {
|
||||
|
||||
Listing listing = program.getListing();
|
||||
CodeUnit cu = listing.getCodeUnitAt(from);
|
||||
boolean showAutoFunctions = true;
|
||||
DisplayableEol displayableEol =
|
||||
new DisplayableEol(cu, true, true, true, false, 5, false, showAutoFunctions);
|
||||
|
||||
String[] comments = displayableEol.getAutomaticComment();
|
||||
assertEquals(1, comments.length);
|
||||
assertEquals("undefined FUN_01001050()", comments[0]);
|
||||
EolExtraCommentsOption eolOption = createShowAllOption();
|
||||
EolComments eolComments = new EolComments(cu, false, 5, eolOption);
|
||||
|
||||
List<String> comments = eolComments.getAutomaticComment();
|
||||
assertEquals(1, comments.size());
|
||||
assertEquals("undefined FUN_01001050()", comments.get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -201,12 +214,16 @@ public class DisplayableEolTest extends AbstractGenericTest {
|
||||
|
||||
Listing listing = program.getListing();
|
||||
CodeUnit cu = listing.getCodeUnitAt(from);
|
||||
boolean showAutoFunctions = false;
|
||||
DisplayableEol displayableEol =
|
||||
new DisplayableEol(cu, true, true, true, false, 5, false, showAutoFunctions);
|
||||
|
||||
String[] comments = displayableEol.getAutomaticComment();
|
||||
assertEquals(0, comments.length);
|
||||
EolExtraCommentsOption eolOption = createShowAllOption();
|
||||
|
||||
// with this at false, all of the string will be rendered
|
||||
eolOption.setUseAbbreviatedComments(false);
|
||||
eolOption.setAutoFunction(EolEnablement.NEVER);
|
||||
EolComments eolComments = new EolComments(cu, false, 5, eolOption);
|
||||
|
||||
List<String> comments = eolComments.getAutomaticComment();
|
||||
assertEquals(0, comments.size());
|
||||
}
|
||||
|
||||
public boolean applyCmd(Command cmd) throws RollbackException {
|
@ -27,7 +27,6 @@ import ghidra.util.layout.HorizontalLayout;
|
||||
public class CustomOptionComponent extends GenericOptionsComponent {
|
||||
|
||||
protected CustomOptionComponent(EditorState editorState) {
|
||||
super(editorState);
|
||||
|
||||
// this layout allows us to easily left-align the single component in this container
|
||||
setLayout(new HorizontalLayout(0));
|
||||
|
@ -32,7 +32,6 @@ public class DefaultOptionComponent extends GenericOptionsComponent {
|
||||
private Component component;
|
||||
|
||||
public DefaultOptionComponent(EditorState editorState) {
|
||||
super(editorState);
|
||||
setLayout(new PairLayout(0, 6, 40));
|
||||
this.component = editorState.getEditorComponent();
|
||||
|
||||
@ -71,7 +70,7 @@ public class DefaultOptionComponent extends GenericOptionsComponent {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setAlignmentPreferredSize(Dimension dimension) {
|
||||
protected void setPreferredAlignmentSize(Dimension dimension) {
|
||||
label.setPreferredSize(dimension);
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -16,56 +15,56 @@
|
||||
*/
|
||||
package docking.options.editor;
|
||||
|
||||
import ghidra.framework.options.EditorState;
|
||||
|
||||
import java.awt.Dimension;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.JPanel;
|
||||
|
||||
import ghidra.framework.options.EditorState;
|
||||
|
||||
public abstract class GenericOptionsComponent extends JPanel {
|
||||
protected final EditorState editorState;
|
||||
|
||||
/**
|
||||
* Do not use this constructor directly. Instead, use the factory method:
|
||||
* {@link #createOptionComponent(EditorState)}
|
||||
*/
|
||||
protected GenericOptionsComponent(EditorState editorState) {
|
||||
this.editorState = editorState;
|
||||
protected GenericOptionsComponent() {
|
||||
// stub
|
||||
}
|
||||
|
||||
/**
|
||||
* A factory method to create new OptionComponents.
|
||||
* @param state The state that will be used to create the correct OptionComponent
|
||||
* @return the new OptionComponent.
|
||||
*/
|
||||
public static GenericOptionsComponent createOptionComponent( EditorState state ) {
|
||||
if ( state.supportsCustomOptionsEditor() ) {
|
||||
return new CustomOptionComponent( state );
|
||||
}
|
||||
return new DefaultOptionComponent( state );
|
||||
}
|
||||
/**
|
||||
* A factory method to create new OptionComponents.
|
||||
* @param state The state that will be used to create the correct OptionComponent
|
||||
* @return the new OptionComponent.
|
||||
*/
|
||||
public static GenericOptionsComponent createOptionComponent(EditorState state) {
|
||||
if (state.supportsCustomOptionsEditor()) {
|
||||
return new CustomOptionComponent(state);
|
||||
}
|
||||
return new DefaultOptionComponent(state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and sets a preferred alignment based upon the given list of option components.
|
||||
* @param components the list of options components from which to determine the alignment.
|
||||
*/
|
||||
/**
|
||||
* Creates and sets a preferred alignment based upon the given list of option components.
|
||||
* @param components the list of options components from which to determine the alignment.
|
||||
*/
|
||||
public static void alignLabels(List<GenericOptionsComponent> components) {
|
||||
int maxWidth = 0;
|
||||
int maxHeight = 0;
|
||||
for (GenericOptionsComponent optionComponent : components) {
|
||||
Dimension dimension = optionComponent.getPreferredAlignmentSize();
|
||||
maxWidth = Math.max( dimension.width, maxWidth );
|
||||
maxHeight = Math.max( dimension.height, maxHeight );
|
||||
}
|
||||
Dimension dimension = optionComponent.getPreferredAlignmentSize();
|
||||
maxWidth = Math.max(dimension.width, maxWidth);
|
||||
maxHeight = Math.max(dimension.height, maxHeight);
|
||||
}
|
||||
|
||||
for (GenericOptionsComponent component : components) {
|
||||
component.setAlignmentPreferredSize( new Dimension(maxWidth, maxHeight));
|
||||
}
|
||||
component.setPreferredAlignmentSize(new Dimension(maxWidth, maxHeight));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnabled(boolean enabled) {
|
||||
public void setEnabled(boolean enabled) {
|
||||
// stub
|
||||
}
|
||||
|
||||
/**
|
||||
@ -73,7 +72,8 @@ public abstract class GenericOptionsComponent extends JPanel {
|
||||
* components.
|
||||
* @param dimension The alignment dimension.
|
||||
*/
|
||||
protected void setAlignmentPreferredSize( Dimension dimension ) {
|
||||
protected void setPreferredAlignmentSize(Dimension dimension) {
|
||||
// stub
|
||||
}
|
||||
|
||||
/**
|
||||
@ -81,6 +81,6 @@ public abstract class GenericOptionsComponent extends JPanel {
|
||||
* @return the alignment dimension.
|
||||
*/
|
||||
protected Dimension getPreferredAlignmentSize() {
|
||||
return getPreferredSize();
|
||||
return getPreferredSize();
|
||||
}
|
||||
}
|
||||
|
@ -18,33 +18,28 @@ package ghidra.framework.options;
|
||||
public interface CustomOption {
|
||||
|
||||
/**
|
||||
* <code>SaveState</code> key which corresponds to custom option
|
||||
* implementation class. The use of this key/value within the stored
|
||||
* state information is reserved for use by the option storage
|
||||
* implementation and should be ignored by {@link #readState(SaveState)}
|
||||
* implementation
|
||||
* Key which corresponds to custom option implementation class. The use of this key/value
|
||||
* within the stored state information is reserved for use by the option storage implementation
|
||||
* and should be ignored by {@link #readState(GProperties)} implementation.
|
||||
*/
|
||||
public final String CUSTOM_OPTION_CLASS_NAME_KEY = "CUSTOM_OPTION_CLASS";
|
||||
|
||||
/**
|
||||
* Concrete subclass of WrappedOption should read all of its
|
||||
* state from the given saveState object.
|
||||
* Read state from the given properties
|
||||
* @param properties container of state information
|
||||
*/
|
||||
public void readState(GProperties properties);
|
||||
|
||||
/**
|
||||
* Concrete subclass of WrappedOption should write all of its
|
||||
* state to the given saveState object.
|
||||
* Write state into the given properties
|
||||
* @param properties container of state information
|
||||
*/
|
||||
public void writeState(GProperties properties);
|
||||
|
||||
/**
|
||||
* CustomOption should implement this method to provide a formatted
|
||||
* string value of this option value. The returned value will
|
||||
* be used in support of the {@link Options#getValueAsString(String)}
|
||||
* and {@link Options#getDefaultValueAsString(String)}.
|
||||
* Subclasses should implement this method to provide a formatted string value of this option
|
||||
* value. The returned value will be used in support of the
|
||||
* {@link Options#getValueAsString(String)} and {@link Options#getDefaultValueAsString(String)}.
|
||||
* @return option value as string
|
||||
*/
|
||||
@Override
|
||||
|
Loading…
Reference in New Issue
Block a user