GP-5996 - Version Tracking - Added option for apply function namespace

This commit is contained in:
dragonmacher
2025-10-03 19:00:44 -04:00
parent c997049e1f
commit 18f176152c
15 changed files with 2137 additions and 533 deletions
@@ -261,27 +261,27 @@ public abstract class AbstractGhidraHeadlessIntegrationTest extends AbstractDock
* *
* @param <T> the return type * @param <T> the return type
* @param <E> the exception type * @param <E> the exception type
* @param p the program * @param dobj the program or other domain object
* @param s the code to execute * @param s the code to execute
* @return the supplier's return value * @return the supplier's return value
* @see #modifyProgram(Program, ExceptionalCallback) * @see #modifyProgram(Program, ExceptionalCallback)
* @see #modifyProgram(Program, ExceptionalFunction) * @see #modifyProgram(Program, ExceptionalFunction)
*/ */
public static <T, E extends Exception> T tx(Program p, ExceptionalSupplier<T, E> s) { public static <T, E extends Exception> T tx(DomainObject dobj, ExceptionalSupplier<T, E> s) {
int txId = p.startTransaction("Test - Function in Transaction"); int txId = dobj.startTransaction("Test - Function in Transaction");
boolean commit = true; boolean commit = true;
try { try {
T t = s.get(); T t = s.get();
p.flushEvents(); dobj.flushEvents();
waitForSwing(); waitForSwing();
return t; return t;
} }
catch (Exception e) { catch (Exception e) {
commit = false; commit = false;
failWithException("Exception modifying program '" + p.getName() + "'", e); failWithException("Exception modifying program '" + dobj.getName() + "'", e);
} }
finally { finally {
p.endTransaction(txId, commit); dobj.endTransaction(txId, commit);
} }
return null; return null;
} }
Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 57 KiB

