diff --git a/Ghidra/Features/Base/src/main/java/ghidra/test/AbstractGhidraHeadlessIntegrationTest.java b/Ghidra/Features/Base/src/main/java/ghidra/test/AbstractGhidraHeadlessIntegrationTest.java index 5479ba2287..25023aa8ce 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/test/AbstractGhidraHeadlessIntegrationTest.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/test/AbstractGhidraHeadlessIntegrationTest.java @@ -261,27 +261,27 @@ public abstract class AbstractGhidraHeadlessIntegrationTest extends AbstractDock * * @param the return type * @param the exception type - * @param p the program + * @param dobj the program or other domain object * @param s the code to execute * @return the supplier's return value * @see #modifyProgram(Program, ExceptionalCallback) * @see #modifyProgram(Program, ExceptionalFunction) */ - public static T tx(Program p, ExceptionalSupplier s) { - int txId = p.startTransaction("Test - Function in Transaction"); + public static T tx(DomainObject dobj, ExceptionalSupplier s) { + int txId = dobj.startTransaction("Test - Function in Transaction"); boolean commit = true; try { T t = s.get(); - p.flushEvents(); + dobj.flushEvents(); waitForSwing(); return t; } catch (Exception e) { commit = false; - failWithException("Exception modifying program '" + p.getName() + "'", e); + failWithException("Exception modifying program '" + dobj.getName() + "'", e); } finally { - p.endTransaction(txId, commit); + dobj.endTransaction(txId, commit); } return null; } diff --git a/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/VTOptions_ApplyMarkupDialog.png b/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/VTOptions_ApplyMarkupDialog.png index dd63936868..3c7649af45 100644 Binary files a/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/VTOptions_ApplyMarkupDialog.png and b/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/VTOptions_ApplyMarkupDialog.png differ diff --git a/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/providers/VT_Apply_Options.html b/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/providers/VT_Apply_Options.html index 05b3375e77..fa3160d0bd 100644 --- a/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/providers/VT_Apply_Options.html +++ b/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/providers/VT_Apply_Options.html @@ -164,13 +164,18 @@
  • Do Not Apply - Do not apply markup to the destination program
  • Add - Adds the function name from the source program to the one already - at the address in the destination program.
  • + 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.
  • Add As Primary - Adds the function name from the source program to the one already at the address in the destination program. Sets the primary label in the destination program to whatever label was the - primary one in the source program.
  • -
  • Replace Always - Always apply markup to from the source - program to the destination program.
  • + primary one in the source program. +
  • Replace Always - Always use the source symbol name to replace + 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.
  • Replace Default Only - Only apply markup from the source program to the destination program when the label in the destination program is a default label.
  • @@ -425,6 +430,40 @@ + + + Function Signature :
    + Specifies the default action to take when applying the function + signature of a match. + + + Replace When Same Parameter Count + + + + + + + Replace Namespace :
    + Specifies whether to apply the source function's namespace when applying + the function name. + +

    + 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. + +

    + + + False + + + + + Inline :
    Specifies the action to take for applying the inline flag when @@ -497,17 +536,7 @@ False - - - Function Signature :
    - Specifies the default action to take when applying the function - signature of a match. - - - Replace When Same Parameter Count - - - + Labels :
    Specifies the default action to take when applying the label of a match. diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/impl/MarkupItemImpl.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/impl/MarkupItemImpl.java index 90c536c950..8029a681c8 100644 --- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/impl/MarkupItemImpl.java +++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/impl/MarkupItemImpl.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -115,33 +115,37 @@ public class MarkupItemImpl implements VTMarkupItem { @Override public VTMarkupType getMarkupType() { - return markupItemStorage.getMarkupType(); + return markupType; } @Override public VTMarkupItemStatus getStatus() { validateDestinationCache(); VTMarkupItemStatus status = markupItemStorage.getStatus(); - if (status == UNAPPLIED) { - if (!gettingStatus) { - try { - gettingStatus = true; - boolean conflictsWithOtherMarkup = getMarkupType().conflictsWithOtherMarkup( - this, getAssociation().getMarkupItems(TaskMonitor.DUMMY)); - if (conflictsWithOtherMarkup) { - return CONFLICT; - } - } - catch (CancelledException e) { - // Shouldn't happen, but ignore if it does. - } - finally { - gettingStatus = false; + if (status != UNAPPLIED) { + return status; + } + + if (!gettingStatus) { + try { + gettingStatus = true; + VTAssociation association = getAssociation(); + Collection items = association.getMarkupItems(TaskMonitor.DUMMY); + boolean conflicts = markupType.conflictsWithOtherMarkup(this, items); + if (conflicts) { + return CONFLICT; } } - if (hasSameSourceDestinationValues()) { - return SAME; + catch (CancelledException e) { + // Shouldn't happen, but ignore if it does. } + finally { + gettingStatus = false; + } + } + + if (hasSameSourceDestinationValues()) { + return SAME; } return status; } @@ -196,15 +200,10 @@ public class MarkupItemImpl implements VTMarkupItem { markupItemStorage.setApplyFailed("Can't apply without a valid destination"); fireMarkupItemStatusChanged(oldStatus, FAILED_APPLY); 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()) { - // TODO Should this throw an Exception instead? -// VTMarkupType itemMarkupType = item.getMarkupType(); -// throw new VersionTrackingApplyException("Cannot apply " + -// itemMarkupType.getDisplayName() + " at " + item.getDestinationAddress().toString() + -// "."); return; } diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/markuptype/FunctionNameMarkupType.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/markuptype/FunctionNameMarkupType.java index 92758a27a9..e3a5d7a2a2 100644 --- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/markuptype/FunctionNameMarkupType.java +++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/markuptype/FunctionNameMarkupType.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -15,8 +15,8 @@ */ package ghidra.feature.vt.api.markuptype; -import static ghidra.feature.vt.gui.util.VTOptionDefines.DEFAULT_OPTION_FOR_FUNCTION_NAME; -import static ghidra.feature.vt.gui.util.VTOptionDefines.FUNCTION_NAME; +import static ghidra.feature.vt.gui.util.VTMatchApplyChoices.FunctionNameChoices.*; +import static ghidra.feature.vt.gui.util.VTOptionDefines.*; import java.util.ArrayList; import java.util.List; @@ -32,6 +32,7 @@ import ghidra.feature.vt.gui.util.VTMatchApplyChoices.FunctionNameChoices; import ghidra.framework.options.Options; import ghidra.framework.options.ToolOptions; import ghidra.program.model.address.Address; +import ghidra.program.model.address.GlobalNamespace; import ghidra.program.model.listing.*; import ghidra.program.model.symbol.*; import ghidra.program.util.*; @@ -46,30 +47,32 @@ public class FunctionNameMarkupType extends FunctionEntryPointBasedAbstractMarku public static final VTMarkupType INSTANCE = new FunctionNameMarkupType(); -// private static final String FUNCTION_NAME = null; - @Override public List createMarkupItems(VTAssociation association) { List list = new ArrayList<>(); - Function sourceFunction = getSourceFunction(association); + Function srcFunction = getSourceFunction(association); Function destinationFunction = getDestinationFunction(association); - if (sourceFunction == null || destinationFunction == null) { + if (srcFunction == null || destinationFunction == null) { return list; } - Address sourceAddress = sourceFunction.getEntryPoint(); - String sourceFunctionName = sourceFunction.getName(); + Address srcAddress = srcFunction.getEntryPoint(); + String srcFunctionName = srcFunction.getName(); - String defaultFunctionName = SymbolUtilities.getDefaultFunctionName(sourceAddress); - if (sourceFunctionName.equals(defaultFunctionName)) { - return list; + String defaultFunctionName = SymbolUtilities.getDefaultFunctionName(srcAddress); + if (srcFunctionName.equals(defaultFunctionName)) { + Namespace namespace = srcFunction.getParentNamespace(); + if (namespace instanceof GlobalNamespace) { + // default function name in the default namespace--nothing to be applied + return list; + } } - MarkupItemImpl markupItemImpl = new MarkupItemImpl(association, this, sourceAddress); // Now we have a function name markup item without a destination. // Force the destination address to be the entry point of the destination function. + MarkupItemImpl markupItemImpl = new MarkupItemImpl(association, this, srcAddress); markupItemImpl.setDefaultDestinationAddress(association.getDestinationAddress(), VTMarkupItem.FUNCTION_ADDRESS_SOURCE); list.add(markupItemImpl); @@ -110,55 +113,124 @@ public class FunctionNameMarkupType extends FunctionEntryPointBasedAbstractMarku "Attempted to unapply a non-applied markup item"); } - FunctionNameStringable sourceSymbolStringable = - (FunctionNameStringable) markupItem.getSourceValue(); - Address destinationAddress = markupItem.getDestinationAddress(); - FunctionNameStringable destinationSymbolStringable = + FunctionNameStringable srcStringable = (FunctionNameStringable) markupItem.getSourceValue(); + Address destAddress = markupItem.getDestinationAddress(); + FunctionNameStringable destStringable = (FunctionNameStringable) markupItem.getOriginalDestinationValue(); - String destinationName = destinationSymbolStringable.getSymbolName(); - Program destinationProgram = getDestinationProgram(markupItem.getAssociation()); - FunctionManager functionManager = destinationProgram.getFunctionManager(); - Function destinationFunction = functionManager.getFunctionAt(destinationAddress); - if (destinationFunction == null) { + Program destProgram = getDestinationProgram(markupItem.getAssociation()); + FunctionManager fm = destProgram.getFunctionManager(); + Function destFunction = fm.getFunctionAt(destAddress); + if (destFunction == null) { return; } - Namespace destinationNamespace = destinationFunction.getParentNamespace(); - // If the name exists as a label then there must have been an AddAsPrimary. - SymbolTable symbolTable = destinationProgram.getSymbolTable(); - Symbol desiredSymbol = - symbolTable.getSymbol(destinationName, destinationAddress, destinationNamespace); - if (desiredSymbol != null && desiredSymbol.getSymbolType() == SymbolType.LABEL) { - SetLabelPrimaryCmd setLabelPrimaryCmd = - new SetLabelPrimaryCmd(desiredSymbol.getAddress(), desiredSymbol.getName(), - desiredSymbol.getParentNamespace()); - setLabelPrimaryCmd.applyTo(destinationProgram); + + // If a symbol with the original name exist as a label, then there must have been an + // AddAsPrimary operation that made the source symbol primary, replacing the original + // destination symbol as primary. + restorePrimarySymbol(destAddress, destStringable, destProgram); + + unapplyFunctionName(srcStringable, destAddress, destStringable, destFunction); + } + + private void restorePrimarySymbol(Address destAddress, FunctionNameStringable destStringable, + Program destProgram) { + + SymbolTable symbolTable = destProgram.getSymbolTable(); + String originalSymbolName = destStringable.getSymbolName(); + String destinationNamespace = destStringable.getSymbolNamespace(); + Symbol originalSymbol = + getSymbol(symbolTable, originalSymbolName, destAddress, destinationNamespace); + if (originalSymbol == null) { + return; // deleted by the user } - String currentName = destinationFunction.getName(); - if (currentName.equals(destinationName)) { - // Don't need to change the function name, but may need to remove the source name as a label. - String sourceName = sourceSymbolStringable.getSymbolName(); - Symbol sourceAsLabel = - symbolTable.getSymbol(sourceName, destinationAddress, destinationNamespace); - if (sourceAsLabel != null) { - sourceAsLabel.delete(); + if (originalSymbol.getSymbolType() != SymbolType.LABEL) { + return; + } + + if (originalSymbol.isPrimary()) { + return; // nothing to do + } + + Address address = originalSymbol.getAddress(); + String name = originalSymbol.getName(); + Namespace ns = originalSymbol.getParentNamespace(); + SetLabelPrimaryCmd setLabelPrimaryCmd = new SetLabelPrimaryCmd(address, name, ns); + setLabelPrimaryCmd.applyTo(destProgram); + } + + private Symbol getSymbol(SymbolTable symbolTable, String name, Address address, + String namespacePath) { + + String expectedNamespacePath = namespacePath; + if (expectedNamespacePath == null) { + expectedNamespacePath = "Global"; + } + Symbol[] symbols = symbolTable.getSymbols(address); + for (Symbol s : symbols) { + String symbolName = s.getName(); + if (!symbolName.equals(name)) { + continue; + } + + Namespace namespace = s.getParentNamespace(); + String symbolNamespacePath = namespace.getName(true); + if (symbolNamespacePath.equals(expectedNamespacePath)) { + return s; + } + } + return null; + } + + private void unapplyFunctionName(FunctionNameStringable sourceStringable, Address destAddress, + FunctionNameStringable destStringable, Function destFunction) + throws VersionTrackingApplyException { + + String originalFunctionName = destStringable.getSymbolName(true); + String currentFunctionName = destFunction.getName(true); + if (currentFunctionName.equals(originalFunctionName)) { + + // The full function name and namespace are unchanged. Assume only a label was added. + Namespace destNamespace = destFunction.getParentNamespace(); + Program destProgram = destFunction.getProgram(); + SymbolTable symbolTable = destProgram.getSymbolTable(); + String srcName = sourceStringable.getSymbolName(); + Symbol srcAsLabel = symbolTable.getSymbol(srcName, destAddress, destNamespace); + if (srcAsLabel != null) { + srcAsLabel.delete(); + return; + } + + // try the label with the source namespace + String srcNsString = sourceStringable.getSymbolNamespace(); + srcAsLabel = getSymbol(symbolTable, srcName, destAddress, srcNsString); + if (srcAsLabel != null) { + srcAsLabel.delete(); + return; + } + + // try the label in the global namespace + srcAsLabel = getSymbol(symbolTable, srcName, destAddress, null); + if (srcAsLabel != null) { + srcAsLabel.delete(); } return; } + try { - destinationSymbolStringable.applyFunctionName(destinationProgram, destinationFunction); + destStringable.unapplyFunctionNameAndNamespace(destFunction); } catch (DuplicateNameException e) { throw new VersionTrackingApplyException( - "Unable to restore function name: " + destinationName, e); + "Unable to restore function name: " + originalFunctionName, e); } catch (InvalidInputException e) { throw new VersionTrackingApplyException( - "Unable to restore function name: " + destinationName, e); + "Unable to restore function name: " + originalFunctionName, e); } catch (CircularDependencyException e) { throw new VersionTrackingApplyException("Unable to restore function name: " + - destinationName + " due to circular dependancy on namespaces", e); + originalFunctionName + " due to circular dependancy on namespaces", e); } } @@ -166,67 +238,99 @@ public class FunctionNameMarkupType extends FunctionEntryPointBasedAbstractMarku public boolean applyMarkup(VTMarkupItem markupItem, ToolOptions markupOptions) throws VersionTrackingApplyException { - VTMatchApplyChoices.FunctionNameChoices functionNameChoice = + VTMatchApplyChoices.FunctionNameChoices nameChoice = markupOptions.getEnum(FUNCTION_NAME, DEFAULT_OPTION_FOR_FUNCTION_NAME); - if (functionNameChoice == FunctionNameChoices.EXCLUDE) { + if (nameChoice == EXCLUDE) { throw new IllegalArgumentException("Can't apply " + markupItem.getMarkupType().getDisplayName() + " since it is excluded."); } - Address destinationAddress = markupItem.getDestinationAddress(); - if (destinationAddress == null) { + Address destAddress = markupItem.getDestinationAddress(); + if (destAddress == null) { throw new VersionTrackingApplyException("The destination address cannot be null!"); } - if (destinationAddress == Address.NO_ADDRESS) { + if (destAddress == Address.NO_ADDRESS) { throw new VersionTrackingApplyException( "The destination address cannot be No Address!"); } - Program destinationProgram = getDestinationProgram(markupItem.getAssociation()); - FunctionManager functionManager = destinationProgram.getFunctionManager(); - Function destinationFunction = functionManager.getFunctionAt(destinationAddress); - if (destinationFunction == null) { + Program destProgram = getDestinationProgram(markupItem.getAssociation()); + FunctionManager fm = destProgram.getFunctionManager(); + Function destFunction = fm.getFunctionAt(destAddress); + if (destFunction == null) { throw new VersionTrackingApplyException( "Couldn't find destination function to apply a name."); } - Symbol destinationSymbol = destinationFunction.getSymbol(); - if (functionNameChoice == FunctionNameChoices.REPLACE_DEFAULT_ONLY && - destinationSymbol.getSource() != SourceType.DEFAULT) { + Symbol destSymbol = destFunction.getSymbol(); + SourceType destSource = destSymbol.getSource(); + if (nameChoice == REPLACE_DEFAULT_ONLY && destSource != SourceType.DEFAULT) { return false; // can't do it because we should only replace default names } - FunctionNameStringable symbolStringable = + FunctionNameStringable srcStringable = (FunctionNameStringable) markupItem.getSourceValue(); - if (symbolStringable == null) { + if (srcStringable == null) { // someone must have deleted the variable from the source throw new VersionTrackingApplyException("Cannot apply function name" + ". The function from the source program no longer exists. Markup Item: " + markupItem); } - if (symbolStringable.getSymbolSourceType() == SourceType.DEFAULT) { - return false; // Can't set a default source name on the destination. + Function srcFunction = getSourceFunction(markupItem.getAssociation()); + boolean replaceNamespace = markupOptions.getBoolean(USE_NAMESPACE_FUNCTIONS, false); + if (!hasAnythingToApply(srcStringable, srcFunction, replaceNamespace)) { + return false; } - String name = symbolStringable.getSymbolName(); + if (cannotAddDefaultSymbol(nameChoice, srcStringable)) { + return false; + } + applyFunctionName(nameChoice, srcFunction, destFunction, srcStringable, replaceNamespace); + return true; + } + + private boolean cannotAddDefaultSymbol(FunctionNameChoices nameChoice, + FunctionNameStringable sourceStringable) { + + if (nameChoice == ADD || nameChoice == ADD_AS_PRIMARY) { + SourceType srcSourceType = sourceStringable.getSymbolSourceType(); + if (srcSourceType == SourceType.DEFAULT) { + return true; // cannot add a default symbol + } + } + + return false; + } + + private boolean hasAnythingToApply(FunctionNameStringable srcStringable, Function srcFunction, + boolean replaceNamespace) { + + if (srcStringable.getSymbolSourceType() != SourceType.DEFAULT) { + return true; // non-default name to apply + } + + Namespace srcNamespace = srcFunction.getParentNamespace(); + if (srcNamespace instanceof GlobalNamespace) { + // default name and default namespace--nothing to apply + return false; + } + + // Default name; non-default namespace. We can apply the namespace if the option is on + return replaceNamespace; + + } + + private void applyFunctionName(FunctionNameChoices nameChoice, Function srcFunction, + Function destFunction, FunctionNameStringable srcStringable, boolean replaceNamespace) + throws VersionTrackingApplyException { + + String name = srcStringable.getSymbolName(); try { - boolean isPrimary = (functionNameChoice == FunctionNameChoices.ADD_AS_PRIMARY); - if (functionNameChoice == FunctionNameChoices.ADD || isPrimary) { - if (destinationFunction.isExternal()) { - throw new VersionTrackingApplyException("Can't add the function name \"" + - name + "\" to the external function \"" + destinationFunction.getName() + - "\". External function names can only be replaced."); - } - // Now check to see which should be primary. - symbolStringable.addFunctionName(destinationProgram, destinationFunction, - isPrimary); - } - else { - symbolStringable.applyFunctionName(destinationProgram, destinationFunction); - } + doApplyFunctionName(nameChoice, srcFunction, destFunction, srcStringable, + replaceNamespace); } catch (DuplicateNameException e) { throw new VersionTrackingApplyException( @@ -240,39 +344,73 @@ public class FunctionNameMarkupType extends FunctionEntryPointBasedAbstractMarku throw new VersionTrackingApplyException("Unable to apply function name: " + name + " due to circular dependancy on namespaces", e); } - return true; + } + + private void doApplyFunctionName(FunctionNameChoices nameChoice, Function srcFunction, + Function destFunction, FunctionNameStringable srcStringable, boolean replaceNamespace) + throws VersionTrackingApplyException, DuplicateNameException, InvalidInputException, + CircularDependencyException { + + if (nameChoice == REPLACE_ALWAYS || nameChoice == REPLACE_DEFAULT_ONLY) { + if (replaceNamespace) { + srcStringable.applyFunctionNameAndNamespace(destFunction); + } + else { + srcStringable.applyFunctionName(destFunction); + } + return; + } + + // An ADD or ADD_AS_PRIMARY + if (destFunction.isExternal()) { + String srcName = srcStringable.getSymbolName(); + String destName = destFunction.getName(); + String msg = "Can't add function name '%s' to external function '%s'. " + + "External function names can only be replaced."; + String formatted = msg.formatted(srcName, destName); + throw new VersionTrackingApplyException(formatted); + } + + boolean isPrimary = (nameChoice == ADD_AS_PRIMARY); + if (replaceNamespace) { + srcStringable.addFunctionNameAndNamespace(srcFunction, destFunction, isPrimary); + } + else { + srcStringable.addFunctionName(destFunction, isPrimary); + } } @Override - public ProgramLocation getDestinationLocation(VTAssociation association, - Address destinationAddress) { - Address defaultDestinationAddress = association.getDestinationAddress(); + public ProgramLocation getDestinationLocation(VTAssociation association, Address destAddress) { + + Address defaultDestAddress = association.getDestinationAddress(); // Ignore the destinationAddress that is handed in and instead use the association // destination address that should be the destination function's entry point. FunctionNameFieldLocation functionNameLocation = - getFunctionNameLocation(association, defaultDestinationAddress, false); + getFunctionNameLocation(association, defaultDestAddress, false); if (functionNameLocation != null) { return functionNameLocation; } // Otherwise, get the primary symbol location. LabelFieldLocation labelLocation = - getPrimaryLabelLocation(association, defaultDestinationAddress, false); + getPrimaryLabelLocation(association, defaultDestAddress, false); if (labelLocation != null) { return labelLocation; } // Otherwise, get the address location. Program program = getDestinationProgram(association); - return new AddressFieldLocation(program, defaultDestinationAddress); + return new AddressFieldLocation(program, defaultDestAddress); } @Override public ProgramLocation getSourceLocation(VTAssociation association, Address sourceAddress) { - Address defaultSourceAddress = association.getSourceAddress(); + // Ignore the sourceAddress that is handed in and instead use the association // source address that should be the source function's entry point. + Address defaultSourceAddress = association.getSourceAddress(); FunctionNameFieldLocation functionNameLocation = getFunctionNameLocation(association, defaultSourceAddress, true); if (functionNameLocation != null) { @@ -340,23 +478,25 @@ public class FunctionNameMarkupType extends FunctionEntryPointBasedAbstractMarku } @Override - public Stringable getCurrentDestinationValue(VTAssociation association, - Address destinationAddress) { - Address expectedDestinationAddress = association.getDestinationAddress(); - if (expectedDestinationAddress.equals(destinationAddress)) { - Function function = getDestinationFunction(association); - if (function == null) { - return null; - } - String functionName = function.getName(); - Namespace namespace = function.getParentNamespace(); - Program program = getDestinationProgram(association); - Address address = association.getDestinationAddress(); - SymbolTable symbolTable = program.getSymbolTable(); - Symbol symbol = symbolTable.getSymbol(functionName, address, namespace); - return new FunctionNameStringable(symbol); + public Stringable getCurrentDestinationValue(VTAssociation association, Address destAddress) { + + Address expectedDestAddress = association.getDestinationAddress(); + if (!expectedDestAddress.equals(destAddress)) { + return null; } - return null; + + Function function = getDestinationFunction(association); + if (function == null) { + return null; + } + + String functionName = function.getName(); + Namespace namespace = function.getParentNamespace(); + Program program = getDestinationProgram(association); + Address address = association.getDestinationAddress(); + SymbolTable symbolTable = program.getSymbolTable(); + Symbol symbol = symbolTable.getSymbol(functionName, address, namespace); + return new FunctionNameStringable(symbol); } @Override @@ -388,16 +528,20 @@ public class FunctionNameMarkupType extends FunctionEntryPointBasedAbstractMarku Options options = applyOptions.copy(); switch (applyAction) { case ADD: - applyOptions.setEnum(FUNCTION_NAME, FunctionNameChoices.ADD); + applyOptions.setEnum(FUNCTION_NAME, ADD); break; case ADD_AS_PRIMARY: - applyOptions.setEnum(FUNCTION_NAME, FunctionNameChoices.ADD_AS_PRIMARY); + applyOptions.setEnum(FUNCTION_NAME, ADD_AS_PRIMARY); break; case REPLACE_DEFAULT_ONLY: - applyOptions.setEnum(FUNCTION_NAME, FunctionNameChoices.REPLACE_DEFAULT_ONLY); + applyOptions.setEnum(FUNCTION_NAME, REPLACE_DEFAULT_ONLY); break; case REPLACE: - applyOptions.setEnum(FUNCTION_NAME, FunctionNameChoices.REPLACE_ALWAYS); + applyOptions.setEnum(FUNCTION_NAME, REPLACE_ALWAYS); + break; + case REPLACE_FIRST_ONLY: + break; + default: break; } return options; @@ -412,8 +556,8 @@ public class FunctionNameMarkupType extends FunctionEntryPointBasedAbstractMarku if (sourceFunction == null || destinationFunction == null) { return false; } - String sourceName = sourceFunction.getName(); - String destinationName = destinationFunction.getName(); + String sourceName = sourceFunction.getName(true); + String destinationName = destinationFunction.getName(true); return sourceName.equals(destinationName); } } diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/markuptype/FunctionSignatureMarkupType.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/markuptype/FunctionSignatureMarkupType.java index 79e3c5a9e9..bb234def63 100644 --- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/markuptype/FunctionSignatureMarkupType.java +++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/markuptype/FunctionSignatureMarkupType.java @@ -1,13 +1,12 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * 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. @@ -16,13 +15,19 @@ */ 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.main.*; import ghidra.feature.vt.api.stringable.FunctionSignatureStringable; import ghidra.feature.vt.api.util.Stringable; import ghidra.feature.vt.api.util.VersionTrackingApplyException; -import ghidra.feature.vt.gui.util.*; -import ghidra.feature.vt.gui.util.VTMatchApplyChoices.FunctionSignatureChoices; +import ghidra.feature.vt.gui.plugin.VTController; +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.ToolOptions; import ghidra.program.model.address.Address; @@ -30,16 +35,12 @@ import ghidra.program.model.listing.*; import ghidra.program.util.*; import ghidra.util.SystemUtilities; -import java.util.*; - public class FunctionSignatureMarkupType extends FunctionEntryPointBasedAbstractMarkupType { //================================================================================================== // Factory Methods //================================================================================================== - public static final VTMarkupType INSTANCE = new FunctionSignatureMarkupType(); - @Override public List createMarkupItems(VTAssociation association) { @@ -67,6 +68,10 @@ public class FunctionSignatureMarkupType extends FunctionEntryPointBasedAbstract // End Factory Methods //================================================================================================== + public static final VTMarkupType INSTANCE = new FunctionSignatureMarkupType(); + + private ToolOptions unapplyOptions; + private FunctionSignatureMarkupType() { super("Function Signature"); } @@ -106,8 +111,42 @@ public class FunctionSignatureMarkupType extends FunctionEntryPointBasedAbstract return; } - destinationSignatureStringable.applyFunctionSignature(destinationFunction, - VT_UNAPPLY_MARKUP_OPTIONS, true); + ToolOptions options = getUnapplyOptions(); + 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 @@ -122,24 +161,6 @@ public class FunctionSignatureMarkupType extends FunctionEntryPointBasedAbstract 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(); if (destinationAddress == null) { @@ -147,7 +168,8 @@ public class FunctionSignatureMarkupType extends FunctionEntryPointBasedAbstract } 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()); @@ -166,11 +188,7 @@ public class FunctionSignatureMarkupType extends FunctionEntryPointBasedAbstract throw new VersionTrackingApplyException( "Couldn't find destination function to apply a name."); } - if (sourceStringable.applyFunctionSignature(destinationFunction, adjustedOptions, 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); + if (sourceStringable.applyFunctionSignature(destinationFunction, markupOptions, false)) { return true; } return false; @@ -223,8 +241,9 @@ public class FunctionSignatureMarkupType extends FunctionEntryPointBasedAbstract } Address entryAddress = function.getEntryPoint(); Stringable value = - isSource ? getSourceValue(association, address) : getCurrentDestinationValue( - association, address); + isSource ? getSourceValue(association, address) + : getCurrentDestinationValue( + association, address); String displayString = (value != null) ? value.getDisplayString() : null; return new FunctionReturnTypeFieldLocation(program, entryAddress, displayString); } @@ -284,6 +303,10 @@ public class FunctionSignatureMarkupType extends FunctionEntryPointBasedAbstract options.setEnum(VTOptionDefines.FUNCTION_SIGNATURE, FunctionSignatureChoices.REPLACE); break; + case REPLACE_FIRST_ONLY: + break; + default: + break; } return options; } diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/markuptype/VTMarkupType.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/markuptype/VTMarkupType.java index 1174dfb44a..8803822800 100644 --- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/markuptype/VTMarkupType.java +++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/markuptype/VTMarkupType.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -15,8 +15,6 @@ */ package ghidra.feature.vt.api.markuptype; -import static ghidra.feature.vt.gui.util.VTOptionDefines.*; - import java.util.Collection; 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.util.Stringable; 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.ToolOptions; import ghidra.program.model.address.Address; @@ -36,33 +32,6 @@ import ghidra.util.task.TaskMonitor; 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; public VTMarkupType(String name) { diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/stringable/FunctionNameStringable.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/stringable/FunctionNameStringable.java index 6bda8f81a6..270a2f73a2 100644 --- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/stringable/FunctionNameStringable.java +++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/stringable/FunctionNameStringable.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -17,6 +17,7 @@ package ghidra.feature.vt.api.stringable; import java.util.*; +import generic.json.Json; import ghidra.app.cmd.label.SetLabelPrimaryCmd; import ghidra.app.util.NamespaceUtils; import ghidra.feature.vt.api.util.Stringable; @@ -45,8 +46,10 @@ public class FunctionNameStringable extends Stringable { if (symbol == null) { return; } + this.symbolName = symbol.getName(); this.sourceType = symbol.getSource(); + Namespace namespace = symbol.getParentNamespace(); while (namespace != null) { if (namespace instanceof GlobalNamespace) { @@ -84,11 +87,11 @@ public class FunctionNameStringable extends Stringable { sourceType = SourceType.valueOf(sourceName); while (tok.hasMoreTokens()) { - getNamespaceInfo(tok); + addNamespaceInfo(tok); } } - private void getNamespaceInfo(StringTokenizer tok) { + private void addNamespaceInfo(StringTokenizer tok) { String name = tok.nextToken(); int id = Integer.parseInt(tok.nextToken()); SymbolType type = SymbolType.getSymbolType(id); @@ -97,64 +100,187 @@ public class FunctionNameStringable extends Stringable { 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 { - 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) { // Apply a default name by removing the current one. - program.getSymbolTable().removeSymbolSpecial(function.getSymbol()); + symbolTable.removeSymbolSpecial(targetFunction.getSymbol()); + s.setNamespace(namespace); 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); } - public void addFunctionName(Program program, Function function, boolean isPrimary) + private Namespace createAllNamespacesAndClasses(Function destFunction, + List 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 { + List 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 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 namespaces) + throws DuplicateNameException, InvalidInputException { + SymbolTable symbolTable = program.getSymbolTable(); Namespace namespace = program.getGlobalNamespace(); - for (NamespaceInfo info : namespaceInfos) { - Namespace ns = symbolTable.getNamespace(info.name, namespace); - namespace = ns != null ? ns : 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); - } + for (NamespaceInfo info : namespaces) { + Namespace nextNs = symbolTable.getNamespace(info.name, namespace); + namespace = nextNs != null ? nextNs : createNamespace(program, info, namespace); } + return namespace; } private Namespace createNamespace(Program program, NamespaceInfo info, Namespace namespace) @@ -209,33 +335,52 @@ public class FunctionNameStringable extends Stringable { return true; } - //================================================================================================== - // Inner Classes - //================================================================================================== +//================================================================================================== +// Inner Classes +//================================================================================================== 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.symbolType = namespace.getSymbol().getSymbolType(); this.sourceType = namespace.getSymbol().getSource(); } - public NamespaceInfo(String name, SymbolType type, SourceType sourceType) { + NamespaceInfo(String name, SymbolType type, SourceType sourceType) { this.name = name; this.symbolType = type; this.sourceType = sourceType; } - } + @Override + public String toString() { + return Json.toString(this); + } - public String getSymbolName() { - return symbolName; - } + @Override + 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; } } diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/provider/matchtable/ApplyMarkupPropertyEditor.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/provider/matchtable/ApplyMarkupPropertyEditor.java index 376a915ceb..d336e34f63 100644 --- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/provider/matchtable/ApplyMarkupPropertyEditor.java +++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/provider/matchtable/ApplyMarkupPropertyEditor.java @@ -19,7 +19,6 @@ import static ghidra.feature.vt.gui.util.VTOptionDefines.*; import java.awt.BorderLayout; import java.awt.Component; -import java.awt.event.ActionListener; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; @@ -59,6 +58,8 @@ public class ApplyMarkupPropertyEditor implements OptionsEditor { private static final String FUNCTION_SIGNATURE_TOOLTIP = "The apply action for the function signature " + "when performing bulk apply operations"; + private static final String USE_NAMESPACE_TOOLTIP = + "When true function namespaces will be applied when names are applied."; private static final String PLATE_COMMENT_TOOLTIP = "The apply action for plate comments when performing bulk apply operations"; private static final String PRE_COMMENT_TOOLTIP = @@ -119,6 +120,7 @@ public class ApplyMarkupPropertyEditor implements OptionsEditor { private JLabel dataMatchDataTypeLabel; private JLabel functionNameLabel; private JLabel functionSignatureLabel; + private JLabel useFunctionNamespaceLabel; private JLabel returnTypeLabel; private JLabel inlineLabel; private JLabel noReturnLabel; @@ -138,6 +140,7 @@ public class ApplyMarkupPropertyEditor implements OptionsEditor { private JComboBox> dataMatchDataTypeComboBox; private JComboBox> functionNameComboBox; private JComboBox> functionSignatureComboBox; + private JCheckBox useFunctionNamespaceCheckBox; private JComboBox> returnTypeComboBox; private JComboBox> callingConventionComboBox; private JComboBox> inlineComboBox; @@ -160,8 +163,6 @@ public class ApplyMarkupPropertyEditor implements OptionsEditor { private JCheckBox ignoreExcludedCheckBox; private JCheckBox ignoreIncompleteCheckBox; - private ActionListener defaultActionListener; - private ToolOptions originalOptions; private PropertyChangeListener listener; private boolean unappliedChanges = false; @@ -206,7 +207,6 @@ public class ApplyMarkupPropertyEditor implements OptionsEditor { panel.add(createSeparator()); panel.add(createIgnoreCheckBoxPanel()); - // TODO More needs to be done with the layout here. panel.setBorder(BorderFactory.createTitledBorder("Apply Markup Options")); JScrollPane scrollPane = new JScrollPane(panel); @@ -223,7 +223,6 @@ public class ApplyMarkupPropertyEditor implements OptionsEditor { private Component createIgnoreCheckBoxPanel() { createIgnoreCheckBoxes(); - setupIgnoreMarkupItemsListeners(); JPanel panel = new JPanel(); panel.add(ignoreExcludedCheckBox); @@ -239,16 +238,10 @@ public class ApplyMarkupPropertyEditor implements OptionsEditor { ignoreExcludedCheckBox.setToolTipText(IGNORE_EXCLUDED_TOOLTIP); } - private void setupIgnoreMarkupItemsListeners() { - ignoreExcludedCheckBox.addActionListener(defaultActionListener); - ignoreIncompleteCheckBox.addActionListener(defaultActionListener); - } - private JPanel createFunctionSignatureSubPanel() { createFunctionSignatureDetailLabels(); createFunctionSignatureDetailChoices(); - setupFunctionSignatureDetailChoiceListeners(); JPanel outerPanel = new JPanel(); outerPanel.setBorder(BorderFactory.createEmptyBorder(0, 3, 0, 0)); @@ -325,19 +318,9 @@ public class ApplyMarkupPropertyEditor implements OptionsEditor { 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() { createParameterLabels(); createParameterChoices(); - setupParameterChoiceListeners(); JPanel outerPanel = new JPanel(); outerPanel.setBorder(BorderFactory.createEmptyBorder(0, 3, 0, 0)); @@ -402,15 +385,6 @@ public class ApplyMarkupPropertyEditor implements OptionsEditor { 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() { JPanel outerPanel = new JPanel(); JPanel panel = new JPanel(); @@ -436,7 +410,6 @@ public class ApplyMarkupPropertyEditor implements OptionsEditor { createNonCommentMarkupLabels(); createNonCommentMarkupChoices(); - setupNonCommentMarkupChoiceListeners(); JPanel outerPanel = new JPanel(); outerPanel.setBorder(BorderFactory.createEmptyBorder(0, 3, 0, 0)); @@ -452,6 +425,9 @@ public class ApplyMarkupPropertyEditor implements OptionsEditor { panel.add(functionSignatureLabel); panel.add(functionSignatureComboBox); + panel.add(useFunctionNamespaceLabel); + panel.add(useFunctionNamespaceCheckBox); + outerPanel.add(panel); return outerPanel; } @@ -468,6 +444,9 @@ public class ApplyMarkupPropertyEditor implements OptionsEditor { functionSignatureLabel = new GDLabel("Function Signature", SwingConstants.RIGHT); functionSignatureLabel.setToolTipText(FUNCTION_SIGNATURE_TOOLTIP); + + useFunctionNamespaceLabel = new GDLabel("Replace Namespace", SwingConstants.RIGHT); + useFunctionNamespaceLabel.setToolTipText(USE_NAMESPACE_TOOLTIP); } private void createNonCommentMarkupChoices() { @@ -485,19 +464,13 @@ public class ApplyMarkupPropertyEditor implements OptionsEditor { functionSignatureComboBox = createComboBox(VTOptionDefines.FUNCTION_SIGNATURE, DEFAULT_OPTION_FOR_FUNCTION_SIGNATURE); functionSignatureComboBox.setToolTipText(FUNCTION_SIGNATURE_TOOLTIP); - } - private void setupNonCommentMarkupChoiceListeners() { - dataMatchDataTypeComboBox.addActionListener(defaultActionListener); - labelsComboBox.addActionListener(defaultActionListener); - functionNameComboBox.addActionListener(defaultActionListener); - functionSignatureComboBox.addActionListener(defaultActionListener); + useFunctionNamespaceCheckBox = createCheckBox(""); } private JPanel createCommentsSubPanel() { createCommentLabels(); createCommentChoices(); - setupCommentChoiceListeners(); JPanel outerPanel = new JPanel(); outerPanel.setBorder(BorderFactory.createEmptyBorder(0, 3, 0, 0)); @@ -562,14 +535,6 @@ public class ApplyMarkupPropertyEditor implements OptionsEditor { 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) { updateNonCommentMarkupOptions(options); updateCommentOptions(options); @@ -594,6 +559,9 @@ public class ApplyMarkupPropertyEditor implements OptionsEditor { FunctionSignatureChoices functionSignatureChoice = (FunctionSignatureChoices) functionSignatureComboBox.getSelectedItem(); options.setEnum(FUNCTION_SIGNATURE, functionSignatureChoice); + + boolean useNamespaces = useFunctionNamespaceCheckBox.isSelected(); + options.setBoolean(USE_NAMESPACE_FUNCTIONS, useNamespaces); } private void updateCommentOptions(ToolOptions options) { @@ -704,6 +672,12 @@ public class ApplyMarkupPropertyEditor implements OptionsEditor { if (functionSignatureChoice != functionSignatureComboBox.getSelectedItem()) { 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) { @@ -852,7 +826,6 @@ public class ApplyMarkupPropertyEditor implements OptionsEditor { // signals that there are unapplied changes private void changesMade(boolean changes) { - // FIXME Is this ok? complete? if (listener != null) { listener.propertyChange(new PropertyChangeEvent(this, GhidraOptions.APPLY_ENABLED, unappliedChanges, changes)); diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/provider/matchtable/VTMatchTableProvider.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/provider/matchtable/VTMatchTableProvider.java index a9ee73fe03..f03903c6ad 100644 --- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/provider/matchtable/VTMatchTableProvider.java +++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/provider/matchtable/VTMatchTableProvider.java @@ -766,6 +766,9 @@ public class VTMatchTableProvider extends ComponentProviderAdapter "Markup items that are incomplete (for example, no destination address is specified) " + "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) .registerOptionsEditor(() -> new ApplyMarkupPropertyEditor(controller)); vtOptions.getOptions(DISPLAY_APPLY_MARKUP_OPTIONS) diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/util/VTOptionDefines.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/util/VTOptionDefines.java index 2732e3a7b4..999ce5d331 100644 --- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/util/VTOptionDefines.java +++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/util/VTOptionDefines.java @@ -68,6 +68,8 @@ public class VTOptionDefines { public static CommentChoices DEFAULT_OPTION_FOR_POST_COMMENTS = 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_RETURN_TYPE = APPLY_MARKUP_OPTIONS_NAME + ".Function Return Type"; @@ -105,9 +107,12 @@ public class VTOptionDefines { public static final String IGNORE_EXCLUDED_MARKUP_ITEMS = APPLY_MARKUP_OPTIONS_NAME + ".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"; + public static final String USE_NAMESPACE_FUNCTIONS = + APPLY_MARKUP_OPTIONS_NAME + ".Replace Namespace"; + // Auto VT Options public static final String AUTO_VT_OPTIONS_NAME = "Auto Version Tracking Options"; diff --git a/Ghidra/Features/VersionTracking/src/test.slow/java/ghidra/feature/vt/api/markupitem/FunctionNameMarkupItem2Test.java b/Ghidra/Features/VersionTracking/src/test.slow/java/ghidra/feature/vt/api/markupitem/FunctionNameMarkupItem2Test.java new file mode 100644 index 0000000000..729e0921fb --- /dev/null +++ b/Ghidra/Features/VersionTracking/src/test.slow/java/ghidra/feature/vt/api/markupitem/FunctionNameMarkupItem2Test.java @@ -0,0 +1,1204 @@ +/* ### + * 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.feature.vt.api.markupitem; + +import static ghidra.feature.vt.api.main.VTMarkupItemApplyActionType.*; +import static ghidra.feature.vt.db.VTTestUtils.*; +import static ghidra.feature.vt.gui.util.VTOptionDefines.*; +import static org.junit.Assert.*; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Test; + +import ghidra.app.cmd.label.DeleteLabelCmd; +import ghidra.app.util.NamespaceUtils; +import ghidra.feature.vt.api.db.VTSessionDB; +import ghidra.feature.vt.api.main.*; +import ghidra.feature.vt.api.markuptype.FunctionNameMarkupType; +import ghidra.feature.vt.gui.task.*; +import ghidra.feature.vt.gui.util.VTMatchApplyChoices.FunctionNameChoices; +import ghidra.framework.options.ToolOptions; +import ghidra.program.database.symbol.SymbolManager; +import ghidra.program.model.address.Address; +import ghidra.program.model.listing.*; +import ghidra.program.model.symbol.*; +import utility.function.Callback; + +/** + * Tests that focus on how to apply function name markup when it comes to default vs non-default + * for source and destination, along with whether or not to apply a non-default namespace. + */ +public class FunctionNameMarkupItem2Test extends AbstractVTMarkupItemTest { + + private SetUp srcSetUp; + private SetUp destSetUp; + private FunctionNameValidator validator; + + private Function destFunction; + private Function srcFunction; + + private String originalSrcName; + private String originalDestName; + private String originalDestNsString; + private String originalSrcNsString; + + private SetUp srcSetUp_DefaultName_DefaultNs = new SetUp(() -> { + setDefaultSource(true); + }); + + private SetUp destSetUp_DefaultName_DefaultNs = new SetUp(() -> { + setDefaultDestination(true); + }); + + private SetUp srcSetUp_DefaultName_NonDefaultNs = new SetUp(() -> { + setDefaultSource(false); + }); + + private SetUp destSetUp_DefaultName_NonDefaultNs = new SetUp(() -> { + setDefaultDestination(false); + }); + + private SetUp srcSetUp_NonDefaultName_DefaultNs = new SetUp(() -> { + setNonDefaultSource(true); + }); + + private SetUp destSetUp_NonDefaultName_DefaultNs = new SetUp(() -> { + setNonDefaultDestination(true); + }); + + private SetUp srcSetUp_NonDefaultName_NonDefaultNs = new SetUp(() -> { + setNonDefaultSource(false); + }); + + private SetUp destSetUp_NonDefaultName_NonDefaultNs = new SetUp(() -> { + setNonDefaultDestination(false); + }); + private VTSessionDB session; + private VTMatch match; + private VTMarkupItem markupItem; + + @Test + public void testDefaultSrcName_DefaultNs_DefaultDestName_DefaultDestNs() throws Exception { + + srcSetUp = srcSetUp_DefaultName_DefaultNs; + destSetUp = destSetUp_DefaultName_DefaultNs; + + // Replace Default + validator = createValidator(FunctionNameChoices.REPLACE_DEFAULT_ONLY); + doTestApply_NoMarkup(); // no markup for default name in the default namespace + + // Replace Always + validator = createValidator(FunctionNameChoices.REPLACE_ALWAYS); + doTestApply_NoMarkup(); + + // Add + validator = createValidator(FunctionNameChoices.ADD); + doTestApply_NoMarkup(); + + // Add as Primary + validator = createValidator(FunctionNameChoices.ADD_AS_PRIMARY); + doTestApply_NoMarkup(); + } + + // @Test + public void testDefaultSrcName_DefaultNs_DefaultDestName_NonDefaultDestNs() throws Exception { + // Note: since there is no markup for a default name in a default namespace, this is covered + // in the test above + } + + //@Test + public void testDefaultSrcName_DefaultNs_NonDefaultDestName_DefaultDestNs() throws Exception { + // Note: since there is no markup for a default name in a default namespace, this is covered + // in the test above + } + + //@Test + public void testDefaultSrcName_DefaultNs_NonDefaultDestName_NonDefaultDestNs() + throws Exception { + // Note: since there is no markup for a default name in a default namespace, this is covered + // in the test above + } + + @Test + public void testDefaultSrcName_NonDefaultNs_DefaultDestName_DefaultDestNs() throws Exception { + + srcSetUp = srcSetUp_DefaultName_NonDefaultNs; + destSetUp = destSetUp_DefaultName_DefaultNs; + + // Replace Default + validator = createValidator(FunctionNameChoices.REPLACE_DEFAULT_ONLY); + doTestApply_NoEffect(); // no effect due to default name when 'replace namespace' is off + assertPrimaryDestNameAndNamespaceUnchanged(); + + // Replace Always + validator = createValidator(FunctionNameChoices.REPLACE_ALWAYS); + doTestApply_NoEffect(); // no effect due to default name when 'replace namespace' is off + assertPrimaryDestNameAndNamespaceUnchanged(); + + // Add + validator = createValidator(FunctionNameChoices.ADD); + doTestApply_NoEffect(); // no effect due to default name when 'replace namespace' is off + assertPrimaryDestNameAndNamespaceUnchanged(); + + // Add as Primary + validator = createValidator(FunctionNameChoices.ADD_AS_PRIMARY); + doTestApply_NoEffect(); // no effect due to default name when 'replace namespace' is off + assertPrimaryDestNameAndNamespaceUnchanged(); + + // + // Now test again with 'replace namespace' enabled + // + // Replace Default + validator = createValidator(FunctionNameChoices.REPLACE_DEFAULT_ONLY, true); + doTestApply(); + assertDestNameUnchanged(); + assertDestHasSourceNs(); + + // Replace Always + validator = createValidator(FunctionNameChoices.REPLACE_ALWAYS, true); + doTestApply(); + assertDestNameUnchanged(); + assertDestHasSourceNs(); + + // Add + validator = createValidator(FunctionNameChoices.ADD, true); + doTestApply_NoEffect(); + assertPrimaryDestNameAndNamespaceUnchanged(); + + // Add as Primary + validator = createValidator(FunctionNameChoices.ADD_AS_PRIMARY, true); + doTestApply_NoEffect(); + assertPrimaryDestNameAndNamespaceUnchanged(); + } + + @Test + public void testDefaultSrcName_NonDefaultNs_DefaultDestName_NonDefaultDestNs() + throws Exception { + + srcSetUp = srcSetUp_DefaultName_NonDefaultNs; + destSetUp = destSetUp_DefaultName_NonDefaultNs; + + // Replace Default + validator = createValidator(FunctionNameChoices.REPLACE_DEFAULT_ONLY); + doTestApply_NoEffect(); // no effect due to default name when 'replace namespace' is off + assertPrimaryDestNameAndNamespaceUnchanged(); + + // Replace Always + validator = createValidator(FunctionNameChoices.REPLACE_ALWAYS); + doTestApply_NoEffect(); + assertPrimaryDestNameAndNamespaceUnchanged(); + + // Add + validator = createValidator(FunctionNameChoices.ADD); + doTestApply_NoEffect(); + assertPrimaryDestNameAndNamespaceUnchanged(); + + // Add as Primary + validator = createValidator(FunctionNameChoices.ADD_AS_PRIMARY); + doTestApply_NoEffect(); + assertPrimaryDestNameAndNamespaceUnchanged(); + + // + // Now test again with 'replace namespace' enabled + // + // Replace Default + validator = createValidator(FunctionNameChoices.REPLACE_DEFAULT_ONLY, true); + doTestApply(); + assertDestNameUnchanged(); + assertDestHasSourceNs(); + + // Replace Always + validator = createValidator(FunctionNameChoices.REPLACE_ALWAYS, true); + doTestApply(); + assertDestNameUnchanged(); + assertDestHasSourceNs(); + + // Add + validator = createValidator(FunctionNameChoices.ADD, true); + doTestApply_NoEffect(); + assertDestNameUnchanged(); + + // Add as Primary + validator = createValidator(FunctionNameChoices.ADD_AS_PRIMARY, true); + doTestApply_NoEffect(); + assertDestNameUnchanged(); + } + + @Test + public void testDefaultSrcName_NonDefaultNs_NonDefaultDestName_DefaultDestNs() + throws Exception { + + srcSetUp = srcSetUp_DefaultName_NonDefaultNs; + destSetUp = destSetUp_NonDefaultName_DefaultNs; + + // Replace Default + validator = createValidator(FunctionNameChoices.REPLACE_DEFAULT_ONLY); + doTestApply_NoEffect(); // no effect due to default name when 'replace namespace' is off + assertPrimaryDestNameAndNamespaceUnchanged(); + + // Replace Always + validator = createValidator(FunctionNameChoices.REPLACE_ALWAYS); + doTestApply_NoEffect(); + assertPrimaryDestNameAndNamespaceUnchanged(); + + // Add + validator = createValidator(FunctionNameChoices.ADD); + doTestApply_NoEffect(); + assertPrimaryDestNameAndNamespaceUnchanged(); + + // Add as Primary + validator = createValidator(FunctionNameChoices.ADD_AS_PRIMARY); + doTestApply_NoEffect(); + + // + // Now test again with 'replace namespace' enabled + // + // Replace Default + validator = createValidator(FunctionNameChoices.REPLACE_DEFAULT_ONLY, true); + doTestApply_NoEffect(); // destination not default; no action taken + assertPrimaryDestNameAndNamespaceUnchanged(); + + // Replace Always + validator = createValidator(FunctionNameChoices.REPLACE_ALWAYS, true); + doTestApply(); + assertDestNameIsDefault(); + assertDestHasSourceNs(); + + // Add + validator = createValidator(FunctionNameChoices.ADD, true); + doTestApply_NoEffect(); + assertPrimaryDestNameAndNamespaceUnchanged(); + + // Add as Primary + validator = createValidator(FunctionNameChoices.ADD_AS_PRIMARY, true); + doTestApply_NoEffect(); + assertPrimaryDestNameAndNamespaceUnchanged(); + } + + @Test + public void testDefaultSrcName_NonDefaultNs_NonDefaultDestName_NonDefaultDestNs() + throws Exception { + + srcSetUp = srcSetUp_DefaultName_NonDefaultNs; + destSetUp = destSetUp_NonDefaultName_NonDefaultNs; + + // Replace Default + validator = createValidator(FunctionNameChoices.REPLACE_DEFAULT_ONLY); + doTestApply_NoEffect(); // no effect due to default name when 'replace namespace' is off + assertPrimaryDestNameAndNamespaceUnchanged(); + + // Replace Always + validator = createValidator(FunctionNameChoices.REPLACE_ALWAYS); + doTestApply_NoEffect(); + assertPrimaryDestNameAndNamespaceUnchanged(); + + // Add + validator = createValidator(FunctionNameChoices.ADD); + doTestApply_NoEffect(); + assertPrimaryDestNameAndNamespaceUnchanged(); + + // Add as Primary + validator = createValidator(FunctionNameChoices.ADD_AS_PRIMARY); + doTestApply_NoEffect(); + assertPrimaryDestNameAndNamespaceUnchanged(); + + // + // Now test again with 'replace namespace' enabled + // + // Replace Default + validator = createValidator(FunctionNameChoices.REPLACE_DEFAULT_ONLY, true); + doTestApply_NoEffect(); // destination not default; no action taken + assertPrimaryDestNameAndNamespaceUnchanged(); + + // Replace Always + validator = createValidator(FunctionNameChoices.REPLACE_ALWAYS, true); + doTestApply(); + assertDestNameIsDefault(); + assertDestHasSourceNs(); + + // Add + validator = createValidator(FunctionNameChoices.ADD, true); + doTestApply_NoEffect(); + assertPrimaryDestNameAndNamespaceUnchanged(); + + // Add as Primary + validator = createValidator(FunctionNameChoices.ADD_AS_PRIMARY, true); + doTestApply_NoEffect(); + assertPrimaryDestNameAndNamespaceUnchanged(); + } + + @Test + public void testNonDefaultSource_DefaultNamespace_DefaultDestName_DefaultDestNs() + throws Exception { + + srcSetUp = srcSetUp_NonDefaultName_DefaultNs; + destSetUp = destSetUp_DefaultName_DefaultNs; + + // Replace Default + validator = createValidator(FunctionNameChoices.REPLACE_DEFAULT_ONLY); + doTestApply(); + assertDestHasSrcName(); + assertDestNsUnchanged(); + + // Replace Always + validator = createValidator(FunctionNameChoices.REPLACE_ALWAYS); + doTestApply(); + assertDestHasSrcName(); + assertDestNsUnchanged(); + + // Add + validator = createValidator(FunctionNameChoices.ADD); + doTestApply(); + assertDestHasSrcName(); // Name replaced, not added, since the destination was default + assertDestNsUnchanged(); + + // Add as Primary + validator = createValidator(FunctionNameChoices.ADD_AS_PRIMARY); + doTestApply(); + assertDestHasSrcName(); // Name replaced, not added, since the destination was default + assertDestNsUnchanged(); + + // + // Now test again with 'replace namespace' enabled + // + // Replace Default + validator = createValidator(FunctionNameChoices.REPLACE_DEFAULT_ONLY, true); + doTestApply(); + assertDestHasSrcName(); + assertDestNsUnchanged(); + + // Replace Always + validator = createValidator(FunctionNameChoices.REPLACE_ALWAYS, true); + doTestApply(); + assertDestHasSrcName(); + assertDestNsUnchanged(); + + // Add + validator = createValidator(FunctionNameChoices.ADD, true); + doTestApply(); + assertDestHasSrcName(); // Name replaced, not added, since the destination was default + assertDestNsUnchanged(); + + // Add as Primary + validator = createValidator(FunctionNameChoices.ADD_AS_PRIMARY, true); + doTestApply(); + assertDestHasSrcName(); // Name replaced, not added, since the destination was default + assertDestNsUnchanged(); + } + + @Test + public void testNonDefaultSource_DefaultNamespace_DefaultDestName_NonDefaultDestNs() + throws Exception { + + srcSetUp = srcSetUp_NonDefaultName_DefaultNs; + destSetUp = destSetUp_DefaultName_NonDefaultNs; + + // Replace Default + validator = createValidator(FunctionNameChoices.REPLACE_DEFAULT_ONLY); + doTestApply(); + assertDestHasSrcName(); + assertDestNsUnchanged(); + + // Replace Always + validator = createValidator(FunctionNameChoices.REPLACE_ALWAYS); + doTestApply(); + assertDestHasSrcName(); + assertDestNsUnchanged(); + + // Add + validator = createValidator(FunctionNameChoices.ADD); + doTestApply(); + assertDestHasSrcName(); // Name replaced, not added, since the destination was default + assertDestNsUnchanged(); + + // Add as Primary + validator = createValidator(FunctionNameChoices.ADD_AS_PRIMARY); + doTestApply(); + assertDestHasSrcName(); // Name replaced, not added, since the destination was default + assertDestNsUnchanged(); + + // + // Now test again with 'replace namespace' enabled + // + // Replace Default + validator = createValidator(FunctionNameChoices.REPLACE_DEFAULT_ONLY, true); + doTestApply(); + assertDestHasSrcName(); + assertDestNsUnchanged(); + + // Replace Always + validator = createValidator(FunctionNameChoices.REPLACE_ALWAYS, true); + doTestApply(); + assertDestHasSrcName(); + assertDestNsUnchanged(); + + // Add + validator = createValidator(FunctionNameChoices.ADD, true); + doTestApply(); + assertDestHasSrcName(); // Name replaced, not added, since the destination was default + assertDestNsUnchanged(); + + // Add as Primary + validator = createValidator(FunctionNameChoices.ADD_AS_PRIMARY, true); + doTestApply(); + assertDestHasSrcName(); // Name replaced, not added, since the destination was default + assertDestNsUnchanged(); + } + + @Test + public void testNonDefaultSource_DefaultNamespace_NonDefaultDestName_DefaultDestNs() + throws Exception { + + srcSetUp = srcSetUp_NonDefaultName_DefaultNs; + destSetUp = destSetUp_NonDefaultName_DefaultNs; + + // Replace Default + validator = createValidator(FunctionNameChoices.REPLACE_DEFAULT_ONLY); + doTestApply_NoEffect(); + assertPrimaryDestNameAndNamespaceUnchanged(); + + // Replace Always + validator = createValidator(FunctionNameChoices.REPLACE_ALWAYS); + doTestApply(); + assertDestHasSrcName(); + assertDestNsUnchanged(); + + // Add + validator = createValidator(FunctionNameChoices.ADD); + doTestApply(); + assertDestLabelAdded(); + assertPrimaryDestNameAndNamespaceUnchanged(); + + // Add as Primary + validator = createValidator(FunctionNameChoices.ADD_AS_PRIMARY); + doTestApply(); + assertDestHasSrcName(); // Name replaced, not added, since the destination was default + assertDestNsUnchanged(); + + // + // Now test again with 'replace namespace' enabled + // + // Replace Default + validator = createValidator(FunctionNameChoices.REPLACE_DEFAULT_ONLY, true); + doTestApply_NoEffect(); + assertPrimaryDestNameAndNamespaceUnchanged(); + + // Replace Always + validator = createValidator(FunctionNameChoices.REPLACE_ALWAYS, true); + doTestApply(); + assertDestHasSrcName(); + assertDestNsUnchanged(); + + // Add + validator = createValidator(FunctionNameChoices.ADD, true); + doTestApply(); + assertDestLabelAdded(); + assertPrimaryDestNameAndNamespaceUnchanged(); + + // Add as Primary + validator = createValidator(FunctionNameChoices.ADD_AS_PRIMARY, true); + doTestApply(); + assertDestLabelAdded(); + assertDestHasSrcName(); + } + + @Test + public void testNonDefaultSource_DefaultNamespace_NonDefaultDestName_NonDefaultDestNs() + throws Exception { + + srcSetUp = srcSetUp_NonDefaultName_DefaultNs; + destSetUp = destSetUp_NonDefaultName_NonDefaultNs; + + // Replace Default + validator = createValidator(FunctionNameChoices.REPLACE_DEFAULT_ONLY); + doTestApply_NoEffect(); + assertPrimaryDestNameAndNamespaceUnchanged(); + + // Replace Always + validator = createValidator(FunctionNameChoices.REPLACE_ALWAYS); + doTestApply(); + assertDestHasSrcName(); + assertDestNsUnchanged(); + + // Add + validator = createValidator(FunctionNameChoices.ADD); + doTestApply(); + assertDestLabelAdded(); + assertPrimaryDestNameAndNamespaceUnchanged(); + + // Add as Primary + validator = createValidator(FunctionNameChoices.ADD_AS_PRIMARY); + doTestApply(); + assertDestHasSrcName(); // Name replaced, not added, since the destination was default + assertDestHasDefaultNamespace(); + + // + // Now test again with 'replace namespace' enabled + // + // Replace Default + validator = createValidator(FunctionNameChoices.REPLACE_DEFAULT_ONLY, true); + doTestApply_NoEffect(); + assertPrimaryDestNameAndNamespaceUnchanged(); + + // Replace Always + validator = createValidator(FunctionNameChoices.REPLACE_ALWAYS, true); + doTestApply(); + assertDestHasSrcName(); + assertDestNsUnchanged(); + + // Add + validator = createValidator(FunctionNameChoices.ADD, true); + doTestApply(); + assertDestLabelAdded(); + assertPrimaryDestNameAndNamespaceUnchanged(); + + // Add as Primary + validator = createValidator(FunctionNameChoices.ADD_AS_PRIMARY, true); + doTestApply(); + assertDestLabelAdded(); + assertDestHasSrcName(); + } + + @Test + public void testNonDefaultSource_NonDefaultNamespace_DefaultDestName_DefaultDestNs() + throws Exception { + + srcSetUp = srcSetUp_NonDefaultName_NonDefaultNs; + destSetUp = destSetUp_DefaultName_DefaultNs; + + // Replace Default + validator = createValidator(FunctionNameChoices.REPLACE_DEFAULT_ONLY); + doTestApply(); + assertDestHasSrcName(); + assertDestNsUnchanged(); + + // Replace Always + validator = createValidator(FunctionNameChoices.REPLACE_ALWAYS); + doTestApply(); + assertDestHasSrcName(); + assertDestNsUnchanged(); + + // Add + validator = createValidator(FunctionNameChoices.ADD); + doTestApply(); + assertDestHasSrcName(); // default destination label, name applied + assertDestNsUnchanged(); + + // Add as Primary + validator = createValidator(FunctionNameChoices.ADD_AS_PRIMARY); + doTestApply(); + assertDestHasSrcName(); // default destination label, name applied + assertDestNsUnchanged(); + + // + // Now test again with 'replace namespace' enabled + // + // Replace Default + validator = createValidator(FunctionNameChoices.REPLACE_DEFAULT_ONLY, true); + doTestApply(); + assertDestHasSrcName(); + assertDestHasSourceNs(); + + // Replace Always + validator = createValidator(FunctionNameChoices.REPLACE_ALWAYS, true); + doTestApply(); + assertDestHasSrcName(); + assertDestHasSourceNs(); + + // Add + validator = createValidator(FunctionNameChoices.ADD, true); + doTestApply(); + assertDestHasSrcName(); // default destination label, name applied + assertDestNsUnchanged(); + + // Add as Primary + validator = createValidator(FunctionNameChoices.ADD_AS_PRIMARY, true); + doTestApply(); + assertDestHasSrcName(); // default destination label, name applied + assertDestNsUnchanged(); + } + + @Test + public void testNonDefaultSource_NonDefaultNamespace_DefaultDestName_NonDefaultDestNs() + throws Exception { + + srcSetUp = srcSetUp_NonDefaultName_NonDefaultNs; + destSetUp = destSetUp_DefaultName_NonDefaultNs; + + // Replace Default + validator = createValidator(FunctionNameChoices.REPLACE_DEFAULT_ONLY); + doTestApply(); + assertDestHasSrcName(); + assertDestNsUnchanged(); + + // Replace Always + validator = createValidator(FunctionNameChoices.REPLACE_ALWAYS); + doTestApply(); + assertDestHasSrcName(); + assertDestNsUnchanged(); + + // Add + validator = createValidator(FunctionNameChoices.ADD); + doTestApply(); + assertDestHasSrcName(); // default destination label, name applied + assertDestNsUnchanged(); + + // Add as Primary + validator = createValidator(FunctionNameChoices.ADD_AS_PRIMARY); + doTestApply(); + assertDestLabelAdded(); + assertDestNsUnchanged(); + + // + // Now test again with 'replace namespace' enabled + // + // Replace Default + validator = createValidator(FunctionNameChoices.REPLACE_DEFAULT_ONLY, true); + doTestApply(); + assertDestHasSrcName(); + assertDestHasSourceNs(); + + // Replace Always + validator = createValidator(FunctionNameChoices.REPLACE_ALWAYS, true); + doTestApply(); + assertDestHasSrcName(); + assertDestHasSourceNs(); + + // Add + validator = createValidator(FunctionNameChoices.ADD, true); + doTestApply(); + assertDestHasSrcName(); // default destination label, name applied + assertDestNsUnchanged(); + + // Add as Primary + validator = createValidator(FunctionNameChoices.ADD_AS_PRIMARY, true); + doTestApply(); + assertDestHasSrcName(); // default destination label, name applied + assertDestNsUnchanged(); + } + + @Test + public void testNonDefaultSource_NonDefaultNamespace_NonDefaultDestName_DefaultDestNs() + throws Exception { + + srcSetUp = srcSetUp_NonDefaultName_NonDefaultNs; + destSetUp = destSetUp_NonDefaultName_DefaultNs; + + // Replace Default + validator = createValidator(FunctionNameChoices.REPLACE_DEFAULT_ONLY); + doTestApply_NoEffect(); + assertPrimaryDestNameAndNamespaceUnchanged(); + + // Replace Always + validator = createValidator(FunctionNameChoices.REPLACE_ALWAYS); + doTestApply(); + assertDestHasSrcName(); + assertDestNsUnchanged(); + + // Add + validator = createValidator(FunctionNameChoices.ADD); + doTestApply(); + assertDestLabelAdded(); + assertPrimaryDestNameAndNamespaceUnchanged(); + + // Add as Primary + validator = createValidator(FunctionNameChoices.ADD_AS_PRIMARY); + doTestApply(); + assertDestLabelAdded(); + assertDestNsUnchanged(); + + // + // Now test again with 'replace namespace' enabled + // + // Replace Default + validator = createValidator(FunctionNameChoices.REPLACE_DEFAULT_ONLY, true); + doTestApply_NoEffect(); + assertPrimaryDestNameAndNamespaceUnchanged(); + + // Replace Always + validator = createValidator(FunctionNameChoices.REPLACE_ALWAYS, true); + doTestApply(); + assertDestHasSrcName(); + assertDestHasSourceNs(); + + // Add + validator = createValidator(FunctionNameChoices.ADD, true); + doTestApply(); + assertDestLabelAdded(); + assertPrimaryDestNameAndNamespaceUnchanged(); + + // Add as Primary + validator = createValidator(FunctionNameChoices.ADD_AS_PRIMARY, true); + doTestApply(); + assertDestLabelAdded(); + assertDestHasSourceNs(); + } + + @Test + public void testNonDefaultSource_NonDefaultNamespace_NonDefaultDestName_NonDefaultDestNs() + throws Exception { + + srcSetUp = srcSetUp_NonDefaultName_NonDefaultNs; + destSetUp = destSetUp_NonDefaultName_NonDefaultNs; + + // Replace Default + validator = createValidator(FunctionNameChoices.REPLACE_DEFAULT_ONLY); + doTestApply_NoEffect(); + assertPrimaryDestNameAndNamespaceUnchanged(); + + // Replace Always + validator = createValidator(FunctionNameChoices.REPLACE_ALWAYS); + doTestApply(); + assertDestHasSrcName(); + assertDestNsUnchanged(); + + // Add + validator = createValidator(FunctionNameChoices.ADD); + doTestApply(); + assertDestLabelAdded(); + assertPrimaryDestNameAndNamespaceUnchanged(); + + // Add as Primary + validator = createValidator(FunctionNameChoices.ADD_AS_PRIMARY); + doTestApply(); + assertDestLabelAdded(); + assertDestHasDefaultNamespace(); + + // + // Now test again with 'replace namespace' enabled + // + // Replace Default + validator = createValidator(FunctionNameChoices.REPLACE_DEFAULT_ONLY, true); + doTestApply_NoEffect(); + assertPrimaryDestNameAndNamespaceUnchanged(); + + // Replace Always + validator = createValidator(FunctionNameChoices.REPLACE_ALWAYS, true); + doTestApply(); + assertDestHasSrcName(); + assertDestHasSourceNs(); + + // Add + validator = createValidator(FunctionNameChoices.ADD, true); + doTestApply(); + assertDestLabelAdded(); + assertDestNameUnchanged(); + assertDestNsUnchanged(); + + // Add as Primary + validator = createValidator(FunctionNameChoices.ADD_AS_PRIMARY, true); + doTestApply(); + assertDestLabelAdded(); + assertDestLabelAdded(); + assertDestHasSourceNs(); + } + +//================================================================================================= +// Private Methods +//================================================================================================= + + private void assertDestLabelAdded() { + SymbolManager symbolTable = destinationProgram.getSymbolTable(); + Address addr = getDestinationMatchAddress(); + Symbol symbol = getSymbol(symbolTable, originalSrcName, addr); + assertNotNull(symbol); + + if (symbol.isPrimary()) { + assertEquals(SymbolType.FUNCTION, symbol.getSymbolType()); + } + else { + assertEquals(SymbolType.LABEL, symbol.getSymbolType()); + } + } + + private void assertDestHasSrcName() { + String appliedDestinationName = destFunction.getName(false); + assertEquals(originalSrcName, appliedDestinationName); + } + + private void assertDestNameIsDefault() { + String appliedDestName = destFunction.getName(false); + assertEquals("FUN_01003f9e", appliedDestName); + } + + private void assertDestHasDefaultNamespace() { + Namespace appliedDestNs = destFunction.getParentNamespace(); + assertEquals("Global", appliedDestNs.toString()); + } + + private void assertDestNameUnchanged() { + String appliedDestName = destFunction.getName(false); + assertEquals(originalDestName, appliedDestName); + } + + private void assertDestNsUnchanged() { + Namespace appliedDestNs = destFunction.getParentNamespace(); + String appliedDestNsString = appliedDestNs.toString(); + assertEquals(originalDestNsString, appliedDestNsString); + } + + private void assertPrimaryDestNameAndNamespaceUnchanged() { + assertDestNameUnchanged(); + assertDestNsUnchanged(); + } + + private void assertDestHasSourceNs() { + Namespace appliedDestNs = destFunction.getParentNamespace(); + String appliedDestNsString = appliedDestNs.toString(); + assertEquals(originalSrcNsString, appliedDestNsString); + } + + private Address getDestinationMatchAddress() { + return destFunction.getEntryPoint(); + } + + private Symbol getSymbol(SymbolTable symbolTable, String name, Address addr) { + + Symbol[] symbols = symbolTable.getSymbols(addr); + for (Symbol s : symbols) { + String symbolName = s.getName(); + if (symbolName.equals(name)) { + return s; + } + } + return null; + } + + private void setDefaultDestination(boolean defaultNamespace) { + + Address destAddress = addr("0x01003f9e", destinationProgram); + FunctionManager destFunctionManager = destinationProgram.getFunctionManager(); + destFunction = destFunctionManager.getFunctionAt(destAddress); + + if (!defaultNamespace) { + setNamespace(destFunction, "Destination::Bar"); + } + } + + private void setNonDefaultDestination(boolean defaultNamespace) { + + Address destAddress = addr("0x01003f9e", destinationProgram); + FunctionManager destFunctionManager = destinationProgram.getFunctionManager(); + destFunction = destFunctionManager.getFunctionAt(destAddress); + + setName(destFunction, "NonDefaultDestName"); + if (!defaultNamespace) { + setNamespace(destFunction, "Destination::Bar"); + } + } + + private void setDefaultSource(boolean defaultNamespace) { + + Address srcAddress = addr("0x01002cf5", sourceProgram); + FunctionManager srcFunctionManager = sourceProgram.getFunctionManager(); + srcFunction = srcFunctionManager.getFunctionAt(srcAddress); + + clearFunctionName(srcFunction); + if (!defaultNamespace) { + setNamespace(srcFunction, "Source::Foo"); + } + } + + private void setNonDefaultSource(boolean defaultNamespace) { + Address srcAddress = addr("0x01002cf5", sourceProgram); + FunctionManager srcFunctionManager = sourceProgram.getFunctionManager(); + srcFunction = srcFunctionManager.getFunctionAt(srcAddress); + + setName(srcFunction, "NonDefaultSrcName"); + if (!defaultNamespace) { + setNamespace(srcFunction, "Source::Foo"); + } + } + + private FunctionNameValidator createValidator(FunctionNameChoices choice) + throws Exception { + return createValidator(choice, false); + } + + private FunctionNameValidator createValidator(FunctionNameChoices choice, + boolean replaceNsOption) throws Exception { + + if (validator != null) { + doTestUnapply(); + resetPrograms(); + } + + srcSetUp.run(); + destSetUp.run(); + + originalSrcName = srcFunction.getName(false); + originalDestName = destFunction.getName(false); + originalSrcNsString = srcFunction.getParentNamespace().toString(); + originalDestNsString = destFunction.getParentNamespace().toString(); + + FunctionNameValidator fv = + new FunctionNameValidator(srcFunction, destFunction, choice); + + if (replaceNsOption) { + ToolOptions options = fv.getOptions(); + options.setBoolean(USE_NAMESPACE_FUNCTIONS, true); + } + + return fv; + } + + private void doTestUnapply() { + + if (markupItem == null) { + return; // the current test found no markup + } + + // + // Verify we can unapply + // + VtTask task = new UnapplyMarkupItemTask(session, null, List.of(markupItem)); + runTask(session, task); + validator.assertUnapplied(); + } + + private void doTestApply() throws Exception { + + session = createNewSession(); + match = createMatchSetWithOneMatch(session, validator.getSourceMatchAddress(), + validator.getDestinationMatchAddress()); + markupItem = validator.searchForMarkupItem(match); + + // + // verify we cannot unapply before we have applied + // + List markupItems = new ArrayList<>(); + Address destinationApplyAddress = validator.getDestinationApplyAddress(); + markupItem.setDefaultDestinationAddress(destinationApplyAddress, TEST_ADDRESS_SOURCE); + markupItems.add(markupItem); + + // + // verify we can apply + // + VTMarkupItemApplyActionType applyAction = validator.getApplyAction(); + if (applyAction != null) { + VtTask task = new ApplyMarkupItemTask(session, markupItems, validator.getOptions()); + runTask(session, task); + VTMarkupItemStatus expectedStatus = applyAction.getApplyStatus(); + VTMarkupItemStatus actualStatus = markupItem.getStatus(); + assertEquals("The markup item status was not correctly set", expectedStatus, + actualStatus); + validator.assertApplied(); + } + else { + fail(); + } + } + + private void doTestApply_NoMarkup() throws Exception { + + session = createNewSession(); + match = createMatchSetWithOneMatch(session, validator.getSourceMatchAddress(), + validator.getDestinationMatchAddress()); + markupItem = validator.searchForMarkupItem(match); + + // + // verify that there isn't a markup item + // + assertNull(markupItem); + } + + protected void doTestApply_NoEffect() throws Exception { + + session = createNewSession(); + match = createMatchSetWithOneMatch(session, validator.getSourceMatchAddress(), + validator.getDestinationMatchAddress()); + markupItem = validator.searchForMarkupItem(match); + + // + // verify we cannot unapply before we have applied + // + List markupItems = new ArrayList<>(); + Address destinationApplyAddress = validator.getDestinationApplyAddress(); + markupItem.setDefaultDestinationAddress(destinationApplyAddress, TEST_ADDRESS_SOURCE); + markupItems.add(markupItem); + + VtTask task = new UnapplyMarkupItemTask(session, null, markupItems); + runTask(session, task); + + // + // verify we can apply + // + VTMarkupItemApplyActionType applyAction = validator.getApplyAction(); + if (applyAction != null) { + task = new ApplyMarkupItemTask(session, markupItems, validator.getOptions()); + runTask(session, task); + assertEquals("The markup item was applied when it should not have been.", + VTMarkupItemStatus.UNAPPLIED, markupItem.getStatus()); + } + + } + + private void clearFunctionName(Function f) { + + Symbol s = f.getSymbol(); + Address addr = f.getEntryPoint(); + DeleteLabelCmd cmd = new DeleteLabelCmd(addr, s.getName(), s.getParentNamespace()); + assertTrue(applyCmd(sourceProgram, cmd)); + } + + private void setName(Function f, String name) { + Program p = f.getProgram(); + tx(p, () -> { + f.setName(name, SourceType.DEFAULT); + }); + } + + private void setNamespace(Function f, String namespacePath) { + + Program p = f.getProgram(); + tx(p, () -> { + Namespace globalNamespace = p.getGlobalNamespace(); + Namespace ns = + NamespaceUtils.createNamespaceHierarchy(namespacePath, globalNamespace, p, + SourceType.USER_DEFINED); + f.setParentNamespace(ns); + }); + } + +//================================================================================================== +// Inner Classes +//================================================================================================== + + /** Simple object to allow the source and destination functions to be setup differently */ + private class SetUp { + private Callback callback; + + SetUp(Callback c) { + this.callback = c; + } + + void run() { + callback.call(); + } + } + + private class FunctionNameValidator extends TestDataProviderAndValidator { + + private Function sourceFunction; + private Function destinationFunction; + private FunctionNameChoices functionNameChoice; + + private String destinationOriginalName; + + FunctionNameValidator(Function sourceFunction, Function destinationFunction, + FunctionNameChoices functionNameChoice) { + + this.sourceFunction = sourceFunction; + this.destinationFunction = destinationFunction; + this.destinationOriginalName = destinationFunction.getName(true); + this.functionNameChoice = functionNameChoice; + } + + @Override + protected Address getDestinationApplyAddress() { + return getDestinationMatchAddress(); + } + + @Override + public ToolOptions getOptions() { + ToolOptions vtOptions = super.getOptions(); + vtOptions.setEnum(FUNCTION_NAME, functionNameChoice); + + return vtOptions; + } + + @Override + protected VTMarkupItemApplyActionType getApplyAction() { + if (functionNameChoice == FunctionNameChoices.EXCLUDE) { + return null; + } + if (functionNameChoice == FunctionNameChoices.ADD || + functionNameChoice == FunctionNameChoices.ADD_AS_PRIMARY) { + return ADD; + } + return REPLACE; + } + + @Override + protected Address getDestinationMatchAddress() { + return destinationFunction.getEntryPoint(); + } + + @Override + protected Address getSourceMatchAddress() { + return sourceFunction.getEntryPoint(); + } + + @Override + protected VTMarkupItem searchForMarkupItem(VTMatch vtMatch) throws Exception { + List items = + FunctionNameMarkupType.INSTANCE.createMarkupItems(vtMatch.getAssociation()); + if (items.isEmpty()) { + return null; // no markup items + } + VTMarkupItem item = items.get(0); + + // we have to set the source stringable value to prevent potential name collisions + updateSourceName(); + + return item; + } + + private void updateSourceName() { + String sourceName = sourceFunction.getName(); + tx(sourceProgram, () -> { + sourceFunction.setName(sourceName, SourceType.USER_DEFINED); + }); + } + + @Override + protected void assertApplied() { + // the tests check their own assertions + } + + private boolean isDefaultFunctionName(String functionName, Function function) { + String defaultFunctionName = + SymbolUtilities.getDefaultFunctionName(function.getEntryPoint()); + return defaultFunctionName.equals(functionName); + } + + @Override + protected void assertUnapplied() { + + String destName = destinationFunction.getName(true); + assertEquals("Function name was not unapplied", destinationOriginalName, destName); + + if (functionNameChoice == FunctionNameChoices.ADD) { + + if (!isDefaultFunctionName(destinationOriginalName, destinationFunction)) { + Program p = destinationFunction.getProgram(); + SymbolTable st = p.getSymbolTable(); + Address addr = getDestinationMatchAddress(); + String sourceName = sourceFunction.getName(); + Symbol sourceSymbol = st.getGlobalSymbol(sourceName, addr); + assertNull(sourceSymbol); + } + } + else if (functionNameChoice == FunctionNameChoices.ADD_AS_PRIMARY) { + // don't think there is anything to test here, since the fact that the function name + // has been restored means that the old source symbol that was made primary is gone. + } + } + } + +} diff --git a/Ghidra/Features/VersionTracking/src/test.slow/java/ghidra/feature/vt/api/markupitem/FunctionNameMarkupItemTest.java b/Ghidra/Features/VersionTracking/src/test.slow/java/ghidra/feature/vt/api/markupitem/FunctionNameMarkupItemTest.java index 7f4ff751d8..3112d5f529 100644 --- a/Ghidra/Features/VersionTracking/src/test.slow/java/ghidra/feature/vt/api/markupitem/FunctionNameMarkupItemTest.java +++ b/Ghidra/Features/VersionTracking/src/test.slow/java/ghidra/feature/vt/api/markupitem/FunctionNameMarkupItemTest.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -15,10 +15,9 @@ */ package ghidra.feature.vt.api.markupitem; -import static ghidra.feature.vt.api.main.VTMarkupItemApplyActionType.ADD; -import static ghidra.feature.vt.api.main.VTMarkupItemApplyActionType.REPLACE; -import static ghidra.feature.vt.db.VTTestUtils.addr; -import static ghidra.feature.vt.gui.util.VTOptionDefines.FUNCTION_NAME; +import static ghidra.feature.vt.api.main.VTMarkupItemApplyActionType.*; +import static ghidra.feature.vt.db.VTTestUtils.*; +import static ghidra.feature.vt.gui.util.VTOptionDefines.*; import static org.junit.Assert.*; import java.util.List; @@ -26,6 +25,7 @@ import java.util.List; import org.junit.Assert; import org.junit.Test; +import ghidra.app.util.NamespaceUtils; import ghidra.feature.vt.api.main.*; import ghidra.feature.vt.api.markuptype.FunctionNameMarkupType; import ghidra.feature.vt.gui.util.VTMatchApplyChoices.FunctionNameChoices; @@ -34,19 +34,15 @@ import ghidra.program.model.address.Address; import ghidra.program.model.data.DWordDataType; import ghidra.program.model.data.DataType; import ghidra.program.model.listing.*; +import ghidra.program.model.listing.Function.FunctionUpdateType; import ghidra.program.model.symbol.*; -import ghidra.util.SystemUtilities; import ghidra.util.exception.DuplicateNameException; import ghidra.util.exception.InvalidInputException; public class FunctionNameMarkupItemTest extends AbstractVTMarkupItemTest { - public FunctionNameMarkupItemTest() { - super(); - } - @Test - public void testFindAndApplyMarkupItem_ReplaceDefault_WithDefaultDestinationName() + public void testReplaceDefault_DefaultDestinationName() throws Exception { Address sourceAddress = addr("0x01002cf5", sourceProgram); @@ -57,17 +53,14 @@ public class FunctionNameMarkupItemTest extends AbstractVTMarkupItemTest { FunctionManager destinationFunctionManager = destinationProgram.getFunctionManager(); Function destinationFunction = destinationFunctionManager.getFunctionAt(destinationAddress); - String sourceName = sourceFunction.getName() + getNonDynamicName(); - String destinationName = destinationFunction.getName(); - FunctionNameValidator validator = - new FunctionNameValidator(sourceFunction, destinationFunction, sourceName, - destinationName, FunctionNameChoices.REPLACE_DEFAULT_ONLY); + new FunctionNameValidator(sourceFunction, destinationFunction, + FunctionNameChoices.REPLACE_DEFAULT_ONLY); doTestFindAndApplyMarkupItem(validator); } @Test - public void testApplyFunctionName_ReplaceDefault_WithNonDefaultDestinationName() + public void testReplaceDefault_NonDefaultDestinationName() throws Exception { Address sourceAddress = addr("0x01002cf5", sourceProgram); FunctionManager sourceFunctionManager = sourceProgram.getFunctionManager(); @@ -79,30 +72,15 @@ public class FunctionNameMarkupItemTest extends AbstractVTMarkupItemTest { String sourceName = sourceFunction.getName() + getNonDynamicName(); setFunctionName(sourceFunction, sourceName); - String destinationName = destinationFunction.getName(); FunctionNameValidator validator = - new FunctionNameValidator(sourceFunction, destinationFunction, sourceName, - destinationName, FunctionNameChoices.REPLACE_DEFAULT_ONLY); + new FunctionNameValidator(sourceFunction, destinationFunction, + FunctionNameChoices.REPLACE_DEFAULT_ONLY); doTestFindAndApplyMarkupItem_NoEffect(validator); } - private void setFunctionName(Function sourceFunction, String sourceName) { - boolean commit = true; - int txID = sourceProgram.startTransaction("Change Source Function Name"); - try { - sourceFunction.setName(sourceName, sourceFunction.getSymbol().getSource()); - } - catch (Exception e) { - commit = false; - } - finally { - sourceProgram.endTransaction(txID, commit); - } - } - @Test - public void testFindAndApplyMarkupItem_ReplaceAlways_WithExistingDuplicateDestinationName() + public void testReplaceAlways_WithExistingDuplicateDestinationName() throws Exception { Address sourceAddress = addr("0x01002cf5", sourceProgram); FunctionManager sourceFunctionManager = sourceProgram.getFunctionManager(); @@ -112,16 +90,13 @@ public class FunctionNameMarkupItemTest extends AbstractVTMarkupItemTest { FunctionManager destinationFunctionManager = destinationProgram.getFunctionManager(); Function destinationFunction = destinationFunctionManager.getFunctionAt(destinationAddress); - String sourceName = sourceFunction.getName(); - String destinationName = destinationFunction.getName(); - FunctionNameValidator validator = new FunctionNameValidator(sourceFunction, - destinationFunction, sourceName, destinationName, FunctionNameChoices.REPLACE_ALWAYS); + destinationFunction, FunctionNameChoices.REPLACE_ALWAYS); doTestFindAndApplyMarkupItem(validator); } @Test - public void testApplyFunctionName_ReplaceAlways_WithNewName() throws Exception { + public void testReplaceAlways_WithNewName() throws Exception { Address sourceAddress = addr("0x01002cf5", sourceProgram); FunctionManager sourceFunctionManager = sourceProgram.getFunctionManager(); Function sourceFunction = sourceFunctionManager.getFunctionAt(sourceAddress); @@ -130,52 +105,66 @@ public class FunctionNameMarkupItemTest extends AbstractVTMarkupItemTest { FunctionManager destinationFunctionManager = destinationProgram.getFunctionManager(); Function destinationFunction = destinationFunctionManager.getFunctionAt(destinationAddress); - String sourceName = sourceFunction.getName() + getNonDynamicName(); - String destinationName = destinationFunction.getName(); - FunctionNameValidator validator = new FunctionNameValidator(sourceFunction, - destinationFunction, sourceName, destinationName, FunctionNameChoices.REPLACE_ALWAYS); + destinationFunction, FunctionNameChoices.REPLACE_ALWAYS); doTestFindAndApplyMarkupItem(validator); } @Test - public void testApplyFunctionName_Add_WithNewName() throws Exception { + public void testReplaceAlways_DifferentName_DifferentNamespace() + throws Exception { + Address sourceAddress = addr("0x01002cf5", sourceProgram); FunctionManager sourceFunctionManager = sourceProgram.getFunctionManager(); Function sourceFunction = sourceFunctionManager.getFunctionAt(sourceAddress); - Address destinationAddress = addr("0x0100415a", destinationProgram); + Address destinationAddress = addr("0x010048a3", destinationProgram); FunctionManager destinationFunctionManager = destinationProgram.getFunctionManager(); Function destinationFunction = destinationFunctionManager.getFunctionAt(destinationAddress); - String sourceName = sourceFunction.getName() + getNonDynamicName(); - String destinationName = destinationFunction.getName(); + setNamespace(sourceFunction, "Source::Foo::Bar"); + setNamespace(destinationFunction, "Destination::Baz"); FunctionNameValidator validator = new FunctionNameValidator(sourceFunction, - destinationFunction, sourceName, destinationName, FunctionNameChoices.ADD); + destinationFunction, FunctionNameChoices.REPLACE_ALWAYS); + + ToolOptions options = validator.getOptions(); + options.setBoolean(USE_NAMESPACE_FUNCTIONS, false); + + // this method will use the options to ensure that the namespace has been applied doTestFindAndApplyMarkupItem(validator); } @Test - public void testApplyFunctionName_Add_WithNewName_MyPrimary() throws Exception { + public void testReplaceAlways_SameName_DifferentNamespace() throws Exception { + Address sourceAddress = addr("0x01002cf5", sourceProgram); FunctionManager sourceFunctionManager = sourceProgram.getFunctionManager(); Function sourceFunction = sourceFunctionManager.getFunctionAt(sourceAddress); - Address destinationAddress = addr("0x0100415a", destinationProgram); + Address destinationAddress = addr("0x010048a3", destinationProgram); FunctionManager destinationFunctionManager = destinationProgram.getFunctionManager(); Function destinationFunction = destinationFunctionManager.getFunctionAt(destinationAddress); - String sourceName = sourceFunction.getName() + getNonDynamicName(); - String destinationName = destinationFunction.getName(); + setName(destinationFunction, sourceFunction.getName()); + + setNamespace(sourceFunction, "Source::Foo::Bar"); + setNamespace(destinationFunction, "Destination::Baz"); FunctionNameValidator validator = new FunctionNameValidator(sourceFunction, - destinationFunction, sourceName, destinationName, FunctionNameChoices.ADD_AS_PRIMARY); + destinationFunction, FunctionNameChoices.REPLACE_ALWAYS); + + ToolOptions options = validator.getOptions(); + options.setBoolean(USE_NAMESPACE_FUNCTIONS, true); + + // this method will use the options to ensure that the namespace has been applied doTestFindAndApplyMarkupItem(validator); } @Test - public void testApplyFunctionName_Add_WithDefault() throws Exception { + public void testReplaceAlways_DifferentNamespace_WithDefaultDestinationName() + throws Exception { + Address sourceAddress = addr("0x01002cf5", sourceProgram); FunctionManager sourceFunctionManager = sourceProgram.getFunctionManager(); Function sourceFunction = sourceFunctionManager.getFunctionAt(sourceAddress); @@ -184,16 +173,22 @@ public class FunctionNameMarkupItemTest extends AbstractVTMarkupItemTest { FunctionManager destinationFunctionManager = destinationProgram.getFunctionManager(); Function destinationFunction = destinationFunctionManager.getFunctionAt(destinationAddress); - String sourceName = sourceFunction.getName() + getNonDynamicName(); - String destinationName = destinationFunction.getName(); + setNamespace(sourceFunction, "Source"); + setNamespace(destinationFunction, "Destination"); + + FunctionNameValidator validator = + new FunctionNameValidator(sourceFunction, destinationFunction, + FunctionNameChoices.REPLACE_ALWAYS); + + // do not apply the namespace + ToolOptions options = validator.getOptions(); + options.setBoolean(USE_NAMESPACE_FUNCTIONS, false); - FunctionNameValidator validator = new FunctionNameValidator(sourceFunction, - destinationFunction, sourceName, destinationName, FunctionNameChoices.ADD); doTestFindAndApplyMarkupItem(validator); } @Test - public void testApplyFunctionName_IgnoreAction() throws Exception { + public void testAdd_WithNewName() throws Exception { Address sourceAddress = addr("0x01002cf5", sourceProgram); FunctionManager sourceFunctionManager = sourceProgram.getFunctionManager(); Function sourceFunction = sourceFunctionManager.getFunctionAt(sourceAddress); @@ -202,18 +197,61 @@ public class FunctionNameMarkupItemTest extends AbstractVTMarkupItemTest { FunctionManager destinationFunctionManager = destinationProgram.getFunctionManager(); Function destinationFunction = destinationFunctionManager.getFunctionAt(destinationAddress); - String sourceName = sourceFunction.getName() + getNonDynamicName(); - String destinationName = destinationFunction.getName(); - FunctionNameValidator validator = new FunctionNameValidator(sourceFunction, - destinationFunction, sourceName, destinationName, FunctionNameChoices.EXCLUDE); + destinationFunction, FunctionNameChoices.ADD); doTestFindAndApplyMarkupItem(validator); } -////// External Function Name Tests ////// + @Test + public void testAdd_WithNewName_DifferentNamespace() throws Exception { + Address sourceAddress = addr("0x01002cf5", sourceProgram); + FunctionManager sourceFunctionManager = sourceProgram.getFunctionManager(); + Function sourceFunction = sourceFunctionManager.getFunctionAt(sourceAddress); + + Address destinationAddress = addr("0x0100415a", destinationProgram); + FunctionManager destinationFunctionManager = destinationProgram.getFunctionManager(); + Function destinationFunction = destinationFunctionManager.getFunctionAt(destinationAddress); + + setNamespace(sourceFunction, "Source::Foo::Bar"); + setNamespace(destinationFunction, "Destination::Baz"); + + FunctionNameValidator validator = new FunctionNameValidator(sourceFunction, + destinationFunction, FunctionNameChoices.ADD); + doTestFindAndApplyMarkupItem(validator); + } @Test - public void testFindAndApplyExternalMarkupItem_ReplaceDefault_WithDefaultDestinationName() + public void testAdd_WithNewName_MyPrimary() throws Exception { + Address sourceAddress = addr("0x01002cf5", sourceProgram); + FunctionManager sourceFunctionManager = sourceProgram.getFunctionManager(); + Function sourceFunction = sourceFunctionManager.getFunctionAt(sourceAddress); + + Address destinationAddress = addr("0x0100415a", destinationProgram); + FunctionManager destinationFunctionManager = destinationProgram.getFunctionManager(); + Function destinationFunction = destinationFunctionManager.getFunctionAt(destinationAddress); + + FunctionNameValidator validator = new FunctionNameValidator(sourceFunction, + destinationFunction, FunctionNameChoices.ADD_AS_PRIMARY); + doTestFindAndApplyMarkupItem(validator); + } + + @Test + public void testAdd_WithDefault() throws Exception { + Address sourceAddress = addr("0x01002cf5", sourceProgram); + FunctionManager sourceFunctionManager = sourceProgram.getFunctionManager(); + Function sourceFunction = sourceFunctionManager.getFunctionAt(sourceAddress); + + Address destinationAddress = addr("0x01003f9e", destinationProgram); + FunctionManager destinationFunctionManager = destinationProgram.getFunctionManager(); + Function destinationFunction = destinationFunctionManager.getFunctionAt(destinationAddress); + + FunctionNameValidator validator = new FunctionNameValidator(sourceFunction, + destinationFunction, FunctionNameChoices.ADD); + doTestFindAndApplyMarkupItem(validator); + } + + @Test + public void testExternal_ReplaceDefault_WithDefaultDestinationName() throws Exception { Function addedSourceFunction = @@ -228,17 +266,14 @@ public class FunctionNameMarkupItemTest extends AbstractVTMarkupItemTest { assertEquals(addedSourceFunction.getName(), sourceFunction.getName()); assertEquals(addedDestinationFunction.getName(), destinationFunction.getName()); - String sourceName = sourceFunction.getName(); - String destinationName = destinationFunction.getName(); - FunctionNameValidator validator = - new FunctionNameValidator(sourceFunction, destinationFunction, sourceName, - destinationName, FunctionNameChoices.REPLACE_DEFAULT_ONLY); + new FunctionNameValidator(sourceFunction, destinationFunction, + FunctionNameChoices.REPLACE_DEFAULT_ONLY); doTestFindAndApplyMarkupItem(validator); } @Test - public void testApplyExternalFunctionName_ReplaceDefault_WithNonDefaultDestinationName() + public void testExternal_ReplaceDefault_WithNonDefaultDestinationName() throws Exception { Function addedSourceFunction = addExternalFunction(sourceProgram, "Modify Source Program", "apples"); @@ -252,17 +287,14 @@ public class FunctionNameMarkupItemTest extends AbstractVTMarkupItemTest { assertEquals(addedSourceFunction.getName(), sourceFunction.getName()); assertEquals(addedDestinationFunction.getName(), destinationFunction.getName()); - String sourceName = sourceFunction.getName(); - String destinationName = destinationFunction.getName(); - FunctionNameValidator validator = - new FunctionNameValidator(sourceFunction, destinationFunction, sourceName, - destinationName, FunctionNameChoices.REPLACE_DEFAULT_ONLY); + new FunctionNameValidator(sourceFunction, destinationFunction, + FunctionNameChoices.REPLACE_DEFAULT_ONLY); doTestFindAndApplyMarkupItem_NoEffect(validator); } @Test - public void testFindAndApplyExternalMarkupItem_ReplaceAlways_WithExistingDuplicateDestinationName() + public void testExternal_ReplaceAlways_WithExistingDuplicateDestinationName() throws Exception { Function addedSourceFunction = addExternalFunction(sourceProgram, "Modify Source Program", "apples"); @@ -270,24 +302,14 @@ public class FunctionNameMarkupItemTest extends AbstractVTMarkupItemTest { addExternalFunction(destinationProgram, "Modify Destination Program", "oranges"); // Make a duplicate named external function. - Function duplicateApplesFunction = null; - int txId = destinationProgram.startTransaction("Modify Destination Program"); - boolean commit = false; - try { - duplicateApplesFunction = + tx(destinationProgram, () -> { + Function applesFunction = createExternalFunction(destinationProgram, new String[] { "user32.dll", "apples" }); - addStackParameter(duplicateApplesFunction, "P1", SourceType.USER_DEFINED, + addStackParameter(applesFunction, "P1", SourceType.USER_DEFINED, new DWordDataType(), 4, "Test Parameter Comment"); - addStackParameter(duplicateApplesFunction, "P2", SourceType.USER_DEFINED, + addStackParameter(applesFunction, "P2", SourceType.USER_DEFINED, new DWordDataType(), 8, "Test Parameter Comment"); - commit = true; - } - catch (Exception e) { - Assert.fail(e.getMessage()); - } - finally { - destinationProgram.endTransaction(txId, commit); - } + }); // Check the function just created. Function sourceFunction = getExternalFunction(sourceProgram, addedSourceFunction.getName()); @@ -296,16 +318,13 @@ public class FunctionNameMarkupItemTest extends AbstractVTMarkupItemTest { assertEquals(addedSourceFunction.getName(), sourceFunction.getName()); assertEquals(addedDestinationFunction.getName(), destinationFunction.getName()); - String sourceName = sourceFunction.getName(); - String destinationName = destinationFunction.getName(); - FunctionNameValidator validator = new FunctionNameValidator(sourceFunction, - destinationFunction, sourceName, destinationName, FunctionNameChoices.REPLACE_ALWAYS); + destinationFunction, FunctionNameChoices.REPLACE_ALWAYS); doTestFindAndApplyMarkupItem(validator); } @Test - public void testApplyExternalFunctionName_ReplaceAlways_WithNewName() throws Exception { + public void testExternal_ReplaceAlways_WithNewName() throws Exception { Function addedSourceFunction = addExternalFunction(sourceProgram, "Modify Source Program", "apples", "origina-apples"); Function addedDestinationFunction = addExternalFunction(destinationProgram, @@ -318,16 +337,13 @@ public class FunctionNameMarkupItemTest extends AbstractVTMarkupItemTest { assertEquals(addedSourceFunction.getName(), sourceFunction.getName()); assertEquals(addedDestinationFunction.getName(), destinationFunction.getName()); - String sourceName = sourceFunction.getName(); - String destinationName = destinationFunction.getName(); - FunctionNameValidator validator = new FunctionNameValidator(sourceFunction, - destinationFunction, sourceName, destinationName, FunctionNameChoices.REPLACE_ALWAYS); + destinationFunction, FunctionNameChoices.REPLACE_ALWAYS); doTestFindAndApplyMarkupItem(validator); } @Test - public void testApplyExternalFunctionName_Add_WithNewName() throws Exception { + public void testExternal_Add_WithNewName() throws Exception { Function addedSourceFunction = addExternalFunction(sourceProgram, "Modify Source Program", "apples"); Function addedDestinationFunction = @@ -340,16 +356,13 @@ public class FunctionNameMarkupItemTest extends AbstractVTMarkupItemTest { assertEquals(addedSourceFunction.getName(), sourceFunction.getName()); assertEquals(addedDestinationFunction.getName(), destinationFunction.getName()); - String sourceName = sourceFunction.getName(); - String destinationName = destinationFunction.getName(); - FunctionNameValidator validator = new FunctionNameValidator(sourceFunction, - destinationFunction, sourceName, destinationName, FunctionNameChoices.ADD); + destinationFunction, FunctionNameChoices.ADD); doTestFindAndApplyMarkupItem_ApplyFails(validator); } @Test - public void testApplyExternalFunctionName_Add_WithNewName_MyPrimary() throws Exception { + public void testExternal_Add_WithNewName_MyPrimary() throws Exception { Function addedSourceFunction = addExternalFunction(sourceProgram, "Modify Source Program", "apples"); Function addedDestinationFunction = @@ -362,16 +375,13 @@ public class FunctionNameMarkupItemTest extends AbstractVTMarkupItemTest { assertEquals(addedSourceFunction.getName(), sourceFunction.getName()); assertEquals(addedDestinationFunction.getName(), destinationFunction.getName()); - String sourceName = sourceFunction.getName(); - String destinationName = destinationFunction.getName(); - FunctionNameValidator validator = new FunctionNameValidator(sourceFunction, - destinationFunction, sourceName, destinationName, FunctionNameChoices.ADD_AS_PRIMARY); + destinationFunction, FunctionNameChoices.ADD_AS_PRIMARY); doTestFindAndApplyMarkupItem_ApplyFails(validator); } @Test - public void testApplyExternalFunctionName_Add_WithDefault() throws Exception { + public void testExternal_Add_WithDefault() throws Exception { Function addedSourceFunction = addExternalFunction(sourceProgram, "Modify Source Program", "oranges"); Function addedDestinationFunction = addDefaultExternalFunction(destinationProgram, @@ -384,16 +394,28 @@ public class FunctionNameMarkupItemTest extends AbstractVTMarkupItemTest { assertEquals(addedSourceFunction.getName(), sourceFunction.getName()); assertEquals(addedDestinationFunction.getName(), destinationFunction.getName()); - String sourceName = sourceFunction.getName(); - String destinationName = destinationFunction.getName(); - FunctionNameValidator validator = new FunctionNameValidator(sourceFunction, - destinationFunction, sourceName, destinationName, FunctionNameChoices.ADD); + destinationFunction, FunctionNameChoices.ADD); doTestFindAndApplyMarkupItem_ApplyFails(validator); } @Test - public void testApplyExternalFunctionName_IgnoreAction() throws Exception { + public void testIgnoreAction() throws Exception { + Address sourceAddress = addr("0x01002cf5", sourceProgram); + FunctionManager sourceFunctionManager = sourceProgram.getFunctionManager(); + Function sourceFunction = sourceFunctionManager.getFunctionAt(sourceAddress); + + Address destinationAddress = addr("0x0100415a", destinationProgram); + FunctionManager destinationFunctionManager = destinationProgram.getFunctionManager(); + Function destinationFunction = destinationFunctionManager.getFunctionAt(destinationAddress); + + FunctionNameValidator validator = new FunctionNameValidator(sourceFunction, + destinationFunction, FunctionNameChoices.EXCLUDE); + doTestFindAndApplyMarkupItem(validator); + } + + @Test + public void testExternal_IgnoreAction() throws Exception { Function addedSourceFunction = addExternalFunction(sourceProgram, "Modify Source Program", "apples"); Function addedDestinationFunction = @@ -406,11 +428,8 @@ public class FunctionNameMarkupItemTest extends AbstractVTMarkupItemTest { assertEquals(addedSourceFunction.getName(), sourceFunction.getName()); assertEquals(addedDestinationFunction.getName(), destinationFunction.getName()); - String sourceName = sourceFunction.getName(); - String destinationName = destinationFunction.getName(); - FunctionNameValidator validator = new FunctionNameValidator(sourceFunction, - destinationFunction, sourceName, destinationName, FunctionNameChoices.EXCLUDE); + destinationFunction, FunctionNameChoices.EXCLUDE); doTestFindAndApplyMarkupItem(validator); } @@ -420,26 +439,25 @@ public class FunctionNameMarkupItemTest extends AbstractVTMarkupItemTest { private class FunctionNameValidator extends TestDataProviderAndValidator { - private String sourceName; - private String sourceOriginalName; - private String destinationName; - private String destinationOriginalName; private Function sourceFunction; private Function destinationFunction; private FunctionNameChoices functionNameChoice; + private String destinationOriginalName; + private String destinationOriginalExternalName; + private String destinationOriginalNamespace; + FunctionNameValidator(Function sourceFunction, Function destinationFunction, - String sourceName, String destinationName, FunctionNameChoices functionNameChoice) { + FunctionNameChoices functionNameChoice) { this.sourceFunction = sourceFunction; this.destinationFunction = destinationFunction; - this.destinationName = destinationName; - this.sourceName = sourceName; + this.destinationOriginalName = destinationFunction.getName(); + this.destinationOriginalNamespace = destinationFunction.getParentNamespace().toString(); if (sourceFunction.isExternal()) { assertTrue("Expected both functions to be external", destinationFunction.isExternal()); - sourceOriginalName = sourceFunction.getExternalLocation().getOriginalImportedName(); - destinationOriginalName = + destinationOriginalExternalName = destinationFunction.getExternalLocation().getOriginalImportedName(); } this.functionNameChoice = functionNameChoice; @@ -494,88 +512,111 @@ public class FunctionNameMarkupItemTest extends AbstractVTMarkupItemTest { } private void updateSourceName() { - int id = sourceFunction.getProgram().startTransaction("update name"); - try { + String sourceName = sourceFunction.getName(); + tx(sourceProgram, () -> { sourceFunction.setName(sourceName, SourceType.USER_DEFINED); - } - catch (Exception e) { - throw new RuntimeException("Update source name failed: " + e.getMessage()); - } - finally { - sourceFunction.getProgram().endTransaction(id, true); - } - + }); } @Override protected void assertApplied() { + + boolean useNamespace = options.getBoolean(USE_NAMESPACE_FUNCTIONS, false); + String destinationName = destinationOriginalName; + + String sourceName = sourceFunction.getName(useNamespace); + String appliedDestinationName = destinationFunction.getName(useNamespace); + boolean sourceIsDefault = isDefaultFunctionName(sourceName, sourceFunction); boolean destinationIsDefault = isDefaultFunctionName(destinationName, destinationFunction); + SymbolTable symbolTable = destinationFunction.getProgram().getSymbolTable(); if (functionNameChoice == FunctionNameChoices.ADD_AS_PRIMARY) { if (!sourceIsDefault) { assertEquals("Function name was not applied", sourceName, - destinationFunction.getName()); + appliedDestinationName); if (!destinationIsDefault) { - Symbol otherSymbol = symbolTable.getGlobalSymbol(destinationName, - getDestinationMatchAddress()); - assertNotNull(otherSymbol); - assertEquals(SymbolType.LABEL, otherSymbol.getSymbolType()); + Address addr = getDestinationApplyAddress(); + Symbol symbol = getSymbol(symbolTable, destinationName, addr); + assertNotNull(symbol); + assertEquals(SymbolType.LABEL, symbol.getSymbolType()); assertEquals("Additional label was not applied", destinationName, - otherSymbol.getName()); + symbol.getName()); } } else if (!destinationIsDefault) { assertEquals("Function name should not have been applied", destinationName, - destinationFunction.getName()); - Symbol destinationSymbol = - symbolTable.getGlobalSymbol(destinationName, getDestinationMatchAddress()); - assertNotNull("Expected an additional label", destinationSymbol); - assertEquals(SymbolType.LABEL, destinationSymbol.getSymbolType()); + appliedDestinationName); + Address addr = getDestinationMatchAddress(); + Symbol symbol = getSymbol(symbolTable, destinationName, addr); + assertNotNull("Expected an additional label", symbol); + assertEquals(SymbolType.LABEL, symbol.getSymbolType()); } } else if (functionNameChoice == FunctionNameChoices.ADD) { if (!destinationIsDefault) { assertEquals("Function name was improperly added", destinationName, - destinationFunction.getName()); + appliedDestinationName); if (!sourceIsDefault) { - Symbol otherSymbol = - symbolTable.getGlobalSymbol(sourceName, getDestinationMatchAddress()); - assertNotNull(otherSymbol); - assertEquals(SymbolType.LABEL, otherSymbol.getSymbolType()); + Address addr = getDestinationMatchAddress(); + Symbol symbol = getSymbol(symbolTable, sourceName, addr); + assertNotNull(symbol); + assertEquals(SymbolType.LABEL, symbol.getSymbolType()); } } else if (!sourceIsDefault) { assertEquals("Function name should have been applied", sourceName, - destinationFunction.getName()); - Symbol destinationSymbol = - symbolTable.getGlobalSymbol(destinationName, getDestinationMatchAddress()); - assertNull("Additional label was unexpected", destinationSymbol); + appliedDestinationName); + Address addr = getDestinationMatchAddress(); + Symbol symbol = getSymbol(symbolTable, destinationName, addr); + assertNull("Additional label was unexpected", symbol); } } else if (functionNameChoice == FunctionNameChoices.REPLACE_ALWAYS) { - // TODO: Was not implemented ?? Should it be ?? + if (sourceIsDefault) { assertEquals("Function name was improperly renamed", destinationName, - destinationFunction.getName()); + appliedDestinationName); if (destinationFunction.isExternal()) { - assertTrue("External Function original name was improperly renamed", - SystemUtilities.isEqual( - destinationFunction.getExternalLocation().getOriginalImportedName(), - destinationOriginalName)); + ExternalLocation externalLocation = + destinationFunction.getExternalLocation(); + String originalImportedName = externalLocation.getOriginalImportedName(); + assertEquals("External Function original name was improperly renamed", + originalImportedName, destinationOriginalExternalName); } } else { + assertEquals("Function name should have been applied", sourceName, - destinationFunction.getName()); -// if (destinationFunction.isExternal()) { -// // original name may get set if symbol type was IMPORTED -// } + appliedDestinationName); + + String sourceNs = sourceFunction.getParentNamespace().toString(); + if (useNamespace) { + String destNs = destinationFunction.getParentNamespace().toString(); + assertEquals(sourceNs, destNs); + } + else { + String currentDestNs = destinationFunction.getParentNamespace().toString(); + assertEquals(destinationOriginalNamespace, currentDestNs); + } + } } } + private Symbol getSymbol(SymbolTable symbolTable, String name, Address addr) { + + Symbol[] symbols = symbolTable.getSymbols(addr); + for (Symbol s : symbols) { + String symbolName = s.getName(); + if (symbolName.equals(name)) { + return s; + } + } + return null; + } + private boolean isDefaultFunctionName(String functionName, Function function) { String defaultFunctionName = SymbolUtilities.getDefaultFunctionName(function.getEntryPoint()); @@ -584,13 +625,18 @@ public class FunctionNameMarkupItemTest extends AbstractVTMarkupItemTest { @Override protected void assertUnapplied() { - assertEquals("Function name was not unapplied", destinationName, + + String sourceName = sourceFunction.getName(); + + assertEquals("Function name was not unapplied", destinationOriginalName, destinationFunction.getName()); if (functionNameChoice == FunctionNameChoices.ADD && - !isDefaultFunctionName(destinationName, destinationFunction)) { + !isDefaultFunctionName(destinationOriginalName, destinationFunction)) { Symbol sourceSymbol = - destinationFunction.getProgram().getSymbolTable().getGlobalSymbol(sourceName, - getDestinationMatchAddress()); + destinationFunction.getProgram() + .getSymbolTable() + .getGlobalSymbol(sourceName, + getDestinationMatchAddress()); assertNull(sourceSymbol); } } @@ -694,7 +740,10 @@ public class FunctionNameMarkupItemTest extends AbstractVTMarkupItemTest { Parameter parameter1 = new ParameterImpl(name, dataType, stackOffset, function.getProgram()); parameter1.setComment(comment); - return function.addParameter(parameter1, sourceType); + + function.updateFunction(null, null, FunctionUpdateType.DYNAMIC_STORAGE_FORMAL_PARAMS, + true, sourceType, parameter1); + return function.getParameter(0); } void checkDataType(DataType expectedDataType, DataType actualDataType) { @@ -703,6 +752,20 @@ public class FunctionNameMarkupItemTest extends AbstractVTMarkupItemTest { assertTrue(failureMessage, expectedDataType.isEquivalent(actualDataType)); } + private void setFunctionName(Function sourceFunction, String sourceName) { + boolean commit = true; + int txID = sourceProgram.startTransaction("Change Source Function Name"); + try { + sourceFunction.setName(sourceName, sourceFunction.getSymbol().getSource()); + } + catch (Exception e) { + commit = false; + } + finally { + sourceProgram.endTransaction(txID, commit); + } + } + private Function addExternalFunction(Program program, String txDescription, String functionName) { return addExternalFunction(program, txDescription, functionName, null); @@ -733,24 +796,34 @@ public class FunctionNameMarkupItemTest extends AbstractVTMarkupItemTest { private Function addDefaultExternalFunction(Program program, String txDescription, String memAddress) { - Function function = null; - int txId = program.startTransaction(txDescription); - boolean commit = false; - try { - function = createExternalFunction(program, new String[] { "user32.dll", null }, + + return tx(program, () -> { + Function function = createExternalFunction(program, new String[] { "user32.dll", null }, addr(memAddress, program)); addStackParameter(function, "P1", SourceType.USER_DEFINED, new DWordDataType(), 4, "Test Parameter Comment"); addStackParameter(function, "P2", SourceType.USER_DEFINED, new DWordDataType(), 8, "Test Parameter Comment"); - commit = true; - } - catch (Exception e) { - Assert.fail(e.getMessage()); - } - finally { - program.endTransaction(txId, commit); - } - return function; + return function; + }); + } + + private void setName(Function f, String name) { + Program p = f.getProgram(); + tx(p, () -> { + f.setName(name, SourceType.DEFAULT); + }); + } + + private void setNamespace(Function f, String namespacePath) { + + Program p = f.getProgram(); + tx(p, () -> { + Namespace globalNamespace = p.getGlobalNamespace(); + Namespace ns = + NamespaceUtils.createNamespaceHierarchy(namespacePath, globalNamespace, p, + SourceType.USER_DEFINED); + f.setParentNamespace(ns); + }); } } diff --git a/Ghidra/Features/VersionTracking/src/test/java/ghidra/feature/vt/api/markupitem/AbstractVTMarkupItemTest.java b/Ghidra/Features/VersionTracking/src/test/java/ghidra/feature/vt/api/markupitem/AbstractVTMarkupItemTest.java index 8877737571..134ebc103c 100644 --- a/Ghidra/Features/VersionTracking/src/test/java/ghidra/feature/vt/api/markupitem/AbstractVTMarkupItemTest.java +++ b/Ghidra/Features/VersionTracking/src/test/java/ghidra/feature/vt/api/markupitem/AbstractVTMarkupItemTest.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -35,6 +35,7 @@ import ghidra.program.database.ProgramDB; import ghidra.program.model.address.Address; import ghidra.program.model.address.AddressSet; import ghidra.test.*; +import ghidra.util.Msg; import ghidra.util.task.TaskMonitor; public abstract class AbstractVTMarkupItemTest extends AbstractGhidraHeadedIntegrationTest { @@ -55,6 +56,10 @@ public abstract class AbstractVTMarkupItemTest extends AbstractGhidraHeadedInteg env = new TestEnv(); setErrorGUIEnabled(false); + resetPrograms(); + } + + protected void resetPrograms() throws Exception { sourceBuilder = new ClassicSampleX86ProgramBuilder("notepadSrc", true, this); sourceProgram = sourceBuilder.getProgram(); @@ -112,6 +117,8 @@ public abstract class AbstractVTMarkupItemTest extends AbstractGhidraHeadedInteg */ protected abstract class TestDataProviderAndValidator { + protected ToolOptions options; + protected abstract Address getSourceMatchAddress(); protected abstract Address getDestinationMatchAddress(); @@ -134,6 +141,11 @@ public abstract class AbstractVTMarkupItemTest extends AbstractGhidraHeadedInteg * @return the Apply Markup Options for the validator to use. */ protected ToolOptions getOptions() { + + if (options != null) { + return options; + } + ToolOptions applyOptions = new ToolOptions(VERSION_TRACKING_OPTIONS_NAME); applyOptions.setEnum(FUNCTION_NAME, FunctionNameChoices.REPLACE_ALWAYS); @@ -165,10 +177,29 @@ public abstract class AbstractVTMarkupItemTest extends AbstractGhidraHeadedInteg applyOptions.setEnum(DATA_MATCH_DATA_TYPE, ReplaceDataChoices.REPLACE_UNDEFINED_DATA_ONLY); + + options = 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 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) throws Exception { @@ -319,7 +350,10 @@ public abstract class AbstractVTMarkupItemTest extends AbstractGhidraHeadedInteg // verify that calling apply still leaves us in an unapplied state // task = new ApplyMarkupItemTask(session, markupItems, validator.getOptions()); + + setErrorsExpected(true); runTask(session, task); + setErrorsExpected(false); assertEquals("The markup item status was not correctly set", VTMarkupItemStatus.FAILED_APPLY, markupItem.getStatus()); @@ -354,9 +388,8 @@ public abstract class AbstractVTMarkupItemTest extends AbstractGhidraHeadedInteg protected static VTMatch createMatchSetWithOneMatch(VTSessionDB db, Address sourceAddress, Address destinationAddress) throws Exception { - int testTransactionID = 0; - try { - testTransactionID = db.startTransaction("Test Match Set Setup"); + + return tx(db, () -> { VTMatchInfo matchInfo = createRandomMatch(sourceAddress, destinationAddress, db); VTMatchSet matchSet = db.createMatchSet( createProgramCorrelator(db.getSourceProgram(), db.getDestinationProgram())); @@ -368,19 +401,22 @@ public abstract class AbstractVTMarkupItemTest extends AbstractGhidraHeadedInteg association.getMarkupItems(TaskMonitor.DUMMY); 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"); try { task.run(TaskMonitor.DUMMY); } finally { session.endTransaction(id, true); + + if (task.hasErrors()) { + String errorDetails = task.getErrorDetails(); + Msg.error(this, "Error applying task: " + errorDetails); + } } } diff --git a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/VersionControlScreenShots.java b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/VersionControlScreenShots.java index 3cf825ac02..4236737e0d 100644 --- a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/VersionControlScreenShots.java +++ b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/VersionControlScreenShots.java @@ -96,6 +96,7 @@ public class VersionControlScreenShots extends GhidraScreenShotGenerator { UndoActionDialog d = waitForDialogComponent(UndoActionDialog.class); captureDialog(d); + close(d); }