mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2024-10-23 05:31:02 +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>
|
<H3><A name="EOL_Comments_Field"></A>EOL Comments Field</H3>
|
||||||
|
|
||||||
<BLOCKQUOTE>
|
<BLOCKQUOTE>
|
||||||
<P>The EOL Comments field displays the end-of-line comment at this address. If there is no
|
<P>The EOL Comments field displays the end-of-line comment at this address. By default,
|
||||||
EOL comment, it displays the repeatable comment. If there is no repeatable comment, it
|
if there is no EOL comment, then other comment types may be displayed.
|
||||||
displays the repeatable comments from all referenced addresses. If there aren't any
|
</P>
|
||||||
referenced repeatable comments, it displays an automatic comment if it can.</P>
|
|
||||||
|
|
||||||
<P><B>Always Show the Automatic Comment -</B> Normally automatic comments are not shown if
|
<P><B>Additional Comment Types -</B> The following comment types may be optionally displayed
|
||||||
there is an EOL comment, repeatable comment, or referenced repeatable comment. By selecting
|
in the EOL Comments field. For each type below, the following setting may be applied:
|
||||||
this option, the automatic comment will be shown even if there is an EOL comment,
|
<UL>
|
||||||
repeatable comment, or referenced repeatable comment.</P>
|
<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><B>Always Show the Referenced Repeatable Comment -</B> Normally referenced repeatable
|
<P>
|
||||||
comments are not shown if there is an EOL comment or repeatable comment. By selecting this
|
Each of the comments below is listed in order of precedence. The default behavior is to
|
||||||
option, the referenced repeatable comment will be shown even if there is an EOL comment or
|
show one comment in the EOL Comments field at a time, based on this precedence, with the EOL
|
||||||
repeatable comment.</P>
|
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 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
|
<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
|
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.io.*;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import generic.theme.GThemeDefaults.Colors.Messages;
|
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.template.TemplateSimplifier;
|
||||||
|
import ghidra.app.util.viewer.field.EolExtraCommentsOption;
|
||||||
import ghidra.framework.plugintool.ServiceProvider;
|
import ghidra.framework.plugintool.ServiceProvider;
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
import ghidra.program.model.address.AddressSetView;
|
import ghidra.program.model.address.AddressSetView;
|
||||||
|
@ -56,10 +58,6 @@ class ProgramTextWriter {
|
||||||
ProgramTextOptions options, ServiceProvider provider) throws FileNotFoundException {
|
ProgramTextOptions options, ServiceProvider provider) throws FileNotFoundException {
|
||||||
|
|
||||||
this.options = options;
|
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.program = program;
|
||||||
this.listing = program.getListing();
|
this.listing = program.getListing();
|
||||||
this.memory = program.getMemory();
|
this.memory = program.getMemory();
|
||||||
|
@ -293,29 +291,7 @@ class ProgramTextWriter {
|
||||||
//// End of Line Area //////////////////////////////////////////
|
//// End of Line Area //////////////////////////////////////////
|
||||||
|
|
||||||
if (options.isShowComments()) {
|
if (options.isShowComments()) {
|
||||||
DisplayableEol displayableEol = new DisplayableEol(currentCodeUnit, false, false,
|
addComments(currentCodeUnit);
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (buffy.length() > 0) {
|
if (buffy.length() > 0) {
|
||||||
|
@ -369,6 +345,35 @@ class ProgramTextWriter {
|
||||||
writer.close();
|
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,
|
private void insertUndefinedBytesRemovedMarker(Address bytesRemovedRangeStart,
|
||||||
Address bytesRemovedRangeEnd) {
|
Address bytesRemovedRangeEnd) {
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
package ghidra.app.util.viewer.field;
|
package ghidra.app.util.viewer.field;
|
||||||
|
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
|
import java.beans.PropertyEditor;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -28,13 +29,13 @@ import ghidra.app.util.viewer.field.ListingColors.CommentColors;
|
||||||
import ghidra.app.util.viewer.format.FieldFormatModel;
|
import ghidra.app.util.viewer.format.FieldFormatModel;
|
||||||
import ghidra.app.util.viewer.options.OptionsGui;
|
import ghidra.app.util.viewer.options.OptionsGui;
|
||||||
import ghidra.app.util.viewer.proxy.ProxyObj;
|
import ghidra.app.util.viewer.proxy.ProxyObj;
|
||||||
import ghidra.framework.options.Options;
|
import ghidra.framework.options.*;
|
||||||
import ghidra.framework.options.ToolOptions;
|
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
import ghidra.program.model.listing.*;
|
import ghidra.program.model.listing.*;
|
||||||
import ghidra.program.util.*;
|
import ghidra.program.util.*;
|
||||||
import ghidra.util.HelpLocation;
|
import ghidra.util.HelpLocation;
|
||||||
import ghidra.util.bean.field.AnnotatedTextFieldElement;
|
import ghidra.util.bean.field.AnnotatedTextFieldElement;
|
||||||
|
import ghidra.util.exception.AssertException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates End of line comment Fields.
|
* Generates End of line comment Fields.
|
||||||
|
@ -43,42 +44,34 @@ public class EolCommentFieldFactory extends FieldFactory {
|
||||||
public static final String FIELD_NAME = "EOL Comment";
|
public static final String FIELD_NAME = "EOL Comment";
|
||||||
private static final String GROUP_TITLE = "EOL Comments Field";
|
private static final String GROUP_TITLE = "EOL Comments Field";
|
||||||
private static final String SEMICOLON_PREFIX = "; ";
|
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;
|
GROUP_TITLE + Options.DELIMITER + FieldUtils.WORD_WRAP_OPTION_NAME;
|
||||||
public static final String MAX_DISPLAY_LINES_MSG =
|
public static final String MAX_DISPLAY_LINES_KEY =
|
||||||
GROUP_TITLE + Options.DELIMITER + "Maximum Lines To Display";
|
GROUP_TITLE + Options.DELIMITER + "Maximum Lines";
|
||||||
public static final String ENABLE_SHOW_SEMICOLON_MSG =
|
public static final String ENABLE_SHOW_SEMICOLON_KEY =
|
||||||
GROUP_TITLE + Options.DELIMITER + "Show Semicolon at Start of Each Line";
|
GROUP_TITLE + Options.DELIMITER + "Prepend Semicolon";
|
||||||
public static final String ENABLE_ALWAYS_SHOW_REPEATABLE_MSG =
|
public static final String ENABLE_PREPEND_REF_ADDRESS_KEY =
|
||||||
GROUP_TITLE + Options.DELIMITER + "Always Show the Repeatable Comment";
|
GROUP_TITLE + Options.DELIMITER + "Prepend Address to References";
|
||||||
public static final String ENABLE_ALWAYS_SHOW_REF_REPEATABLE_MSG =
|
public static final String EXTRA_COMMENT_KEY =
|
||||||
GROUP_TITLE + Options.DELIMITER + "Always Show the Referenced Repeatable Comments";
|
GROUP_TITLE + Options.DELIMITER + "Auto 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 Color DEFAULT_COLOR = Palette.BLUE;
|
public static final Color DEFAULT_COLOR = Palette.BLUE;
|
||||||
|
|
||||||
private boolean isWordWrap;
|
private boolean isWordWrap;
|
||||||
private int maxDisplayLines;
|
private int maxDisplayLines;
|
||||||
private boolean showSemicolon;
|
private boolean showSemicolon;
|
||||||
private boolean alwaysShowRepeatable;
|
|
||||||
private boolean alwaysShowRefRepeatables;
|
|
||||||
private boolean alwaysShowAutomatic;
|
|
||||||
private boolean useAbbreviatedAutomatic;
|
|
||||||
private boolean showAutomaticFunctions;
|
|
||||||
private boolean prependRefAddress;
|
private boolean prependRefAddress;
|
||||||
private int repeatableCommentStyle;
|
private int repeatableCommentStyle;
|
||||||
private int automaticCommentStyle;
|
private int automaticCommentStyle;
|
||||||
private int refRepeatableCommentStyle;
|
private int refRepeatableCommentStyle;
|
||||||
|
|
||||||
// The codeUnitFormatOptions is used to monitor "follow pointer..." option to avoid
|
private EolExtraCommentsOption extraCommentsOption = new EolExtraCommentsOption();
|
||||||
// duplication of data within auto-comment. We don't bother adding a listener
|
private PropertyEditor extraCommmentsEditor = new EolExtraCommentsPropertyEditor();
|
||||||
// to kick the model since this is done by the operand field.
|
|
||||||
|
// 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;
|
private BrowserCodeUnitFormatOptions codeUnitFormatOptions;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -100,55 +93,49 @@ public class EolCommentFieldFactory extends FieldFactory {
|
||||||
super(FIELD_NAME, model, hlProvider, displayOptions, fieldOptions);
|
super(FIELD_NAME, model, hlProvider, displayOptions, fieldOptions);
|
||||||
HelpLocation hl = new HelpLocation("CodeBrowserPlugin", "EOL_Comments_Field");
|
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.");
|
"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);
|
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. " +
|
"Displays a semi-colon before each line in the end-of-line comment. " +
|
||||||
"This option is ignored if word wrapping is on.");
|
"This option is ignored if word wrapping is on.");
|
||||||
|
|
||||||
fieldOptions.registerOption(ENABLE_ALWAYS_SHOW_REPEATABLE_MSG, false, hl,
|
fieldOptions.registerOption(ENABLE_PREPEND_REF_ADDRESS_KEY, 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,
|
|
||||||
"Displays the address before each referenced repeatable comment.");
|
"Displays the address before each referenced repeatable comment.");
|
||||||
|
|
||||||
maxDisplayLines = fieldOptions.getInt(MAX_DISPLAY_LINES_MSG, 6);
|
maxDisplayLines = fieldOptions.getInt(MAX_DISPLAY_LINES_KEY, 6);
|
||||||
isWordWrap = fieldOptions.getBoolean(ENABLE_WORD_WRAP_MSG, false);
|
isWordWrap = fieldOptions.getBoolean(ENABLE_WORD_WRAP_KEY, false);
|
||||||
repeatableCommentStyle =
|
repeatableCommentStyle =
|
||||||
displayOptions.getInt(OptionsGui.COMMENT_REPEATABLE.getStyleOptionName(), -1);
|
displayOptions.getInt(OptionsGui.COMMENT_REPEATABLE.getStyleOptionName(), -1);
|
||||||
automaticCommentStyle =
|
automaticCommentStyle =
|
||||||
displayOptions.getInt(OptionsGui.COMMENT_AUTO.getStyleOptionName(), -1);
|
displayOptions.getInt(OptionsGui.COMMENT_AUTO.getStyleOptionName(), -1);
|
||||||
refRepeatableCommentStyle =
|
refRepeatableCommentStyle =
|
||||||
displayOptions.getInt(OptionsGui.COMMENT_REF_REPEAT.getStyleOptionName(), -1);
|
displayOptions.getInt(OptionsGui.COMMENT_REF_REPEAT.getStyleOptionName(), -1);
|
||||||
showSemicolon = fieldOptions.getBoolean(ENABLE_SHOW_SEMICOLON_MSG, false);
|
showSemicolon = fieldOptions.getBoolean(ENABLE_SHOW_SEMICOLON_KEY, false);
|
||||||
alwaysShowRepeatable = fieldOptions.getBoolean(ENABLE_ALWAYS_SHOW_REPEATABLE_MSG, false);
|
prependRefAddress = fieldOptions.getBoolean(ENABLE_PREPEND_REF_ADDRESS_KEY, 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);
|
|
||||||
|
|
||||||
fieldOptions.getOptions(GROUP_TITLE).setOptionsHelpLocation(hl);
|
fieldOptions.getOptions(GROUP_TITLE).setOptionsHelpLocation(hl);
|
||||||
|
|
||||||
codeUnitFormatOptions = new BrowserCodeUnitFormatOptions(fieldOptions, true);
|
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
|
@Override
|
||||||
public void fieldOptionsChanged(Options options, String optionName, Object oldValue,
|
public void fieldOptionsChanged(Options options, String optionName, Object oldValue,
|
||||||
Object newValue) {
|
Object newValue) {
|
||||||
if (optionName.equals(MAX_DISPLAY_LINES_MSG)) {
|
if (optionName.equals(MAX_DISPLAY_LINES_KEY)) {
|
||||||
setMaximumLinesToDisplay(((Integer) newValue).intValue(), options);
|
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();
|
isWordWrap = ((Boolean) newValue).booleanValue();
|
||||||
}
|
}
|
||||||
else if (optionName.equals(ENABLE_SHOW_SEMICOLON_MSG)) {
|
else if (optionName.equals(ENABLE_SHOW_SEMICOLON_KEY)) {
|
||||||
showSemicolon = ((Boolean) newValue).booleanValue();
|
showSemicolon = ((Boolean) newValue).booleanValue();
|
||||||
}
|
}
|
||||||
else if (optionName.equals(ENABLE_ALWAYS_SHOW_REPEATABLE_MSG)) {
|
else if (optionName.equals(EXTRA_COMMENT_KEY)) {
|
||||||
alwaysShowRepeatable = ((Boolean) newValue).booleanValue();
|
extraCommentsOption = (EolExtraCommentsOption) newValue;
|
||||||
}
|
}
|
||||||
else if (optionName.equals(ENABLE_ALWAYS_SHOW_REF_REPEATABLE_MSG)) {
|
else if (optionName.equals(ENABLE_PREPEND_REF_ADDRESS_KEY)) {
|
||||||
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)) {
|
|
||||||
prependRefAddress = ((Boolean) newValue).booleanValue();
|
prependRefAddress = ((Boolean) newValue).booleanValue();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -249,7 +227,7 @@ public class EolCommentFieldFactory extends FieldFactory {
|
||||||
private void setMaximumLinesToDisplay(int maxLines, Options options) {
|
private void setMaximumLinesToDisplay(int maxLines, Options options) {
|
||||||
if (maxLines < 1) {
|
if (maxLines < 1) {
|
||||||
maxLines = 1;
|
maxLines = 1;
|
||||||
options.setInt(MAX_DISPLAY_LINES_MSG, maxLines);
|
options.setInt(MAX_DISPLAY_LINES_KEY, maxLines);
|
||||||
}
|
}
|
||||||
maxDisplayLines = maxLines;
|
maxDisplayLines = maxLines;
|
||||||
}
|
}
|
||||||
|
@ -279,57 +257,44 @@ public class EolCommentFieldFactory extends FieldFactory {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DisplayableEol displayableEol =
|
EolComments comments =
|
||||||
new DisplayableEol(cu, alwaysShowRepeatable, alwaysShowRefRepeatables,
|
new EolComments(cu, codeUnitFormatOptions.followReferencedPointers(),
|
||||||
alwaysShowAutomatic, codeUnitFormatOptions.followReferencedPointers(),
|
maxDisplayLines, extraCommentsOption);
|
||||||
maxDisplayLines, useAbbreviatedAutomatic, showAutomaticFunctions);
|
|
||||||
|
|
||||||
List<FieldElement> elementList = new ArrayList<>();
|
|
||||||
|
|
||||||
// This Code Unit's End of Line Comment
|
// This Code Unit's End of Line Comment
|
||||||
AttributedString myEolPrefixString = new AttributedString(SEMICOLON_PREFIX,
|
List<FieldElement> elementList = new ArrayList<>();
|
||||||
CommentColors.EOL, getMetrics(style), false, null);
|
AttributedString prefix = createPrefix(style);
|
||||||
String[] eolComments = displayableEol.getEOLComments();
|
List<String> eols = comments.getEOLComments();
|
||||||
List<FieldElement> eolFieldElements = convertToFieldElements(program, eolComments,
|
List<FieldElement> eolElements = convertToFieldElements(program, eols, prefix, 0);
|
||||||
myEolPrefixString, showSemicolon, isWordWrap, getNextRow(elementList));
|
elementList.addAll(eolElements);
|
||||||
elementList.addAll(eolFieldElements);
|
|
||||||
|
|
||||||
// This Code Unit's Repeatable Comment
|
if (comments.isShowingRepeatables()) {
|
||||||
if (alwaysShowRepeatable || elementList.isEmpty()) {
|
prefix = createPrefix(repeatableCommentStyle);
|
||||||
AttributedString myRepeatablePrefixString = new AttributedString(SEMICOLON_PREFIX,
|
int row = getNextRow(elementList);
|
||||||
CommentColors.REPEATABLE, getMetrics(repeatableCommentStyle), false, null);
|
List<String> repeatables = comments.getRepeatableComments();
|
||||||
String[] repeatableComments = displayableEol.getRepeatableComments();
|
List<FieldElement> elements = convertToFieldElements(program, repeatables, prefix, row);
|
||||||
List<FieldElement> repeatableFieldElements =
|
elementList.addAll(elements);
|
||||||
convertToFieldElements(program, repeatableComments, myRepeatablePrefixString,
|
|
||||||
showSemicolon, isWordWrap, getNextRow(elementList));
|
|
||||||
elementList.addAll(repeatableFieldElements);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Referenced Repeatable Comments
|
if (comments.isShowingRefRepeatables()) {
|
||||||
if (alwaysShowRefRepeatables || elementList.isEmpty()) {
|
prefix = createPrefix(refRepeatableCommentStyle);
|
||||||
AttributedString refRepeatPrefixString = new AttributedString(SEMICOLON_PREFIX,
|
List<RefRepeatComment> refRepeatables =
|
||||||
CommentColors.REF_REPEATABLE, getMetrics(refRepeatableCommentStyle), false, null);
|
comments.getReferencedRepeatableComments();
|
||||||
int refRepeatCount = displayableEol.getReferencedRepeatableCommentsCount();
|
for (RefRepeatComment comment : refRepeatables) {
|
||||||
for (int subTypeIndex = 0; subTypeIndex < refRepeatCount; subTypeIndex++) {
|
int row = getNextRow(elementList);
|
||||||
RefRepeatComment refRepeatComment =
|
String[] lines = comment.getCommentLines();
|
||||||
displayableEol.getReferencedRepeatableComments(subTypeIndex);
|
List<FieldElement> elements = convertToRefFieldElements(
|
||||||
String[] refRepeatComments = refRepeatComment.getCommentLines();
|
lines, program, prefix, comment.getAddress(), row);
|
||||||
List<FieldElement> refRepeatFieldElements = convertToRefFieldElements(
|
elementList.addAll(elements);
|
||||||
refRepeatComments, program, refRepeatPrefixString, showSemicolon, isWordWrap,
|
|
||||||
prependRefAddress, refRepeatComment.getAddress(), getNextRow(elementList));
|
|
||||||
elementList.addAll(refRepeatFieldElements);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Automatic Comment
|
if (comments.isShowingAutoComments()) {
|
||||||
if (alwaysShowAutomatic || elementList.isEmpty()) {
|
prefix = createPrefix(automaticCommentStyle);
|
||||||
AttributedString autoCommentPrefixString = new AttributedString(SEMICOLON_PREFIX,
|
int row = getNextRow(elementList);
|
||||||
CommentColors.AUTO, getMetrics(automaticCommentStyle), false, null);
|
List<String> autos = comments.getAutomaticComment();
|
||||||
String[] autoComment = displayableEol.getAutomaticComment();
|
List<FieldElement> elements = convertToFieldElements(program, autos, prefix, row);
|
||||||
List<FieldElement> autoCommentFieldElements =
|
elementList.addAll(elements);
|
||||||
convertToFieldElements(program, autoComment, autoCommentPrefixString, showSemicolon,
|
|
||||||
isWordWrap, getNextRow(elementList));
|
|
||||||
elementList.addAll(autoCommentFieldElements);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FieldElement[] fieldElements = elementList.toArray(new FieldElement[elementList.size()]);
|
FieldElement[] fieldElements = elementList.toArray(new FieldElement[elementList.size()]);
|
||||||
|
@ -340,6 +305,27 @@ public class EolCommentFieldFactory extends FieldFactory {
|
||||||
maxDisplayLines, hlProvider);
|
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) {
|
private int getNextRow(List<FieldElement> elementList) {
|
||||||
int elementIndex = elementList.size() - 1;
|
int elementIndex = elementList.size() - 1;
|
||||||
if (elementIndex >= 0) {
|
if (elementIndex >= 0) {
|
||||||
|
@ -352,26 +338,25 @@ public class EolCommentFieldFactory extends FieldFactory {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<FieldElement> convertToFieldElements(Program program, String[] comments,
|
private List<FieldElement> convertToFieldElements(Program program, List<String> comments,
|
||||||
AttributedString currentPrefixString, boolean showPrefix, boolean wordWrap,
|
AttributedString currentPrefixString, int nextRow) {
|
||||||
int nextRow) {
|
|
||||||
|
|
||||||
List<FieldElement> fieldElements = new ArrayList<>();
|
List<FieldElement> fieldElements = new ArrayList<>();
|
||||||
if (comments.length == 0) {
|
if (comments.isEmpty()) {
|
||||||
return fieldElements;
|
return fieldElements;
|
||||||
}
|
}
|
||||||
for (int rowIndex = 0; rowIndex < comments.length; rowIndex++) {
|
for (int rowIndex = 0; rowIndex < comments.size(); rowIndex++) {
|
||||||
int encodedRow = nextRow + rowIndex;
|
int encodedRow = nextRow + rowIndex;
|
||||||
fieldElements.add(CommentUtils.parseTextForAnnotations(comments[rowIndex], program,
|
fieldElements.add(CommentUtils.parseTextForAnnotations(comments.get(rowIndex), program,
|
||||||
currentPrefixString, encodedRow));
|
currentPrefixString, encodedRow));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wordWrap) {
|
if (isWordWrap) {
|
||||||
int lineWidth = showPrefix ? width - currentPrefixString.getStringWidth() : width;
|
int lineWidth = showSemicolon ? width - currentPrefixString.getStringWidth() : width;
|
||||||
fieldElements = FieldUtils.wrap(fieldElements, lineWidth);
|
fieldElements = FieldUtils.wrap(fieldElements, lineWidth);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showPrefix) {
|
if (showSemicolon) {
|
||||||
for (int i = 0; i < fieldElements.size(); i++) {
|
for (int i = 0; i < fieldElements.size(); i++) {
|
||||||
RowColLocation startRowCol =
|
RowColLocation startRowCol =
|
||||||
fieldElements.get(i).getDataLocationForCharacterIndex(0);
|
fieldElements.get(i).getDataLocationForCharacterIndex(0);
|
||||||
|
@ -387,8 +372,7 @@ public class EolCommentFieldFactory extends FieldFactory {
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<FieldElement> convertToRefFieldElements(String[] comments, Program program,
|
private List<FieldElement> convertToRefFieldElements(String[] comments, Program program,
|
||||||
AttributedString currentPrefixString, boolean showPrefix, boolean wordWrap,
|
AttributedString currentPrefixString, Address refAddress, int nextRow) {
|
||||||
boolean showRefAddress, Address refAddress, int nextRow) {
|
|
||||||
|
|
||||||
int numCommentLines = comments.length;
|
int numCommentLines = comments.length;
|
||||||
List<FieldElement> fieldElements = new ArrayList<>();
|
List<FieldElement> fieldElements = new ArrayList<>();
|
||||||
|
@ -400,7 +384,7 @@ public class EolCommentFieldFactory extends FieldFactory {
|
||||||
fieldElements.add(CommentUtils.parseTextForAnnotations(comments[rowIndex], program,
|
fieldElements.add(CommentUtils.parseTextForAnnotations(comments[rowIndex], program,
|
||||||
currentPrefixString, encodedRow));
|
currentPrefixString, encodedRow));
|
||||||
}
|
}
|
||||||
if (showRefAddress) {
|
if (prependRefAddress) {
|
||||||
FieldElement commentElement = fieldElements.get(0);
|
FieldElement commentElement = fieldElements.get(0);
|
||||||
// Address
|
// Address
|
||||||
String refAddrComment = "{@address " + refAddress.toString() + "}";
|
String refAddrComment = "{@address " + refAddress.toString() + "}";
|
||||||
|
@ -418,12 +402,12 @@ public class EolCommentFieldFactory extends FieldFactory {
|
||||||
new FieldElement[] { addressElement, spacerElement, commentElement }));
|
new FieldElement[] { addressElement, spacerElement, commentElement }));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wordWrap) {
|
if (isWordWrap) {
|
||||||
int lineWidth = showPrefix ? width - currentPrefixString.getStringWidth() : width;
|
int lineWidth = showSemicolon ? width - currentPrefixString.getStringWidth() : width;
|
||||||
fieldElements = FieldUtils.wrap(fieldElements, lineWidth);
|
fieldElements = FieldUtils.wrap(fieldElements, lineWidth);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showPrefix) {
|
if (showSemicolon) {
|
||||||
for (int i = 0; i < fieldElements.size(); i++) {
|
for (int i = 0; i < fieldElements.size(); i++) {
|
||||||
RowColLocation startRowCol =
|
RowColLocation startRowCol =
|
||||||
fieldElements.get(i).getDataLocationForCharacterIndex(0);
|
fieldElements.get(i).getDataLocationForCharacterIndex(0);
|
||||||
|
@ -451,10 +435,9 @@ public class EolCommentFieldFactory extends FieldFactory {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
CodeUnit cu = (CodeUnit) obj;
|
CodeUnit cu = (CodeUnit) obj;
|
||||||
DisplayableEol displayableEol =
|
EolComments displayableEol =
|
||||||
new DisplayableEol(cu, alwaysShowRepeatable, alwaysShowRefRepeatables,
|
new EolComments(cu, codeUnitFormatOptions.followReferencedPointers(),
|
||||||
alwaysShowAutomatic, codeUnitFormatOptions.followReferencedPointers(),
|
maxDisplayLines, extraCommentsOption);
|
||||||
maxDisplayLines, useAbbreviatedAutomatic, showAutomaticFunctions);
|
|
||||||
|
|
||||||
// Hold position in connected tool if navigating within semicolon.
|
// Hold position in connected tool if navigating within semicolon.
|
||||||
int numLeadColumns = 0;
|
int numLeadColumns = 0;
|
||||||
|
@ -489,10 +472,9 @@ public class EolCommentFieldFactory extends FieldFactory {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
DisplayableEol displayableEol =
|
EolComments displayableEol =
|
||||||
new DisplayableEol((CodeUnit) obj, alwaysShowRepeatable, alwaysShowRefRepeatables,
|
new EolComments((CodeUnit) obj, codeUnitFormatOptions.followReferencedPointers(),
|
||||||
alwaysShowAutomatic, codeUnitFormatOptions.followReferencedPointers(),
|
maxDisplayLines, extraCommentsOption);
|
||||||
maxDisplayLines, useAbbreviatedAutomatic, showAutomaticFunctions);
|
|
||||||
|
|
||||||
ListingTextField btf = (ListingTextField) bf;
|
ListingTextField btf = (ListingTextField) bf;
|
||||||
RowColLocation eolRowCol = displayableEol.getRowCol((CommentFieldLocation) loc);
|
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) {
|
private void setupNamespaceOptions(Options fieldOptions) {
|
||||||
// we need to install a custom editor that allows us to edit a group of related options
|
// 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,
|
fieldOptions.registerOption(NAMESPACE_OPTIONS_KEY, OptionType.CUSTOM_TYPE,
|
||||||
new NamespaceWrappedOption(), null, "Adjusts the XREFs Field namespace display",
|
new NamespaceWrappedOption(), new HelpLocation("CodeBrowserPlugin", "XREFs_Field"),
|
||||||
namespaceOptionsEditor);
|
"Adjusts the XREFs Field namespace display", namespaceOptionsEditor);
|
||||||
CustomOption customOption = fieldOptions.getCustomOption(NAMESPACE_OPTIONS_KEY, null);
|
CustomOption customOption = fieldOptions.getCustomOption(NAMESPACE_OPTIONS_KEY, null);
|
||||||
fieldOptions.getOptions(NAMESPACE_OPTIONS_KEY)
|
|
||||||
.setOptionsHelpLocation(new HelpLocation("CodeBrowserPlugin", "XREFs_Field"));
|
|
||||||
if (!(customOption instanceof NamespaceWrappedOption)) {
|
if (!(customOption instanceof NamespaceWrappedOption)) {
|
||||||
throw new AssertException("Someone set an option for " + NAMESPACE_OPTIONS_KEY +
|
throw new AssertException("Someone set an option for " + NAMESPACE_OPTIONS_KEY +
|
||||||
" that is not the expected " +
|
" that is not the expected " +
|
||||||
"ghidra.app.util.viewer.field.NamespaceWrappedOption type.");
|
NamespaceWrappedOption.class.getName() + " type.");
|
||||||
}
|
}
|
||||||
|
|
||||||
NamespaceWrappedOption namespaceOption = (NamespaceWrappedOption) customOption;
|
NamespaceWrappedOption namespaceOption = (NamespaceWrappedOption) customOption;
|
||||||
|
|
|
@ -578,29 +578,16 @@ public class CodeBrowserOptionsTest extends AbstractGhidraHeadedIntegrationTest
|
||||||
@Test
|
@Test
|
||||||
public void testEOLCommentsOptions() throws Exception {
|
public void testEOLCommentsOptions() throws Exception {
|
||||||
|
|
||||||
final int SHOW_AUTO = 0;
|
// for readability
|
||||||
final int SHOW_REF_REPEAT = 1;
|
String EXTRA_COMMENTS = EolCommentFieldFactory.EXTRA_COMMENT_KEY;
|
||||||
final int SHOW_REPEATABLE = 2;
|
String WORD_WRAP = EolCommentFieldFactory.ENABLE_WORD_WRAP_KEY;
|
||||||
final int WORD_WRAP = 3;
|
String MAX_LINES = EolCommentFieldFactory.MAX_DISPLAY_LINES_KEY;
|
||||||
final int MAX_LINES = 4;
|
String SHOW_REF_ADDR = EolCommentFieldFactory.ENABLE_PREPEND_REF_ADDRESS_KEY;
|
||||||
final int SHOW_REF_ADDR = 5;
|
String SHOW_SEMICOLON = EolCommentFieldFactory.ENABLE_SHOW_SEMICOLON_KEY;
|
||||||
//final int SHOW_FUNCTION_AUTO = 6;
|
|
||||||
final int SHOW_SEMICOLON = 7;
|
|
||||||
showTool(tool);
|
showTool(tool);
|
||||||
loadProgram();
|
loadProgram();
|
||||||
Options options = tool.getOptions(GhidraOptions.CATEGORY_BROWSER_FIELDS);
|
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 callAddress = addr("0x1003fcc");
|
||||||
Address callRefAddress = addr("0x1006642");
|
Address callRefAddress = addr("0x1006642");
|
||||||
Address otherRefAddress = addr("0x1003fa1");
|
Address otherRefAddress = addr("0x1003fa1");
|
||||||
|
@ -637,66 +624,77 @@ public class CodeBrowserOptionsTest extends AbstractGhidraHeadedIntegrationTest
|
||||||
CodeUnit.REPEATABLE_COMMENT, "Mem ref line1.\n" + "");
|
CodeUnit.REPEATABLE_COMMENT, "Mem ref line1.\n" + "");
|
||||||
tool.execute(commentRefCmd, program);
|
tool.execute(commentRefCmd, program);
|
||||||
|
|
||||||
options.setBoolean(names.get(SHOW_AUTO), false);
|
// these values are all DEFAULT, by default; set them in case that changes in the future
|
||||||
options.setBoolean(names.get(SHOW_REF_REPEAT), false);
|
EolExtraCommentsOption extraCommentsOption = new EolExtraCommentsOption();
|
||||||
options.setBoolean(names.get(SHOW_REPEATABLE), false);
|
extraCommentsOption.setRepeatable(EolEnablement.DEFAULT);
|
||||||
options.setBoolean(names.get(WORD_WRAP), false);
|
extraCommentsOption.setRefRepeatable(EolEnablement.DEFAULT);
|
||||||
options.setInt(names.get(MAX_LINES), 20);
|
extraCommentsOption.setAutoData(EolEnablement.DEFAULT);
|
||||||
options.setBoolean(names.get(SHOW_REF_ADDR), false);
|
extraCommentsOption.setAutoFunction(EolEnablement.DEFAULT);
|
||||||
options.setBoolean(names.get(SHOW_SEMICOLON), false);
|
|
||||||
|
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.updateNow();
|
||||||
cb.goToField(callAddress, "EOL Comment", 0, 0);
|
cb.goToField(callAddress, "EOL Comment", 0, 0);
|
||||||
ListingTextField btf = (ListingTextField) cb.getCurrentField();
|
ListingTextField btf = (ListingTextField) cb.getCurrentField();
|
||||||
assertEquals(9, getNumberOfLines(btf));
|
assertEquals(9, getNumberOfLines(btf));
|
||||||
|
|
||||||
options.setBoolean(names.get(WORD_WRAP), true);
|
options.setBoolean(WORD_WRAP, true);
|
||||||
cb.updateNow();
|
cb.updateNow();
|
||||||
btf = (ListingTextField) cb.getCurrentField();
|
btf = (ListingTextField) cb.getCurrentField();
|
||||||
assertEquals(18, getNumberOfLines(btf));
|
assertEquals(18, getNumberOfLines(btf));
|
||||||
|
|
||||||
options.setBoolean(names.get(SHOW_SEMICOLON), true);
|
options.setBoolean(SHOW_SEMICOLON, true);
|
||||||
cb.updateNow();
|
cb.updateNow();
|
||||||
btf = (ListingTextField) cb.getCurrentField();
|
btf = (ListingTextField) cb.getCurrentField();
|
||||||
assertEquals(20, getNumberOfLines(btf));
|
assertEquals(20, getNumberOfLines(btf));
|
||||||
assertEquals("; ", btf.getFieldElement(1, 0).getText());
|
assertEquals("; ", btf.getFieldElement(1, 0).getText());
|
||||||
|
|
||||||
options.setBoolean(names.get(SHOW_REPEATABLE), true);
|
extraCommentsOption.setRepeatable(EolEnablement.ALWAYS);
|
||||||
|
options.setCustomOption(EXTRA_COMMENTS, extraCommentsOption);
|
||||||
cb.updateNow();
|
cb.updateNow();
|
||||||
btf = (ListingTextField) cb.getCurrentField();
|
btf = (ListingTextField) cb.getCurrentField();
|
||||||
assertEquals(20, getNumberOfLines(btf));
|
assertEquals(20, getNumberOfLines(btf));
|
||||||
assertEquals("; ", btf.getFieldElement(1, 0).getText());
|
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();
|
cb.updateNow();
|
||||||
btf = (ListingTextField) cb.getCurrentField();
|
btf = (ListingTextField) cb.getCurrentField();
|
||||||
assertEquals(20, getNumberOfLines(btf));
|
assertEquals(20, getNumberOfLines(btf));
|
||||||
assertEquals("; ", btf.getFieldElement(1, 0).getText());
|
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();
|
cb.updateNow();
|
||||||
btf = (ListingTextField) cb.getCurrentField();
|
btf = (ListingTextField) cb.getCurrentField();
|
||||||
assertEquals(20, getNumberOfLines(btf));
|
assertEquals(20, getNumberOfLines(btf));
|
||||||
assertEquals("; ", btf.getFieldElement(1, 0).getText());
|
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();
|
cb.updateNow();
|
||||||
btf = (ListingTextField) cb.getCurrentField();
|
btf = (ListingTextField) cb.getCurrentField();
|
||||||
assertEquals(20, getNumberOfLines(btf));
|
assertEquals(20, getNumberOfLines(btf));
|
||||||
assertEquals("; ", btf.getFieldElement(1, 0).getText());
|
assertEquals("; ", btf.getFieldElement(1, 0).getText());
|
||||||
|
|
||||||
options.setBoolean(names.get(SHOW_REPEATABLE), false);
|
extraCommentsOption.setRepeatable(EolEnablement.DEFAULT);
|
||||||
|
options.setCustomOption(EXTRA_COMMENTS, extraCommentsOption);
|
||||||
cb.updateNow();
|
cb.updateNow();
|
||||||
btf = (ListingTextField) cb.getCurrentField();
|
btf = (ListingTextField) cb.getCurrentField();
|
||||||
assertEquals(20, getNumberOfLines(btf));
|
assertEquals(20, getNumberOfLines(btf));
|
||||||
assertEquals("; ", btf.getFieldElement(1, 0).getText());
|
assertEquals("; ", btf.getFieldElement(1, 0).getText());
|
||||||
|
|
||||||
options.setBoolean(names.get(WORD_WRAP), false);
|
options.setBoolean(WORD_WRAP, false);
|
||||||
cb.updateNow();
|
cb.updateNow();
|
||||||
btf = (ListingTextField) cb.getCurrentField();
|
btf = (ListingTextField) cb.getCurrentField();
|
||||||
assertEquals(12, getNumberOfLines(btf));
|
assertEquals(12, getNumberOfLines(btf));
|
||||||
assertTrue("; ".equals(btf.getFieldElement(5, 0).getText()));
|
assertTrue("; ".equals(btf.getFieldElement(5, 0).getText()));
|
||||||
|
|
||||||
options.setBoolean(names.get(SHOW_SEMICOLON), false);
|
options.setBoolean(SHOW_SEMICOLON, false);
|
||||||
cb.updateNow();
|
cb.updateNow();
|
||||||
btf = (ListingTextField) cb.getCurrentField();
|
btf = (ListingTextField) cb.getCurrentField();
|
||||||
assertEquals(12, getNumberOfLines(btf));
|
assertEquals(12, getNumberOfLines(btf));
|
||||||
|
@ -704,7 +702,7 @@ public class CodeBrowserOptionsTest extends AbstractGhidraHeadedIntegrationTest
|
||||||
assertEquals("01003fa1", btf.getFieldElement(11, 4).getText());
|
assertEquals("01003fa1", btf.getFieldElement(11, 4).getText());
|
||||||
assertEquals("Mem ref line1.", btf.getFieldElement(11, 11).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();
|
cb.updateNow();
|
||||||
btf = (ListingTextField) cb.getCurrentField();
|
btf = (ListingTextField) cb.getCurrentField();
|
||||||
assertEquals(11, getNumberOfLines(btf));
|
assertEquals(11, getNumberOfLines(btf));
|
||||||
|
|
|
@ -247,8 +247,8 @@ public class CommentsPluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
setFieldWidth(browser, EolCommentFieldFactory.FIELD_NAME, 100);
|
setFieldWidth(browser, EolCommentFieldFactory.FIELD_NAME, 100);
|
||||||
|
|
||||||
Options options = tool.getOptions(GhidraOptions.CATEGORY_BROWSER_FIELDS);
|
Options options = tool.getOptions(GhidraOptions.CATEGORY_BROWSER_FIELDS);
|
||||||
options.setBoolean(EolCommentFieldFactory.ENABLE_WORD_WRAP_MSG, true);
|
options.setBoolean(EolCommentFieldFactory.ENABLE_WORD_WRAP_KEY, true);
|
||||||
options.setInt(EolCommentFieldFactory.MAX_DISPLAY_LINES_MSG, 100);
|
options.setInt(EolCommentFieldFactory.MAX_DISPLAY_LINES_KEY, 100);
|
||||||
|
|
||||||
runSwing(() -> tool.getToolFrame().setSize(800, 800));
|
runSwing(() -> tool.getToolFrame().setSize(800, 800));
|
||||||
|
|
||||||
|
|
|
@ -65,7 +65,7 @@ public class EolCommentFieldFactoryTest extends AbstractGhidraHeadedIntegrationT
|
||||||
ListingTextField tf = getFieldText(function);
|
ListingTextField tf = getFieldText(function);
|
||||||
assertEquals(2, tf.getNumRows());
|
assertEquals(2, tf.getNumRows());
|
||||||
|
|
||||||
setBooleanOption(EolCommentFieldFactory.ENABLE_WORD_WRAP_MSG, true);
|
setBooleanOption(EolCommentFieldFactory.ENABLE_WORD_WRAP_KEY, true);
|
||||||
|
|
||||||
tf = getFieldText(function);
|
tf = getFieldText(function);
|
||||||
assertEquals(4, tf.getNumRows());
|
assertEquals(4, tf.getNumRows());
|
||||||
|
|
|
@ -243,7 +243,8 @@ public class OptionsDialogTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
// skip options that are "not simple", i.e. have custom editors
|
// skip options that are "not simple", i.e. have custom editors
|
||||||
if (simpleName.equals("Display Namespace") ||
|
if (simpleName.equals("Display Namespace") ||
|
||||||
simpleName.equals("Array Display Options") ||
|
simpleName.equals("Array Display Options") ||
|
||||||
simpleName.equals("Address Display Options")) {
|
simpleName.equals("Address Display Options") ||
|
||||||
|
simpleName.equals("Auto Comments")) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,13 +18,16 @@ package ghidra.app.plugin.core.comments;
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import generic.test.AbstractGenericTest;
|
import generic.test.AbstractGenericTest;
|
||||||
import ghidra.app.cmd.refs.AddMemRefCmd;
|
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.framework.cmd.Command;
|
||||||
import ghidra.program.database.ProgramBuilder;
|
import ghidra.program.database.ProgramBuilder;
|
||||||
import ghidra.program.database.ProgramDB;
|
import ghidra.program.database.ProgramDB;
|
||||||
|
@ -37,11 +40,11 @@ import ghidra.program.model.symbol.SourceType;
|
||||||
import ghidra.test.AbstractGhidraHeadlessIntegrationTest;
|
import ghidra.test.AbstractGhidraHeadlessIntegrationTest;
|
||||||
import ghidra.util.exception.RollbackException;
|
import ghidra.util.exception.RollbackException;
|
||||||
|
|
||||||
public class DisplayableEolTest extends AbstractGenericTest {
|
public class EolCommentsTest extends AbstractGenericTest {
|
||||||
|
|
||||||
private ProgramDB program;
|
private ProgramDB program;
|
||||||
|
|
||||||
public DisplayableEolTest() {
|
public EolCommentsTest() {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,12 +75,22 @@ public class DisplayableEolTest extends AbstractGenericTest {
|
||||||
|
|
||||||
Listing listing = program.getListing();
|
Listing listing = program.getListing();
|
||||||
CodeUnit cu = listing.getCodeUnitAt(addr("0x110"));
|
CodeUnit cu = listing.getCodeUnitAt(addr("0x110"));
|
||||||
DisplayableEol displayableEol =
|
|
||||||
new DisplayableEol(cu, true, true, true, false, 5, true, true);
|
|
||||||
|
|
||||||
String[] comments = displayableEol.getAutomaticComment();
|
EolExtraCommentsOption eolOption = createShowAllOption();
|
||||||
assertEquals(1, comments.length);
|
EolComments eolComments = new EolComments(cu, false, 5, eolOption);
|
||||||
assertEquals("? -> 00000120", comments[0]);
|
|
||||||
|
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
|
@Test
|
||||||
|
@ -89,12 +102,12 @@ public class DisplayableEolTest extends AbstractGenericTest {
|
||||||
|
|
||||||
Listing listing = program.getListing();
|
Listing listing = program.getListing();
|
||||||
CodeUnit cu = listing.getCodeUnitAt(addr("0x1001000"));
|
CodeUnit cu = listing.getCodeUnitAt(addr("0x1001000"));
|
||||||
DisplayableEol displayableEol =
|
EolExtraCommentsOption eolOption = createShowAllOption();
|
||||||
new DisplayableEol(cu, true, true, true, false, 5, true, true);
|
EolComments eolComments = new EolComments(cu, false, 5, eolOption);
|
||||||
|
|
||||||
String[] comments = displayableEol.getAutomaticComment();
|
List<String> comments = eolComments.getAutomaticComment();
|
||||||
assertEquals(1, comments.length);
|
assertEquals(1, comments.size());
|
||||||
assertEquals("= \"one.two\"", comments[0]);
|
assertEquals("= \"one.two\"", comments.get(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -109,15 +122,15 @@ public class DisplayableEolTest extends AbstractGenericTest {
|
||||||
Listing listing = program.getListing();
|
Listing listing = program.getListing();
|
||||||
CodeUnit cu = listing.getCodeUnitAt(addr("0x1001000"));
|
CodeUnit cu = listing.getCodeUnitAt(addr("0x1001000"));
|
||||||
|
|
||||||
|
EolExtraCommentsOption eolOption = createShowAllOption();
|
||||||
|
|
||||||
// with this at false, all of the string will be rendered
|
// 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 =
|
List<String> comments = eolComments.getAutomaticComment();
|
||||||
new DisplayableEol(cu, true, true, true, false, 5, useAbbreviatedComments, true);
|
assertEquals(1, comments.size());
|
||||||
|
assertEquals("= \"one.two\"", comments.get(0));
|
||||||
String[] comments = displayableEol.getAutomaticComment();
|
|
||||||
assertEquals(1, comments.length);
|
|
||||||
assertEquals("= \"one.two\"", comments[0]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -132,16 +145,16 @@ public class DisplayableEolTest extends AbstractGenericTest {
|
||||||
Listing listing = program.getListing();
|
Listing listing = program.getListing();
|
||||||
CodeUnit cu = listing.getCodeUnitAt(addr("0x1001000"));
|
CodeUnit cu = listing.getCodeUnitAt(addr("0x1001000"));
|
||||||
|
|
||||||
// with this at false, all of the string will be rendered
|
EolExtraCommentsOption eolOption = createShowAllOption();
|
||||||
boolean useAbbreviatedComments = false;
|
|
||||||
boolean showAutoFunctions = false;
|
|
||||||
DisplayableEol displayableEol =
|
|
||||||
new DisplayableEol(cu, true, true, true, false, 5, useAbbreviatedComments,
|
|
||||||
showAutoFunctions);
|
|
||||||
|
|
||||||
String[] comments = displayableEol.getAutomaticComment();
|
// with this at false, all of the string will be rendered
|
||||||
assertEquals(1, comments.length);
|
eolOption.setUseAbbreviatedComments(false);
|
||||||
assertEquals("= \"one.two\"", comments[0]);
|
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
|
@Test
|
||||||
|
@ -159,15 +172,15 @@ public class DisplayableEolTest extends AbstractGenericTest {
|
||||||
Listing listing = program.getListing();
|
Listing listing = program.getListing();
|
||||||
CodeUnit cu = listing.getCodeUnitAt(addr("0x1001000"));
|
CodeUnit cu = listing.getCodeUnitAt(addr("0x1001000"));
|
||||||
|
|
||||||
|
EolExtraCommentsOption eolOption = createShowAllOption();
|
||||||
|
|
||||||
// with this at true, only the used part of the string will be rendered
|
// 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 =
|
List<String> comments = eolComments.getAutomaticComment();
|
||||||
new DisplayableEol(cu, true, true, true, false, 5, useAbbreviatedComments, true);
|
assertEquals(1, comments.size());
|
||||||
|
assertEquals("= \"two\"", comments.get(0));// full string is one.two
|
||||||
String[] comments = displayableEol.getAutomaticComment();
|
|
||||||
assertEquals(1, comments.length);
|
|
||||||
assertEquals("= \"two\"", comments[0]);// full string is one.two
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -181,13 +194,13 @@ public class DisplayableEolTest extends AbstractGenericTest {
|
||||||
|
|
||||||
Listing listing = program.getListing();
|
Listing listing = program.getListing();
|
||||||
CodeUnit cu = listing.getCodeUnitAt(from);
|
CodeUnit cu = listing.getCodeUnitAt(from);
|
||||||
boolean showAutoFunctions = true;
|
|
||||||
DisplayableEol displayableEol =
|
|
||||||
new DisplayableEol(cu, true, true, true, false, 5, false, showAutoFunctions);
|
|
||||||
|
|
||||||
String[] comments = displayableEol.getAutomaticComment();
|
EolExtraCommentsOption eolOption = createShowAllOption();
|
||||||
assertEquals(1, comments.length);
|
EolComments eolComments = new EolComments(cu, false, 5, eolOption);
|
||||||
assertEquals("undefined FUN_01001050()", comments[0]);
|
|
||||||
|
List<String> comments = eolComments.getAutomaticComment();
|
||||||
|
assertEquals(1, comments.size());
|
||||||
|
assertEquals("undefined FUN_01001050()", comments.get(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -201,12 +214,16 @@ public class DisplayableEolTest extends AbstractGenericTest {
|
||||||
|
|
||||||
Listing listing = program.getListing();
|
Listing listing = program.getListing();
|
||||||
CodeUnit cu = listing.getCodeUnitAt(from);
|
CodeUnit cu = listing.getCodeUnitAt(from);
|
||||||
boolean showAutoFunctions = false;
|
|
||||||
DisplayableEol displayableEol =
|
|
||||||
new DisplayableEol(cu, true, true, true, false, 5, false, showAutoFunctions);
|
|
||||||
|
|
||||||
String[] comments = displayableEol.getAutomaticComment();
|
EolExtraCommentsOption eolOption = createShowAllOption();
|
||||||
assertEquals(0, comments.length);
|
|
||||||
|
// 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 {
|
public boolean applyCmd(Command cmd) throws RollbackException {
|
|
@ -27,7 +27,6 @@ import ghidra.util.layout.HorizontalLayout;
|
||||||
public class CustomOptionComponent extends GenericOptionsComponent {
|
public class CustomOptionComponent extends GenericOptionsComponent {
|
||||||
|
|
||||||
protected CustomOptionComponent(EditorState editorState) {
|
protected CustomOptionComponent(EditorState editorState) {
|
||||||
super(editorState);
|
|
||||||
|
|
||||||
// this layout allows us to easily left-align the single component in this container
|
// this layout allows us to easily left-align the single component in this container
|
||||||
setLayout(new HorizontalLayout(0));
|
setLayout(new HorizontalLayout(0));
|
||||||
|
|
|
@ -32,7 +32,6 @@ public class DefaultOptionComponent extends GenericOptionsComponent {
|
||||||
private Component component;
|
private Component component;
|
||||||
|
|
||||||
public DefaultOptionComponent(EditorState editorState) {
|
public DefaultOptionComponent(EditorState editorState) {
|
||||||
super(editorState);
|
|
||||||
setLayout(new PairLayout(0, 6, 40));
|
setLayout(new PairLayout(0, 6, 40));
|
||||||
this.component = editorState.getEditorComponent();
|
this.component = editorState.getEditorComponent();
|
||||||
|
|
||||||
|
@ -71,7 +70,7 @@ public class DefaultOptionComponent extends GenericOptionsComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void setAlignmentPreferredSize(Dimension dimension) {
|
protected void setPreferredAlignmentSize(Dimension dimension) {
|
||||||
label.setPreferredSize(dimension);
|
label.setPreferredSize(dimension);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
/* ###
|
/* ###
|
||||||
* IP: GHIDRA
|
* IP: GHIDRA
|
||||||
* REVIEWED: YES
|
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -16,22 +15,21 @@
|
||||||
*/
|
*/
|
||||||
package docking.options.editor;
|
package docking.options.editor;
|
||||||
|
|
||||||
import ghidra.framework.options.EditorState;
|
|
||||||
|
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
|
|
||||||
|
import ghidra.framework.options.EditorState;
|
||||||
|
|
||||||
public abstract class GenericOptionsComponent extends JPanel {
|
public abstract class GenericOptionsComponent extends JPanel {
|
||||||
protected final EditorState editorState;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Do not use this constructor directly. Instead, use the factory method:
|
* Do not use this constructor directly. Instead, use the factory method:
|
||||||
* {@link #createOptionComponent(EditorState)}
|
* {@link #createOptionComponent(EditorState)}
|
||||||
*/
|
*/
|
||||||
protected GenericOptionsComponent(EditorState editorState) {
|
protected GenericOptionsComponent() {
|
||||||
this.editorState = editorState;
|
// stub
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -39,11 +37,11 @@ public abstract class GenericOptionsComponent extends JPanel {
|
||||||
* @param state The state that will be used to create the correct OptionComponent
|
* @param state The state that will be used to create the correct OptionComponent
|
||||||
* @return the new OptionComponent.
|
* @return the new OptionComponent.
|
||||||
*/
|
*/
|
||||||
public static GenericOptionsComponent createOptionComponent( EditorState state ) {
|
public static GenericOptionsComponent createOptionComponent(EditorState state) {
|
||||||
if ( state.supportsCustomOptionsEditor() ) {
|
if (state.supportsCustomOptionsEditor()) {
|
||||||
return new CustomOptionComponent( state );
|
return new CustomOptionComponent(state);
|
||||||
}
|
}
|
||||||
return new DefaultOptionComponent( state );
|
return new DefaultOptionComponent(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -55,17 +53,18 @@ public abstract class GenericOptionsComponent extends JPanel {
|
||||||
int maxHeight = 0;
|
int maxHeight = 0;
|
||||||
for (GenericOptionsComponent optionComponent : components) {
|
for (GenericOptionsComponent optionComponent : components) {
|
||||||
Dimension dimension = optionComponent.getPreferredAlignmentSize();
|
Dimension dimension = optionComponent.getPreferredAlignmentSize();
|
||||||
maxWidth = Math.max( dimension.width, maxWidth );
|
maxWidth = Math.max(dimension.width, maxWidth);
|
||||||
maxHeight = Math.max( dimension.height, maxHeight );
|
maxHeight = Math.max(dimension.height, maxHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (GenericOptionsComponent component : components) {
|
for (GenericOptionsComponent component : components) {
|
||||||
component.setAlignmentPreferredSize( new Dimension(maxWidth, maxHeight));
|
component.setPreferredAlignmentSize(new Dimension(maxWidth, maxHeight));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setEnabled(boolean enabled) {
|
public void setEnabled(boolean enabled) {
|
||||||
|
// stub
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -73,7 +72,8 @@ public abstract class GenericOptionsComponent extends JPanel {
|
||||||
* components.
|
* components.
|
||||||
* @param dimension The alignment dimension.
|
* @param dimension The alignment dimension.
|
||||||
*/
|
*/
|
||||||
protected void setAlignmentPreferredSize( Dimension dimension ) {
|
protected void setPreferredAlignmentSize(Dimension dimension) {
|
||||||
|
// stub
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -18,33 +18,28 @@ package ghidra.framework.options;
|
||||||
public interface CustomOption {
|
public interface CustomOption {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <code>SaveState</code> key which corresponds to custom option
|
* Key which corresponds to custom option implementation class. The use of this key/value
|
||||||
* implementation class. The use of this key/value within the stored
|
* within the stored state information is reserved for use by the option storage implementation
|
||||||
* state information is reserved for use by the option storage
|
* and should be ignored by {@link #readState(GProperties)} implementation.
|
||||||
* implementation and should be ignored by {@link #readState(SaveState)}
|
|
||||||
* implementation
|
|
||||||
*/
|
*/
|
||||||
public final String CUSTOM_OPTION_CLASS_NAME_KEY = "CUSTOM_OPTION_CLASS";
|
public final String CUSTOM_OPTION_CLASS_NAME_KEY = "CUSTOM_OPTION_CLASS";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Concrete subclass of WrappedOption should read all of its
|
* Read state from the given properties
|
||||||
* state from the given saveState object.
|
|
||||||
* @param properties container of state information
|
* @param properties container of state information
|
||||||
*/
|
*/
|
||||||
public void readState(GProperties properties);
|
public void readState(GProperties properties);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Concrete subclass of WrappedOption should write all of its
|
* Write state into the given properties
|
||||||
* state to the given saveState object.
|
|
||||||
* @param properties container of state information
|
* @param properties container of state information
|
||||||
*/
|
*/
|
||||||
public void writeState(GProperties properties);
|
public void writeState(GProperties properties);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CustomOption should implement this method to provide a formatted
|
* Subclasses should implement this method to provide a formatted string value of this option
|
||||||
* string value of this option value. The returned value will
|
* value. The returned value will be used in support of the
|
||||||
* be used in support of the {@link Options#getValueAsString(String)}
|
* {@link Options#getValueAsString(String)} and {@link Options#getDefaultValueAsString(String)}.
|
||||||
* and {@link Options#getDefaultValueAsString(String)}.
|
|
||||||
* @return option value as string
|
* @return option value as string
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
|
|
Loading…
Reference in New Issue
Block a user