@@ -164,13 +164,18 @@
<ul> <ul>
<li><b>Do Not Apply</b> - Do not apply markup to the destination program</li> <li><b>Do Not Apply</b> - Do not apply markup to the destination program</li>
<li><b>Add</b> - Adds the function name from the source program to the one already <li><b>Add</b> - Adds the function name from the source program to the one already
at the address in the destination program.</li> at the address in the destination program. If the existing primary
symbol is default, then no new label will not be added, but instead
the existing label will be replaced with the source name.</li>
<li><b>Add As Primary</b> - Adds the function name from the source program to <li><b>Add As Primary</b> - Adds the function name from the source program to
the one already at the address in the destination program. Sets the the one already at the address in the destination program. Sets the
primary label in the destination program to whatever label was the primary label in the destination program to whatever label was the
primary one in the source program.</li> primary one in the source program. </li>
<li><b>Replace Always</b> - Always apply markup to from the source <li><b>Replace Always</b> - Always use the source symbol name to replace
program to the destination program.</li> the destination symbol name. The one exception is that if the
source symbol is a default symbol, then the destination symbol
name will be removed instead of being replaced with the source
symbol name.</li>
<li><b>Replace Default Only</b> - Only apply markup from the <li><b>Replace Default Only</b> - Only apply markup from the
source program to the destination program when the label source program to the destination program when the label
in the destination program is a default label.</li> in the destination program is a default label.</li>
@@ -425,6 +430,40 @@
</TD> </TD>
</TR> </TR>
<TR>
<TD><b>Function Signature</b> : <br>
Specifies the default action to take when applying the function
signature of a match.
</TD>
<TD nowrap><tt>Replace When Same Parameter Count</tt>
</TD>
</TR>
<TR>
<TD><b>Replace Namespace</b> : <br>
Specifies whether to apply the source function's namespace when applying
the function name.
<P>
<B>Note: If the source uses the Global namespace, then the destination
will not be updated when this option is on. The Global namespace is
considered the default namesapce. The assumption is that if the
destination program has a non-Global namespace, then the existing
destination namespace is preferred over the Global namespace.
</B>
</P>
</TD>
<TD nowrap><tt>False</tt>
</TD>
</TR>
<TR> <TR>
<TD><b>Inline</b> : <br> <TD><b>Inline</b> : <br>
Specifies the action to take for applying the inline flag when Specifies the action to take for applying the inline flag when
@@ -497,17 +536,7 @@
<TD nowrap><tt>False</tt> <TD nowrap><tt>False</tt>
</TD> </TD>
</TR> </TR>
<TR>
<TD><b>Function Signature</b> : <br>
Specifies the default action to take when applying the function
signature of a match.
</TD>
<TD nowrap><tt>Replace When Same Parameter Count</tt>
</TD>
</TR>
<TR> <TR>
<TD><b>Labels</b> : <br> <TD><b>Labels</b> : <br>
Specifies the default action to take when applying the label of a match. Specifies the default action to take when applying the label of a match.
@@ -4,9 +4,9 @@
* 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.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -115,33 +115,37 @@ public class MarkupItemImpl implements VTMarkupItem {
@Override @Override
public VTMarkupType getMarkupType() { public VTMarkupType getMarkupType() {
return markupItemStorage.getMarkupType(); return markupType;
} }
@Override @Override
public VTMarkupItemStatus getStatus() { public VTMarkupItemStatus getStatus() {
validateDestinationCache(); validateDestinationCache();
VTMarkupItemStatus status = markupItemStorage.getStatus(); VTMarkupItemStatus status = markupItemStorage.getStatus();
if (status == UNAPPLIED) { if (status != UNAPPLIED) {
if (!gettingStatus) { return status;
try { }
gettingStatus = true;
boolean conflictsWithOtherMarkup = getMarkupType().conflictsWithOtherMarkup( if (!gettingStatus) {
this, getAssociation().getMarkupItems(TaskMonitor.DUMMY)); try {
if (conflictsWithOtherMarkup) { gettingStatus = true;
return CONFLICT; VTAssociation association = getAssociation();
} Collection<VTMarkupItem> items = association.getMarkupItems(TaskMonitor.DUMMY);
} boolean conflicts = markupType.conflictsWithOtherMarkup(this, items);
catch (CancelledException e) { if (conflicts) {
// Shouldn't happen, but ignore if it does. return CONFLICT;
}
finally {
gettingStatus = false;
} }
} }
if (hasSameSourceDestinationValues()) { catch (CancelledException e) {
return SAME; // Shouldn't happen, but ignore if it does.
} }
finally {
gettingStatus = false;
}
}
if (hasSameSourceDestinationValues()) {
return SAME;
} }
return status; return status;
} }
@@ -196,15 +200,10 @@ public class MarkupItemImpl implements VTMarkupItem {
markupItemStorage.setApplyFailed("Can't apply without a valid destination"); markupItemStorage.setApplyFailed("Can't apply without a valid destination");
fireMarkupItemStatusChanged(oldStatus, FAILED_APPLY); fireMarkupItemStatusChanged(oldStatus, FAILED_APPLY);
throw new VersionTrackingApplyException( throw new VersionTrackingApplyException(
"Cannot apply a markup item without first " + "setting the destination address"); "Cannot apply a markup item without first setting the destination address");
} }
if (!canApply()) { if (!canApply()) {
// TODO Should this throw an Exception instead?
// VTMarkupType itemMarkupType = item.getMarkupType();
// throw new VersionTrackingApplyException("Cannot apply " +
// itemMarkupType.getDisplayName() + " at " + item.getDestinationAddress().toString() +
// ".");
return; return;
} }
@@ -1,13 +1,12 @@
/* ### /* ###
* 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.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -16,13 +15,19 @@
*/ */
package ghidra.feature.vt.api.markuptype; package ghidra.feature.vt.api.markuptype;
import static ghidra.feature.vt.gui.util.VTOptionDefines.*;
import java.util.*;
import ghidra.feature.vt.api.impl.MarkupItemImpl; import ghidra.feature.vt.api.impl.MarkupItemImpl;
import ghidra.feature.vt.api.main.*; import ghidra.feature.vt.api.main.*;
import ghidra.feature.vt.api.stringable.FunctionSignatureStringable; import ghidra.feature.vt.api.stringable.FunctionSignatureStringable;
import ghidra.feature.vt.api.util.Stringable; import ghidra.feature.vt.api.util.Stringable;
import ghidra.feature.vt.api.util.VersionTrackingApplyException; import ghidra.feature.vt.api.util.VersionTrackingApplyException;
import ghidra.feature.vt.gui.util.*; import ghidra.feature.vt.gui.plugin.VTController;
import ghidra.feature.vt.gui.util.VTMatchApplyChoices.FunctionSignatureChoices; import ghidra.feature.vt.gui.util.VTMatchApplyChoices;
import ghidra.feature.vt.gui.util.VTMatchApplyChoices.*;
import ghidra.feature.vt.gui.util.VTOptionDefines;
import ghidra.framework.options.Options; import ghidra.framework.options.Options;
import ghidra.framework.options.ToolOptions; import ghidra.framework.options.ToolOptions;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
@@ -30,16 +35,12 @@ import ghidra.program.model.listing.*;
import ghidra.program.util.*; import ghidra.program.util.*;
import ghidra.util.SystemUtilities; import ghidra.util.SystemUtilities;
import java.util.*;
public class FunctionSignatureMarkupType extends FunctionEntryPointBasedAbstractMarkupType { public class FunctionSignatureMarkupType extends FunctionEntryPointBasedAbstractMarkupType {
//================================================================================================== //==================================================================================================
// Factory Methods // Factory Methods
//================================================================================================== //==================================================================================================
public static final VTMarkupType INSTANCE = new FunctionSignatureMarkupType();
@Override @Override
public List<VTMarkupItem> createMarkupItems(VTAssociation association) { public List<VTMarkupItem> createMarkupItems(VTAssociation association) {
@@ -67,6 +68,10 @@ public class FunctionSignatureMarkupType extends FunctionEntryPointBasedAbstract
// End Factory Methods // End Factory Methods
//================================================================================================== //==================================================================================================
public static final VTMarkupType INSTANCE = new FunctionSignatureMarkupType();
private ToolOptions unapplyOptions;
private FunctionSignatureMarkupType() { private FunctionSignatureMarkupType() {
super("Function Signature"); super("Function Signature");
} }
@@ -106,8 +111,42 @@ public class FunctionSignatureMarkupType extends FunctionEntryPointBasedAbstract
return; return;
} }
destinationSignatureStringable.applyFunctionSignature(destinationFunction, ToolOptions options = getUnapplyOptions();
VT_UNAPPLY_MARKUP_OPTIONS, true); destinationSignatureStringable.applyFunctionSignature(destinationFunction, options, true);
}
private ToolOptions getUnapplyOptions() {
if (unapplyOptions != null) {
return unapplyOptions;
}
unapplyOptions = new ToolOptions(VTController.VERSION_TRACKING_OPTIONS_NAME);
unapplyOptions.setEnum(FUNCTION_SIGNATURE, FunctionSignatureChoices.REPLACE);
unapplyOptions.setEnum(CALLING_CONVENTION, CallingConventionChoices.NAME_MATCH);
unapplyOptions.setEnum(INLINE, ReplaceChoices.REPLACE);
unapplyOptions.setEnum(NO_RETURN, ReplaceChoices.REPLACE);
unapplyOptions.setEnum(VAR_ARGS, ReplaceChoices.REPLACE);
unapplyOptions.setEnum(CALL_FIXUP, ReplaceChoices.REPLACE);
unapplyOptions.setEnum(FUNCTION_RETURN_TYPE, ParameterDataTypeChoices.REPLACE);
unapplyOptions.setEnum(PARAMETER_DATA_TYPES, ParameterDataTypeChoices.REPLACE);
unapplyOptions.setEnum(PARAMETER_NAMES, SourcePriorityChoices.REPLACE);
unapplyOptions.setEnum(PARAMETER_COMMENTS, CommentChoices.OVERWRITE_EXISTING);
unapplyOptions.setEnum(FUNCTION_NAME, FunctionNameChoices.REPLACE_ALWAYS);
unapplyOptions.setEnum(LABELS, LabelChoices.REPLACE_ALL);
unapplyOptions.setEnum(PLATE_COMMENT, CommentChoices.OVERWRITE_EXISTING);
unapplyOptions.setEnum(PRE_COMMENT, CommentChoices.OVERWRITE_EXISTING);
unapplyOptions.setEnum(END_OF_LINE_COMMENT, CommentChoices.OVERWRITE_EXISTING);
unapplyOptions.setEnum(REPEATABLE_COMMENT, CommentChoices.OVERWRITE_EXISTING);
unapplyOptions.setEnum(POST_COMMENT, CommentChoices.OVERWRITE_EXISTING);
unapplyOptions.setEnum(DATA_MATCH_DATA_TYPE,
ReplaceDataChoices.REPLACE_ALL_DATA);
return unapplyOptions;
} }
@Override @Override
@@ -122,24 +161,6 @@ public class FunctionSignatureMarkupType extends FunctionEntryPointBasedAbstract
markupItem.getMarkupType().getDisplayName() + " since it is excluded."); markupItem.getMarkupType().getDisplayName() + " since it is excluded.");
} }
ToolOptions adjustedOptions = markupOptions.copy();
// switch (functionSignatureChoice) {
// case REPLACE:
// adjustedOptions.putEnum(VTOptionDefines.FUNCTION_SIGNATURE,
// FunctionSignatureChoices.REPLACE);
// break;
// case WHEN_SAME_PARAMETER_COUNT:
// adjustedOptions.putEnum(VTOptionDefines.FUNCTION_SIGNATURE,
// FunctionSignatureChoices.WHEN_SAME_PARAMETER_COUNT);
// break;
// case WHEN_TAKING_SIGNATURE:
// // Don't apply the names. The function signature will do it if needed.
// return false;
// default:
// throw new IllegalArgumentException("Unsupported apply action: " + applyAction);
//
// }
Address destinationAddress = markupItem.getDestinationAddress(); Address destinationAddress = markupItem.getDestinationAddress();
if (destinationAddress == null) { if (destinationAddress == null) {
@@ -147,7 +168,8 @@ public class FunctionSignatureMarkupType extends FunctionEntryPointBasedAbstract
} }
if (destinationAddress == Address.NO_ADDRESS) { if (destinationAddress == Address.NO_ADDRESS) {
throw new VersionTrackingApplyException("The destination address cannot be No Address!"); throw new VersionTrackingApplyException(
"The destination address cannot be No Address!");
} }
Program destinationProgram = getDestinationProgram(markupItem.getAssociation()); Program destinationProgram = getDestinationProgram(markupItem.getAssociation());
@@ -166,11 +188,7 @@ public class FunctionSignatureMarkupType extends FunctionEntryPointBasedAbstract
throw new VersionTrackingApplyException( throw new VersionTrackingApplyException(
"Couldn't find destination function to apply a name."); "Couldn't find destination function to apply a name.");
} }
if (sourceStringable.applyFunctionSignature(destinationFunction, adjustedOptions, false)) { if (sourceStringable.applyFunctionSignature(destinationFunction, markupOptions, false)) {
// // If the function signature was applied, apply the names if necessary.
// applyParameterNamesIfNeeded(markupItem, adjustedOptions);
// // If the function signature was applied, apply the no return flag if necessary.
// applyNoReturnIfNeeded(markupItem, adjustedOptions);
return true; return true;
} }
return false; return false;
@@ -223,8 +241,9 @@ public class FunctionSignatureMarkupType extends FunctionEntryPointBasedAbstract
} }
Address entryAddress = function.getEntryPoint(); Address entryAddress = function.getEntryPoint();
Stringable value = Stringable value =
isSource ? getSourceValue(association, address) : getCurrentDestinationValue( isSource ? getSourceValue(association, address)
association, address); : getCurrentDestinationValue(
association, address);
String displayString = (value != null) ? value.getDisplayString() : null; String displayString = (value != null) ? value.getDisplayString() : null;
return new FunctionReturnTypeFieldLocation(program, entryAddress, displayString); return new FunctionReturnTypeFieldLocation(program, entryAddress, displayString);
} }
@@ -284,6 +303,10 @@ public class FunctionSignatureMarkupType extends FunctionEntryPointBasedAbstract
options.setEnum(VTOptionDefines.FUNCTION_SIGNATURE, options.setEnum(VTOptionDefines.FUNCTION_SIGNATURE,
FunctionSignatureChoices.REPLACE); FunctionSignatureChoices.REPLACE);
break; break;
case REPLACE_FIRST_ONLY:
break;
default:
break;
} }
return options; return options;
} }
@@ -4,9 +4,9 @@
* 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.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -15,8 +15,6 @@
*/ */
package ghidra.feature.vt.api.markuptype; package ghidra.feature.vt.api.markuptype;
import static ghidra.feature.vt.gui.util.VTOptionDefines.*;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
@@ -24,8 +22,6 @@ import ghidra.feature.vt.api.impl.MarkupItemImpl;
import ghidra.feature.vt.api.main.*; import ghidra.feature.vt.api.main.*;
import ghidra.feature.vt.api.util.Stringable; import ghidra.feature.vt.api.util.Stringable;
import ghidra.feature.vt.api.util.VersionTrackingApplyException; import ghidra.feature.vt.api.util.VersionTrackingApplyException;
import ghidra.feature.vt.gui.plugin.VTController;
import ghidra.feature.vt.gui.util.VTMatchApplyChoices.*;
import ghidra.framework.options.Options; import ghidra.framework.options.Options;
import ghidra.framework.options.ToolOptions; import ghidra.framework.options.ToolOptions;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
@@ -36,33 +32,6 @@ import ghidra.util.task.TaskMonitor;
public abstract class VTMarkupType { public abstract class VTMarkupType {
static final ToolOptions VT_UNAPPLY_MARKUP_OPTIONS =
new ToolOptions(VTController.VERSION_TRACKING_OPTIONS_NAME);
static {
VT_UNAPPLY_MARKUP_OPTIONS.setEnum(FUNCTION_SIGNATURE, FunctionSignatureChoices.REPLACE);
VT_UNAPPLY_MARKUP_OPTIONS.setEnum(CALLING_CONVENTION, CallingConventionChoices.NAME_MATCH);
VT_UNAPPLY_MARKUP_OPTIONS.setEnum(INLINE, ReplaceChoices.REPLACE);
VT_UNAPPLY_MARKUP_OPTIONS.setEnum(NO_RETURN, ReplaceChoices.REPLACE);
VT_UNAPPLY_MARKUP_OPTIONS.setEnum(VAR_ARGS, ReplaceChoices.REPLACE);
VT_UNAPPLY_MARKUP_OPTIONS.setEnum(CALL_FIXUP, ReplaceChoices.REPLACE);
VT_UNAPPLY_MARKUP_OPTIONS.setEnum(FUNCTION_RETURN_TYPE, ParameterDataTypeChoices.REPLACE);
VT_UNAPPLY_MARKUP_OPTIONS.setEnum(PARAMETER_DATA_TYPES, ParameterDataTypeChoices.REPLACE);
VT_UNAPPLY_MARKUP_OPTIONS.setEnum(PARAMETER_NAMES, SourcePriorityChoices.REPLACE);
VT_UNAPPLY_MARKUP_OPTIONS.setEnum(PARAMETER_COMMENTS, CommentChoices.OVERWRITE_EXISTING);
VT_UNAPPLY_MARKUP_OPTIONS.setEnum(FUNCTION_NAME, FunctionNameChoices.REPLACE_ALWAYS);
VT_UNAPPLY_MARKUP_OPTIONS.setEnum(LABELS, LabelChoices.REPLACE_ALL);
VT_UNAPPLY_MARKUP_OPTIONS.setEnum(PLATE_COMMENT, CommentChoices.OVERWRITE_EXISTING);
VT_UNAPPLY_MARKUP_OPTIONS.setEnum(PRE_COMMENT, CommentChoices.OVERWRITE_EXISTING);
VT_UNAPPLY_MARKUP_OPTIONS.setEnum(END_OF_LINE_COMMENT, CommentChoices.OVERWRITE_EXISTING);
VT_UNAPPLY_MARKUP_OPTIONS.setEnum(REPEATABLE_COMMENT, CommentChoices.OVERWRITE_EXISTING);
VT_UNAPPLY_MARKUP_OPTIONS.setEnum(POST_COMMENT, CommentChoices.OVERWRITE_EXISTING);
VT_UNAPPLY_MARKUP_OPTIONS.setEnum(DATA_MATCH_DATA_TYPE,
ReplaceDataChoices.REPLACE_ALL_DATA);
}
private final String name; private final String name;
public VTMarkupType(String name) { public VTMarkupType(String name) {
@@ -4,9 +4,9 @@
* 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.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -17,6 +17,7 @@ package ghidra.feature.vt.api.stringable;
import java.util.*; import java.util.*;
import generic.json.Json;
import ghidra.app.cmd.label.SetLabelPrimaryCmd; import ghidra.app.cmd.label.SetLabelPrimaryCmd;
import ghidra.app.util.NamespaceUtils; import ghidra.app.util.NamespaceUtils;
import ghidra.feature.vt.api.util.Stringable; import ghidra.feature.vt.api.util.Stringable;
@@ -45,8 +46,10 @@ public class FunctionNameStringable extends Stringable {
if (symbol == null) { if (symbol == null) {
return; return;
} }
this.symbolName = symbol.getName(); this.symbolName = symbol.getName();
this.sourceType = symbol.getSource(); this.sourceType = symbol.getSource();
Namespace namespace = symbol.getParentNamespace(); Namespace namespace = symbol.getParentNamespace();
while (namespace != null) { while (namespace != null) {
if (namespace instanceof GlobalNamespace) { if (namespace instanceof GlobalNamespace) {
@@ -84,11 +87,11 @@ public class FunctionNameStringable extends Stringable {
sourceType = SourceType.valueOf(sourceName); sourceType = SourceType.valueOf(sourceName);
while (tok.hasMoreTokens()) { while (tok.hasMoreTokens()) {
getNamespaceInfo(tok); addNamespaceInfo(tok);
} }
} }
private void getNamespaceInfo(StringTokenizer tok) { private void addNamespaceInfo(StringTokenizer tok) {
String name = tok.nextToken(); String name = tok.nextToken();
int id = Integer.parseInt(tok.nextToken()); int id = Integer.parseInt(tok.nextToken());
SymbolType type = SymbolType.getSymbolType(id); SymbolType type = SymbolType.getSymbolType(id);
@@ -97,64 +100,187 @@ public class FunctionNameStringable extends Stringable {
namespaceInfos.add(new NamespaceInfo(name, type, nameSpaceSourceType)); namespaceInfos.add(new NamespaceInfo(name, type, nameSpaceSourceType));
} }
public void applyFunctionName(Program program, Function function) // Note: this is only meant to be called on the 'destination stringable'
public void unapplyFunctionNameAndNamespace(Function targetFunction)
throws DuplicateNameException, InvalidInputException, CircularDependencyException { throws DuplicateNameException, InvalidInputException, CircularDependencyException {
Address entryPoint = function.getEntryPoint(); Namespace ns = createAllNamespacesAndClasses(targetFunction, namespaceInfos);
doApplyFunctionName(targetFunction, ns);
}
// Note: this is only meant to be called on the 'source stringable'
public void applyFunctionNameAndNamespace(Function targetFunction)
throws DuplicateNameException, InvalidInputException, CircularDependencyException {
// use this stringable's namespace info to create the equivalent namespace in the target
Namespace ns = createAllNamespacesAndClasses(targetFunction, namespaceInfos);
if (ns instanceof GlobalNamespace) {
Namespace targetNs = targetFunction.getParentNamespace();
if (!(targetNs instanceof GlobalNamespace)) {
// Assume for now that any non-global namespace in the target is preferred by the
// user to the default global namespace. We can always add another option for this
// behavior in the future. If we change this code, then update the help.
ns = targetNs;
}
}
doApplyFunctionName(targetFunction, ns);
}
// Note: this is only meant to be called on the 'source stringable'
public void applyFunctionName(Function targetFunction)
throws DuplicateNameException, InvalidInputException, CircularDependencyException {
// apply only the name; keep the existing namespace
Symbol s = targetFunction.getSymbol();
Namespace ns = s.getParentNamespace();
doApplyFunctionName(targetFunction, ns);
}
private void doApplyFunctionName(Function targetFunction, Namespace namespace)
throws DuplicateNameException, InvalidInputException, CircularDependencyException {
Symbol s = targetFunction.getSymbol();
Program targetProgram = targetFunction.getProgram();
SymbolTable symbolTable = targetProgram.getSymbolTable();
Address entryPoint = targetFunction.getEntryPoint();
if (entryPoint.isMemoryAddress() && sourceType == SourceType.DEFAULT) { if (entryPoint.isMemoryAddress() && sourceType == SourceType.DEFAULT) {
// Apply a default name by removing the current one. // Apply a default name by removing the current one.
program.getSymbolTable().removeSymbolSpecial(function.getSymbol()); symbolTable.removeSymbolSpecial(targetFunction.getSymbol());
s.setNamespace(namespace);
return; return;
} }
SymbolTable symbolTable = program.getSymbolTable();
Namespace namespace = program.getGlobalNamespace();
for (NamespaceInfo info : namespaceInfos) {
Namespace ns = symbolTable.getNamespace(info.name, namespace);
if (ns != null) {
if (function.isExternal() != ns.isExternal()) {
throw new DuplicateNameException("Conflicting namespace: " + info.name);
}
if (info.symbolType == SymbolType.CLASS &&
ns.getSymbol().getSymbolType() == SymbolType.NAMESPACE) {
// Promote existing namespace to class
ns = NamespaceUtils.convertNamespaceToClass(ns);
}
namespace = ns;
}
else {
namespace = createNamespace(program, info, namespace);
}
}
Symbol s = function.getSymbol();
s.setNameAndNamespace(symbolName, namespace, sourceType); s.setNameAndNamespace(symbolName, namespace, sourceType);
} }
public void addFunctionName(Program program, Function function, boolean isPrimary) private Namespace createAllNamespacesAndClasses(Function destFunction,
List<NamespaceInfo> infos) throws DuplicateNameException, InvalidInputException {
Program destProgram = destFunction.getProgram();
SymbolTable symbolTable = destProgram.getSymbolTable();
Namespace namespace = destProgram.getGlobalNamespace();
for (NamespaceInfo info : infos) {
Namespace ns = symbolTable.getNamespace(info.name, namespace);
if (ns == null) {
namespace = createNamespace(destProgram, info, namespace);
continue;
}
if (destFunction.isExternal() != ns.isExternal()) {
throw new DuplicateNameException("Conflicting namespace: " + info.name);
}
if (info.symbolType == SymbolType.CLASS &&
ns.getSymbol().getSymbolType() == SymbolType.NAMESPACE) {
// Promote existing namespace to class
ns = NamespaceUtils.convertNamespaceToClass(ns);
}
namespace = ns;
}
return namespace;
}
public void addFunctionNameAndNamespace(Function sourceFunction,
Function destFunction, boolean isPrimary)
throws DuplicateNameException, InvalidInputException, CircularDependencyException { throws DuplicateNameException, InvalidInputException, CircularDependencyException {
List<NamespaceInfo> infos = new ArrayList<>();
Namespace namespace = sourceFunction.getParentNamespace();
while (namespace != null) {
if (namespace instanceof GlobalNamespace) {
break;
}
infos.add(new NamespaceInfo(namespace));
namespace = namespace.getParentNamespace();
}
Collections.reverse(infos);
doAddFunctionName(destFunction, infos, isPrimary);
}
public void addFunctionName(Function destFunction, boolean isPrimary)
throws DuplicateNameException, InvalidInputException, CircularDependencyException {
doAddFunctionName(destFunction, List.of(), isPrimary);
}
private void doAddFunctionName(Function destFunction, List<NamespaceInfo> namespaces,
boolean isPrimary)
throws DuplicateNameException, InvalidInputException, CircularDependencyException {
Program destProgram = destFunction.getProgram();
SymbolTable symbolTable = destProgram.getSymbolTable();
Namespace namespace = createAllNamespaces(destProgram, namespaces);
Symbol symbol = destFunction.getSymbol();
if (symbol.getSource() == SourceType.DEFAULT) {
// Special case: when the destination is default, replace the default symbol instead of
// adding a new symbol.
applyFunctionName(destFunction);
return;
}
Address entryPoint = destFunction.getEntryPoint();
Symbol addedSymbol = symbolTable.createLabel(entryPoint, symbolName, namespace, sourceType);
if (!isPrimary || addedSymbol == null) {
return;
}
Address address = addedSymbol.getAddress();
String name = addedSymbol.getName();
Namespace ns = addedSymbol.getParentNamespace();
SetLabelPrimaryCmd cmd = new SetLabelPrimaryCmd(address, name, ns);
cmd.applyTo(destProgram);
}
public String getSymbolNamespace() {
if (namespaceInfos.isEmpty()) {
return null;
}
StringBuilder buffy = new StringBuilder();
for (NamespaceInfo info : namespaceInfos) {
buffy.append(info.name).append(Namespace.DELIMITER);
}
int end = buffy.length();
int n = Namespace.DELIMITER.length();
buffy.delete(end - n, end);
return buffy.toString();
}
public String getSymbolName() {
return getSymbolName(false);
}
public String getSymbolName(boolean includeNamespace) {
if (!includeNamespace) {
return symbolName;
}
StringBuilder buffy = new StringBuilder();
for (NamespaceInfo info : namespaceInfos) {
buffy.append(info.name).append(Namespace.DELIMITER);
}
buffy.append(symbolName);
return buffy.toString();
}
public SourceType getSymbolSourceType() {
return sourceType;
}
private Namespace createAllNamespaces(Program program, List<NamespaceInfo> namespaces)
throws DuplicateNameException, InvalidInputException {
SymbolTable symbolTable = program.getSymbolTable(); SymbolTable symbolTable = program.getSymbolTable();
Namespace namespace = program.getGlobalNamespace(); Namespace namespace = program.getGlobalNamespace();
for (NamespaceInfo info : namespaceInfos) { for (NamespaceInfo info : namespaces) {
Namespace ns = symbolTable.getNamespace(info.name, namespace); Namespace nextNs = symbolTable.getNamespace(info.name, namespace);
namespace = ns != null ? ns : createNamespace(program, info, namespace); namespace = nextNs != null ? nextNs : createNamespace(program, info, namespace);
}
Symbol functionSymbol = function.getSymbol();
if (functionSymbol.getSource() == SourceType.DEFAULT) {
function.getSymbol().setNameAndNamespace(symbolName, namespace, sourceType);
}
else {
// Add a label.
Symbol addedSymbol = symbolTable.createLabel(function.getEntryPoint(), symbolName,
namespace, sourceType);
if (isPrimary && addedSymbol != null) {
SetLabelPrimaryCmd setLabelPrimaryCmd =
new SetLabelPrimaryCmd(addedSymbol.getAddress(), addedSymbol.getName(),
addedSymbol.getParentNamespace());
setLabelPrimaryCmd.applyTo(program);
}
} }
return namespace;
} }
private Namespace createNamespace(Program program, NamespaceInfo info, Namespace namespace) private Namespace createNamespace(Program program, NamespaceInfo info, Namespace namespace)
@@ -209,33 +335,52 @@ public class FunctionNameStringable extends Stringable {
return true; return true;
} }
//================================================================================================== //==================================================================================================
// Inner Classes // Inner Classes
//================================================================================================== //==================================================================================================
private static class NamespaceInfo { private static class NamespaceInfo {
String name;
SymbolType symbolType;
SourceType sourceType;
public NamespaceInfo(Namespace namespace) { private String name;
private SymbolType symbolType;
private SourceType sourceType;
NamespaceInfo(Namespace namespace) {
this.name = namespace.getName(); this.name = namespace.getName();
this.symbolType = namespace.getSymbol().getSymbolType(); this.symbolType = namespace.getSymbol().getSymbolType();
this.sourceType = namespace.getSymbol().getSource(); this.sourceType = namespace.getSymbol().getSource();
} }
public NamespaceInfo(String name, SymbolType type, SourceType sourceType) { NamespaceInfo(String name, SymbolType type, SourceType sourceType) {
this.name = name; this.name = name;
this.symbolType = type; this.symbolType = type;
this.sourceType = sourceType; this.sourceType = sourceType;
} }
} @Override
public String toString() {
return Json.toString(this);
}
public String getSymbolName() { @Override
return symbolName; public int hashCode() {
} return Objects.hash(name, sourceType, symbolType);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
NamespaceInfo other = (NamespaceInfo) obj;
return Objects.equals(name, other.name) && sourceType == other.sourceType &&
Objects.equals(symbolType, other.symbolType);
}
public SourceType getSymbolSourceType() {
return sourceType;
} }
} }
@@ -19,7 +19,6 @@ import static ghidra.feature.vt.gui.util.VTOptionDefines.*;
import java.awt.BorderLayout; import java.awt.BorderLayout;
import java.awt.Component; import java.awt.Component;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
@@ -59,6 +58,8 @@ public class ApplyMarkupPropertyEditor implements OptionsEditor {
private static final String FUNCTION_SIGNATURE_TOOLTIP = private static final String FUNCTION_SIGNATURE_TOOLTIP =
"<html>The apply action for the <b>function signature</b> " + "<html>The apply action for the <b>function signature</b> " +
"when performing bulk apply operations</html>"; "when performing bulk apply operations</html>";
private static final String USE_NAMESPACE_TOOLTIP =
"<html>When true function namespaces will be applied when names are applied.";
private static final String PLATE_COMMENT_TOOLTIP = private static final String PLATE_COMMENT_TOOLTIP =
"<html>The apply action for <b>plate comments</b> when performing bulk apply operations</html>"; "<html>The apply action for <b>plate comments</b> when performing bulk apply operations</html>";
private static final String PRE_COMMENT_TOOLTIP = private static final String PRE_COMMENT_TOOLTIP =
@@ -119,6 +120,7 @@ public class ApplyMarkupPropertyEditor implements OptionsEditor {
private JLabel dataMatchDataTypeLabel; private JLabel dataMatchDataTypeLabel;
private JLabel functionNameLabel; private JLabel functionNameLabel;
private JLabel functionSignatureLabel; private JLabel functionSignatureLabel;
private JLabel useFunctionNamespaceLabel;
private JLabel returnTypeLabel; private JLabel returnTypeLabel;
private JLabel inlineLabel; private JLabel inlineLabel;
private JLabel noReturnLabel; private JLabel noReturnLabel;
@@ -138,6 +140,7 @@ public class ApplyMarkupPropertyEditor implements OptionsEditor {
private JComboBox<Enum<?>> dataMatchDataTypeComboBox; private JComboBox<Enum<?>> dataMatchDataTypeComboBox;
private JComboBox<Enum<?>> functionNameComboBox; private JComboBox<Enum<?>> functionNameComboBox;
private JComboBox<Enum<?>> functionSignatureComboBox; private JComboBox<Enum<?>> functionSignatureComboBox;
private JCheckBox useFunctionNamespaceCheckBox;
private JComboBox<Enum<?>> returnTypeComboBox; private JComboBox<Enum<?>> returnTypeComboBox;
private JComboBox<Enum<?>> callingConventionComboBox; private JComboBox<Enum<?>> callingConventionComboBox;
private JComboBox<Enum<?>> inlineComboBox; private JComboBox<Enum<?>> inlineComboBox;
@@ -160,8 +163,6 @@ public class ApplyMarkupPropertyEditor implements OptionsEditor {
private JCheckBox ignoreExcludedCheckBox; private JCheckBox ignoreExcludedCheckBox;
private JCheckBox ignoreIncompleteCheckBox; private JCheckBox ignoreIncompleteCheckBox;
private ActionListener defaultActionListener;
private ToolOptions originalOptions; private ToolOptions originalOptions;
private PropertyChangeListener listener; private PropertyChangeListener listener;
private boolean unappliedChanges = false; private boolean unappliedChanges = false;
@@ -206,7 +207,6 @@ public class ApplyMarkupPropertyEditor implements OptionsEditor {
panel.add(createSeparator()); panel.add(createSeparator());
panel.add(createIgnoreCheckBoxPanel()); panel.add(createIgnoreCheckBoxPanel());
// TODO More needs to be done with the layout here.
panel.setBorder(BorderFactory.createTitledBorder("Apply Markup Options")); panel.setBorder(BorderFactory.createTitledBorder("Apply Markup Options"));
JScrollPane scrollPane = new JScrollPane(panel); JScrollPane scrollPane = new JScrollPane(panel);
@@ -223,7 +223,6 @@ public class ApplyMarkupPropertyEditor implements OptionsEditor {
private Component createIgnoreCheckBoxPanel() { private Component createIgnoreCheckBoxPanel() {
createIgnoreCheckBoxes(); createIgnoreCheckBoxes();
setupIgnoreMarkupItemsListeners();
JPanel panel = new JPanel(); JPanel panel = new JPanel();
panel.add(ignoreExcludedCheckBox); panel.add(ignoreExcludedCheckBox);
@@ -239,16 +238,10 @@ public class ApplyMarkupPropertyEditor implements OptionsEditor {
ignoreExcludedCheckBox.setToolTipText(IGNORE_EXCLUDED_TOOLTIP); ignoreExcludedCheckBox.setToolTipText(IGNORE_EXCLUDED_TOOLTIP);
} }
private void setupIgnoreMarkupItemsListeners() {
ignoreExcludedCheckBox.addActionListener(defaultActionListener);
ignoreIncompleteCheckBox.addActionListener(defaultActionListener);
}
private JPanel createFunctionSignatureSubPanel() { private JPanel createFunctionSignatureSubPanel() {
createFunctionSignatureDetailLabels(); createFunctionSignatureDetailLabels();
createFunctionSignatureDetailChoices(); createFunctionSignatureDetailChoices();
setupFunctionSignatureDetailChoiceListeners();
JPanel outerPanel = new JPanel(); JPanel outerPanel = new JPanel();
outerPanel.setBorder(BorderFactory.createEmptyBorder(0, 3, 0, 0)); outerPanel.setBorder(BorderFactory.createEmptyBorder(0, 3, 0, 0));
@@ -325,19 +318,9 @@ public class ApplyMarkupPropertyEditor implements OptionsEditor {
varArgsComboBox.setToolTipText(VAR_ARGS_TOOLTIP); varArgsComboBox.setToolTipText(VAR_ARGS_TOOLTIP);
} }
private void setupFunctionSignatureDetailChoiceListeners() {
returnTypeComboBox.addActionListener(defaultActionListener);
inlineComboBox.addActionListener(defaultActionListener);
noReturnComboBox.addActionListener(defaultActionListener);
callingConventionComboBox.addActionListener(defaultActionListener);
callFixupComboBox.addActionListener(defaultActionListener);
varArgsComboBox.addActionListener(defaultActionListener);
}
private JPanel createParametersSubPanel() { private JPanel createParametersSubPanel() {
createParameterLabels(); createParameterLabels();
createParameterChoices(); createParameterChoices();
setupParameterChoiceListeners();
JPanel outerPanel = new JPanel(); JPanel outerPanel = new JPanel();
outerPanel.setBorder(BorderFactory.createEmptyBorder(0, 3, 0, 0)); outerPanel.setBorder(BorderFactory.createEmptyBorder(0, 3, 0, 0));
@@ -402,15 +385,6 @@ public class ApplyMarkupPropertyEditor implements OptionsEditor {
PARAMETER_NAMES_REPLACE_IF_SAME_PRIORITY_TOOLTIP); PARAMETER_NAMES_REPLACE_IF_SAME_PRIORITY_TOOLTIP);
} }
private void setupParameterChoiceListeners() {
parameterDataTypesComboBox.addActionListener(defaultActionListener);
parameterNamesComboBox.addActionListener(defaultActionListener);
userHighestPriorityRB.addActionListener(defaultActionListener);
importHighestPriorityRB.addActionListener(defaultActionListener);
replaceIfSameSourceCheckBox.addActionListener(defaultActionListener);
parameterCommentsComboBox.addActionListener(defaultActionListener);
}
private JPanel createPrioritySubPanel() { private JPanel createPrioritySubPanel() {
JPanel outerPanel = new JPanel(); JPanel outerPanel = new JPanel();
JPanel panel = new JPanel(); JPanel panel = new JPanel();
@@ -436,7 +410,6 @@ public class ApplyMarkupPropertyEditor implements OptionsEditor {
createNonCommentMarkupLabels(); createNonCommentMarkupLabels();
createNonCommentMarkupChoices(); createNonCommentMarkupChoices();
setupNonCommentMarkupChoiceListeners();
JPanel outerPanel = new JPanel(); JPanel outerPanel = new JPanel();
outerPanel.setBorder(BorderFactory.createEmptyBorder(0, 3, 0, 0)); outerPanel.setBorder(BorderFactory.createEmptyBorder(0, 3, 0, 0));
@@ -452,6 +425,9 @@ public class ApplyMarkupPropertyEditor implements OptionsEditor {
panel.add(functionSignatureLabel); panel.add(functionSignatureLabel);
panel.add(functionSignatureComboBox); panel.add(functionSignatureComboBox);
panel.add(useFunctionNamespaceLabel);
panel.add(useFunctionNamespaceCheckBox);
outerPanel.add(panel); outerPanel.add(panel);
return outerPanel; return outerPanel;
} }
@@ -468,6 +444,9 @@ public class ApplyMarkupPropertyEditor implements OptionsEditor {
functionSignatureLabel = new GDLabel("Function Signature", SwingConstants.RIGHT); functionSignatureLabel = new GDLabel("Function Signature", SwingConstants.RIGHT);
functionSignatureLabel.setToolTipText(FUNCTION_SIGNATURE_TOOLTIP); functionSignatureLabel.setToolTipText(FUNCTION_SIGNATURE_TOOLTIP);
useFunctionNamespaceLabel = new GDLabel("Replace Namespace", SwingConstants.RIGHT);
useFunctionNamespaceLabel.setToolTipText(USE_NAMESPACE_TOOLTIP);
} }
private void createNonCommentMarkupChoices() { private void createNonCommentMarkupChoices() {
@@ -485,19 +464,13 @@ public class ApplyMarkupPropertyEditor implements OptionsEditor {
functionSignatureComboBox = createComboBox(VTOptionDefines.FUNCTION_SIGNATURE, functionSignatureComboBox = createComboBox(VTOptionDefines.FUNCTION_SIGNATURE,
DEFAULT_OPTION_FOR_FUNCTION_SIGNATURE); DEFAULT_OPTION_FOR_FUNCTION_SIGNATURE);
functionSignatureComboBox.setToolTipText(FUNCTION_SIGNATURE_TOOLTIP); functionSignatureComboBox.setToolTipText(FUNCTION_SIGNATURE_TOOLTIP);
}
private void setupNonCommentMarkupChoiceListeners() { useFunctionNamespaceCheckBox = createCheckBox("");
dataMatchDataTypeComboBox.addActionListener(defaultActionListener);
labelsComboBox.addActionListener(defaultActionListener);
functionNameComboBox.addActionListener(defaultActionListener);
functionSignatureComboBox.addActionListener(defaultActionListener);
} }
private JPanel createCommentsSubPanel() { private JPanel createCommentsSubPanel() {
createCommentLabels(); createCommentLabels();
createCommentChoices(); createCommentChoices();
setupCommentChoiceListeners();
JPanel outerPanel = new JPanel(); JPanel outerPanel = new JPanel();
outerPanel.setBorder(BorderFactory.createEmptyBorder(0, 3, 0, 0)); outerPanel.setBorder(BorderFactory.createEmptyBorder(0, 3, 0, 0));
@@ -562,14 +535,6 @@ public class ApplyMarkupPropertyEditor implements OptionsEditor {
postCommentsComboBox.setToolTipText(POST_COMMENT_TOOLTIP); postCommentsComboBox.setToolTipText(POST_COMMENT_TOOLTIP);
} }
private void setupCommentChoiceListeners() {
plateCommentsComboBox.addActionListener(defaultActionListener);
preCommentsComboBox.addActionListener(defaultActionListener);
endOfLineCommentsComboBox.addActionListener(defaultActionListener);
repeatableCommentsComboBox.addActionListener(defaultActionListener);
postCommentsComboBox.addActionListener(defaultActionListener);
}
private void updateOptions(ToolOptions options) { private void updateOptions(ToolOptions options) {
updateNonCommentMarkupOptions(options); updateNonCommentMarkupOptions(options);
updateCommentOptions(options); updateCommentOptions(options);
@@ -594,6 +559,9 @@ public class ApplyMarkupPropertyEditor implements OptionsEditor {
FunctionSignatureChoices functionSignatureChoice = FunctionSignatureChoices functionSignatureChoice =
(FunctionSignatureChoices) functionSignatureComboBox.getSelectedItem(); (FunctionSignatureChoices) functionSignatureComboBox.getSelectedItem();
options.setEnum(FUNCTION_SIGNATURE, functionSignatureChoice); options.setEnum(FUNCTION_SIGNATURE, functionSignatureChoice);
boolean useNamespaces = useFunctionNamespaceCheckBox.isSelected();
options.setBoolean(USE_NAMESPACE_FUNCTIONS, useNamespaces);
} }
private void updateCommentOptions(ToolOptions options) { private void updateCommentOptions(ToolOptions options) {
@@ -704,6 +672,12 @@ public class ApplyMarkupPropertyEditor implements OptionsEditor {
if (functionSignatureChoice != functionSignatureComboBox.getSelectedItem()) { if (functionSignatureChoice != functionSignatureComboBox.getSelectedItem()) {
functionSignatureComboBox.setSelectedItem(functionSignatureChoice); functionSignatureComboBox.setSelectedItem(functionSignatureChoice);
} }
boolean useNamespace =
options.getBoolean(USE_NAMESPACE_FUNCTIONS, DEFAULT_OPTION_FOR_NAMESPACE_FUNCTIONS);
if (useFunctionNamespaceCheckBox.isSelected() != useNamespace) {
useFunctionNamespaceCheckBox.setSelected(useNamespace);
}
} }
private void setEditorCommentValues(ToolOptions options) { private void setEditorCommentValues(ToolOptions options) {
@@ -852,7 +826,6 @@ public class ApplyMarkupPropertyEditor implements OptionsEditor {
// signals that there are unapplied changes // signals that there are unapplied changes
private void changesMade(boolean changes) { private void changesMade(boolean changes) {
// FIXME Is this ok? complete?
if (listener != null) { if (listener != null) {
listener.propertyChange(new PropertyChangeEvent(this, GhidraOptions.APPLY_ENABLED, listener.propertyChange(new PropertyChangeEvent(this, GhidraOptions.APPLY_ENABLED,
unappliedChanges, changes)); unappliedChanges, changes));
@@ -766,6 +766,9 @@ public class VTMatchTableProvider extends ComponentProviderAdapter
"Markup items that are incomplete (for example, no destination address is specified) " + "Markup items that are incomplete (for example, no destination address is specified) " +
"should become ignored by applying a match."); "should become ignored by applying a match.");
vtOptions.registerOption(USE_NAMESPACE_FUNCTIONS, DEFAULT_OPTION_FOR_NAMESPACE_FUNCTIONS,
null, "Apply the non-Global source namespace to the destination function.");
vtOptions.getOptions(APPLY_MARKUP_OPTIONS_NAME) vtOptions.getOptions(APPLY_MARKUP_OPTIONS_NAME)
.registerOptionsEditor(() -> new ApplyMarkupPropertyEditor(controller)); .registerOptionsEditor(() -> new ApplyMarkupPropertyEditor(controller));
vtOptions.getOptions(DISPLAY_APPLY_MARKUP_OPTIONS) vtOptions.getOptions(DISPLAY_APPLY_MARKUP_OPTIONS)
@@ -68,6 +68,8 @@ public class VTOptionDefines {
public static CommentChoices DEFAULT_OPTION_FOR_POST_COMMENTS = public static CommentChoices DEFAULT_OPTION_FOR_POST_COMMENTS =
CommentChoices.APPEND_TO_EXISTING; CommentChoices.APPEND_TO_EXISTING;
public static boolean DEFAULT_OPTION_FOR_NAMESPACE_FUNCTIONS = false;
public static final String FUNCTION_NAME = APPLY_MARKUP_OPTIONS_NAME + ".Function Name"; public static final String FUNCTION_NAME = APPLY_MARKUP_OPTIONS_NAME + ".Function Name";
public static final String FUNCTION_RETURN_TYPE = APPLY_MARKUP_OPTIONS_NAME + public static final String FUNCTION_RETURN_TYPE = APPLY_MARKUP_OPTIONS_NAME +
".Function Return Type"; ".Function Return Type";
@@ -105,9 +107,12 @@ public class VTOptionDefines {
public static final String IGNORE_EXCLUDED_MARKUP_ITEMS = APPLY_MARKUP_OPTIONS_NAME + public static final String IGNORE_EXCLUDED_MARKUP_ITEMS = APPLY_MARKUP_OPTIONS_NAME +
".Set Excluded Markup Items To Ignored"; ".Set Excluded Markup Items To Ignored";
public final static String DISPLAY_APPLY_MARKUP_OPTIONS = APPLY_MARKUP_OPTIONS_NAME + public static final String DISPLAY_APPLY_MARKUP_OPTIONS = APPLY_MARKUP_OPTIONS_NAME +
Options.DELIMITER + "Display Apply Markup Options"; Options.DELIMITER + "Display Apply Markup Options";
public static final String USE_NAMESPACE_FUNCTIONS =
APPLY_MARKUP_OPTIONS_NAME + ".Replace Namespace";
// Auto VT Options // Auto VT Options
public static final String AUTO_VT_OPTIONS_NAME = "Auto Version Tracking Options"; public static final String AUTO_VT_OPTIONS_NAME = "Auto Version Tracking Options";
@@ -4,9 +4,9 @@
* 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.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -35,6 +35,7 @@ import ghidra.program.database.ProgramDB;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSet; import ghidra.program.model.address.AddressSet;
import ghidra.test.*; import ghidra.test.*;
import ghidra.util.Msg;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
public abstract class AbstractVTMarkupItemTest extends AbstractGhidraHeadedIntegrationTest { public abstract class AbstractVTMarkupItemTest extends AbstractGhidraHeadedIntegrationTest {
@@ -55,6 +56,10 @@ public abstract class AbstractVTMarkupItemTest extends AbstractGhidraHeadedInteg
env = new TestEnv(); env = new TestEnv();
setErrorGUIEnabled(false); setErrorGUIEnabled(false);
resetPrograms();
}
protected void resetPrograms() throws Exception {
sourceBuilder = new ClassicSampleX86ProgramBuilder("notepadSrc", true, this); sourceBuilder = new ClassicSampleX86ProgramBuilder("notepadSrc", true, this);
sourceProgram = sourceBuilder.getProgram(); sourceProgram = sourceBuilder.getProgram();
@@ -112,6 +117,8 @@ public abstract class AbstractVTMarkupItemTest extends AbstractGhidraHeadedInteg
*/ */
protected abstract class TestDataProviderAndValidator { protected abstract class TestDataProviderAndValidator {
protected ToolOptions options;
protected abstract Address getSourceMatchAddress(); protected abstract Address getSourceMatchAddress();
protected abstract Address getDestinationMatchAddress(); protected abstract Address getDestinationMatchAddress();
@@ -134,6 +141,11 @@ public abstract class AbstractVTMarkupItemTest extends AbstractGhidraHeadedInteg
* @return the Apply Markup Options for the validator to use. * @return the Apply Markup Options for the validator to use.
*/ */
protected ToolOptions getOptions() { protected ToolOptions getOptions() {
if (options != null) {
return options;
}
ToolOptions applyOptions = new ToolOptions(VERSION_TRACKING_OPTIONS_NAME); ToolOptions applyOptions = new ToolOptions(VERSION_TRACKING_OPTIONS_NAME);
applyOptions.setEnum(FUNCTION_NAME, FunctionNameChoices.REPLACE_ALWAYS); applyOptions.setEnum(FUNCTION_NAME, FunctionNameChoices.REPLACE_ALWAYS);
@@ -165,10 +177,29 @@ public abstract class AbstractVTMarkupItemTest extends AbstractGhidraHeadedInteg
applyOptions.setEnum(DATA_MATCH_DATA_TYPE, applyOptions.setEnum(DATA_MATCH_DATA_TYPE,
ReplaceDataChoices.REPLACE_UNDEFINED_DATA_ONLY); ReplaceDataChoices.REPLACE_UNDEFINED_DATA_ONLY);
options = applyOptions;
return applyOptions; return applyOptions;
} }
} }
protected void doTestFindButCannotApplyMarkupItem_SameStatus(
TestDataProviderAndValidator validator) throws Exception {
VTSessionDB session = createNewSession();
VTMatch match = createMatchSetWithOneMatch(session, validator.getSourceMatchAddress(),
validator.getDestinationMatchAddress());
VTMarkupItem markupItem = validator.searchForMarkupItem(match);
List<VTMarkupItem> markupItems = new ArrayList<>();
Address destinationApplyAddress = validator.getDestinationApplyAddress();
markupItem.setDefaultDestinationAddress(destinationApplyAddress, TEST_ADDRESS_SOURCE);
markupItems.add(markupItem);
VTMarkupItemStatus status = markupItem.getStatus();
assertEquals(VTMarkupItemStatus.SAME, status);
}
protected void doTestFindAndApplyMarkupItem(TestDataProviderAndValidator validator) protected void doTestFindAndApplyMarkupItem(TestDataProviderAndValidator validator)
throws Exception { throws Exception {
@@ -319,7 +350,10 @@ public abstract class AbstractVTMarkupItemTest extends AbstractGhidraHeadedInteg
// verify that calling apply still leaves us in an unapplied state // verify that calling apply still leaves us in an unapplied state
// //
task = new ApplyMarkupItemTask(session, markupItems, validator.getOptions()); task = new ApplyMarkupItemTask(session, markupItems, validator.getOptions());
setErrorsExpected(true);
runTask(session, task); runTask(session, task);
setErrorsExpected(false);
assertEquals("The markup item status was not correctly set", assertEquals("The markup item status was not correctly set",
VTMarkupItemStatus.FAILED_APPLY, markupItem.getStatus()); VTMarkupItemStatus.FAILED_APPLY, markupItem.getStatus());
@@ -354,9 +388,8 @@ public abstract class AbstractVTMarkupItemTest extends AbstractGhidraHeadedInteg
protected static VTMatch createMatchSetWithOneMatch(VTSessionDB db, Address sourceAddress, protected static VTMatch createMatchSetWithOneMatch(VTSessionDB db, Address sourceAddress,
Address destinationAddress) throws Exception { Address destinationAddress) throws Exception {
int testTransactionID = 0;
try { return tx(db, () -> {
testTransactionID = db.startTransaction("Test Match Set Setup");
VTMatchInfo matchInfo = createRandomMatch(sourceAddress, destinationAddress, db); VTMatchInfo matchInfo = createRandomMatch(sourceAddress, destinationAddress, db);
VTMatchSet matchSet = db.createMatchSet( VTMatchSet matchSet = db.createMatchSet(
createProgramCorrelator(db.getSourceProgram(), db.getDestinationProgram())); createProgramCorrelator(db.getSourceProgram(), db.getDestinationProgram()));
@@ -368,19 +401,22 @@ public abstract class AbstractVTMarkupItemTest extends AbstractGhidraHeadedInteg
association.getMarkupItems(TaskMonitor.DUMMY); association.getMarkupItems(TaskMonitor.DUMMY);
return addedMatch; return addedMatch;
} });
finally {
db.endTransaction(testTransactionID, true);
}
} }
private void runTask(VTSession session, VtTask task) { protected void runTask(VTSession session, VtTask task) {
int id = session.startTransaction("test"); int id = session.startTransaction("test");
try { try {
task.run(TaskMonitor.DUMMY); task.run(TaskMonitor.DUMMY);
} }
finally { finally {
session.endTransaction(id, true); session.endTransaction(id, true);
if (task.hasErrors()) {
String errorDetails = task.getErrorDetails();
Msg.error(this, "Error applying task: " + errorDetails);
}
} }
} }
@@ -96,6 +96,7 @@ public class VersionControlScreenShots extends GhidraScreenShotGenerator {
UndoActionDialog d = waitForDialogComponent(UndoActionDialog.class); UndoActionDialog d = waitForDialogComponent(UndoActionDialog.class);
captureDialog(d); captureDialog(d);
close(d); close(d);
} }