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 fa3160d0bd..dd7625325f 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
@@ -458,7 +458,7 @@
- False
+ | True
|
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 e3a5d7a2a2..392b099bfc 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
@@ -279,7 +279,8 @@ public class FunctionNameMarkupType extends FunctionEntryPointBasedAbstractMarku
}
Function srcFunction = getSourceFunction(markupItem.getAssociation());
- boolean replaceNamespace = markupOptions.getBoolean(USE_NAMESPACE_FUNCTIONS, false);
+ boolean replaceNamespace = markupOptions.getBoolean(USE_NAMESPACE_FUNCTIONS,
+ DEFAULT_OPTION_FOR_NAMESPACE_FUNCTIONS);
if (!hasAnythingToApply(srcStringable, srcFunction, replaceNamespace)) {
return false;
}
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 bb234def63..a691c70a45 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
@@ -188,6 +188,7 @@ public class FunctionSignatureMarkupType extends FunctionEntryPointBasedAbstract
throw new VersionTrackingApplyException(
"Couldn't find destination function to apply a name.");
}
+
if (sourceStringable.applyFunctionSignature(destinationFunction, markupOptions, false)) {
return true;
}
diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/stringable/FunctionSignatureStringable.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/stringable/FunctionSignatureStringable.java
index 4453fe85a3..fb3d34edec 100644
--- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/stringable/FunctionSignatureStringable.java
+++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/stringable/FunctionSignatureStringable.java
@@ -546,6 +546,11 @@ public class FunctionSignatureStringable extends Stringable {
useCustomStorage ? FunctionUpdateType.CUSTOM_STORAGE
: FunctionUpdateType.DYNAMIC_STORAGE_ALL_PARAMS,
true, signatureSource);
+
+ // Test to see if destination function is now in the same namespace as the source
+ // if so copy the class structure from source to destination if the update didn't
+ copyClassStructure(newParams, toFunction, markupOptions);
+
if (forceApply) {
// must force signatureSource if precedence has been lowered
// TODO: Should any manual change in function signature force source to be USER_DEFINED instead ??
@@ -578,6 +583,195 @@ public class FunctionSignatureStringable extends Stringable {
return true;
}
+ /**
+ * Method to determine if a copy is needed of the source class struct to the destination program
+ * and if so, does the copy
+ * @param newParams the params to apply to the destination program as determined by the apply
+ * options
+ * @param toFunction the destination function
+ */
+ private void copyClassStructure(List newParams, Function toFunction,
+ ToolOptions markupOptions) {
+
+ // this is only meant to handle the case where there are auto this params
+ // existing other mechanisms handle custom storage case already
+ if (toFunction.hasCustomVariableStorage()) {
+ return;
+ }
+
+ VTMatchApplyChoices.ParameterDataTypeChoices parameterDataTypesChoice =
+ markupOptions.getEnum(VTOptionDefines.PARAMETER_DATA_TYPES,
+ DEFAULT_OPTION_FOR_PARAMETER_DATA_TYPES);
+
+ if (parameterDataTypesChoice == ParameterDataTypeChoices.EXCLUDE) {
+ return;
+ }
+
+ boolean onlyReplaceUndefineds =
+ (parameterDataTypesChoice == ParameterDataTypeChoices.REPLACE_UNDEFINED_DATA_TYPES_ONLY);
+
+ boolean replaceAlways = (parameterDataTypesChoice == ParameterDataTypeChoices.REPLACE);
+
+ // check to see if the resulting newParams after checking the markupOptions includes
+ // a this param and if so resolve the class data type in the destination program
+ // to make sure the class structure gets copied to the destination program
+
+ // if source function is not a thiscall then no class structure to copy
+ if (!callingConventionName.equals(CompilerSpec.CALLING_CONVENTION_thiscall)) {
+ return;
+ }
+ // if there are no new params to apply as determined by the options then nothing to copy
+ if (newParams.isEmpty()) {
+ return;
+ }
+
+ // if newParams does not have a this param to copy as determined by the apply options then
+ // no copy needed
+ if (!newParams.get(0).getName().equals("this")) {
+ return;
+ }
+
+ // get the this param for source and verify it is a pointer to a structure
+ ParameterInfo sourceParam1 = parameterInfos.get(0);
+ DataType sourceClassDataType = getPointedToDataType(sourceParam1.dataType);
+ if (sourceClassDataType == null) {
+ return;
+ }
+
+ if (!isStructure(sourceClassDataType)) {
+ return;
+ }
+
+ // get the pointed to data type for the new destination this param
+ // if it isn't a pointer to a data type then return
+ Parameter newDestinationParam1 = newParams.get(0);
+ DataType newDestinationClassDataType =
+ getPointedToDataType(newDestinationParam1.getDataType());
+ if (newDestinationClassDataType == null) {
+ return;
+ }
+
+ // if the new this param (ie what the calling method has determined what the new this
+ // param is supposed to be) is not a pointer to a structure then return because it
+ // doesn't think it should also be a class structure
+ if (!isStructure(newDestinationClassDataType)) {
+ return;
+ }
+
+ // if it gets this far then can probably assume both the new destination and source this
+ // data types are pointers to class structures
+ // NOTE: at this time there is a use case where the calling method incorrectly assumes the
+ // newParam is the same class structure as the source one. However, that method did not
+ // check the FunctionName option to see if that option was to not replace the function
+ // name. So we need to have a check to see if the current function namespace is already
+ // the same as the assumed new destination this structure name which would indicate that
+ // the function already was in the same class and so we still need to do the resolve to make
+ // sure that the structure contents get resolved according to the parameter replace options
+ VTMatchApplyChoices.FunctionNameChoices functionNameChoice =
+ markupOptions.getEnum(VTOptionDefines.FUNCTION_NAME, DEFAULT_OPTION_FOR_FUNCTION_NAME);
+ String currentDestinationFunctionNamespaceName =
+ toFunction.getSymbol().getParentNamespace().getName();
+ String newDestinationClassName = newDestinationClassDataType.getName();
+ if (functionNameChoice == FunctionNameChoices.EXCLUDE &&
+ !currentDestinationFunctionNamespaceName.equals(newDestinationClassName)) {
+ return;
+ }
+
+ // now use the path to the source data type to try and get the same named structure
+ // in the destination data type manager
+ ProgramBasedDataTypeManager destinationDataTypeManager =
+ toFunction.getProgram().getDataTypeManager();
+
+ String pathName = sourceClassDataType.getPathName();
+ DataType destinationDataTypeInDTM = destinationDataTypeManager.getDataType(pathName);
+
+ // only do the copy if the option is to replace always or if it is replace undefines and the
+ // destination structure is empty (ie undefined)
+ if (replaceAlways ||
+ destinationDataTypeInDTM == null ||
+ (destinationDataTypeInDTM.isNotYetDefined() && onlyReplaceUndefineds)) {
+
+ // since nothing above prevents the copy go ahead and copy the source class data type
+
+ //needs to be the original dt to get the struct * not just the struct
+ destinationDataTypeManager.resolve(sourceParam1.dataType,
+ DataTypeConflictHandler.REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER);
+
+ // check to see if the resolve probably updated the function's auto this param datatype
+ // it might not have if there was a .conflict created during the resolve or if the
+ // source and destination data types were in different folders or if one program
+ // has the preferred root data type manager folder set and the other doesn't
+ // not sure there is a way to tell this info since I don't know which the decompiler will
+ // pick at this point because nothing has been applied yet
+ DataType destinationDataTypeConflict =
+ destinationDataTypeManager.getDataType(pathName + ".conflict");
+
+ if (destinationDataTypeConflict != null) {
+ Msg.debug(this, "The applied class structure " +
+ newDestinationClassDataType.getPathName() +
+ " was copied to the destination program but a .conflict was created due to there " +
+ "already being a non-empty structure with that same path and name.");
+ }
+
+ // get the original toFunction this param if there was one and get it's path
+ // if the new path is different then spit out warming too
+ if (toFunction.getParameterCount() == 0) {
+ return;
+ }
+
+ if (!toFunction.getParameter(0).getName().equals("this")) {
+ return;
+ }
+
+ DataType pointedToDataType =
+ getPointedToDataType(toFunction.getParameter(0).getDataType());
+ if (pointedToDataType == null) {
+ return;
+ }
+
+ if (!pointedToDataType.getName().equals(newDestinationClassName)) {
+ return;
+ }
+
+ if (pointedToDataType.getPathName().equals(pathName)) {
+ return; // already handled with conflict check above
+ }
+
+ Msg.debug(this,
+ "The applied class structure was copied to the same data type manager " +
+ "path as in the source program whichi is different than the path to the existing " +
+ "class structure. The decompiler will first check for one in the Preferred Class" +
+ "Root Folder (if one has been set) otherwise it will use the first one it finds.");
+ }
+
+ }
+
+ private boolean isStructure(DataType dataType) {
+
+ if (dataType instanceof Structure) {
+ return true;
+ }
+
+ if (dataType instanceof StructureDataType) {
+ return true;
+ }
+
+ return false;
+
+ }
+
+ private DataType getPointedToDataType(DataType dataType) {
+
+ if (!(dataType instanceof PointerDataType)) {
+ return null;
+ }
+
+ PointerDataType pointerDataType = (PointerDataType) dataType;
+
+ return pointerDataType.getDataType();
+
+ }
+
private void applyInline(Function toFunction, boolean fromFunctionIsInline,
ToolOptions markupOptions) {
ReplaceChoices inlineChoice = markupOptions.getEnum(INLINE, DEFAULT_OPTION_FOR_INLINE);
@@ -604,10 +798,12 @@ public class FunctionSignatureStringable extends Stringable {
return returnParam; // Not replacing return type.
}
DataType toReturnType = toFunction.getReturnType();
+
DataType fromReturnType = returnInfo.dataType;
boolean isFromDefault = fromReturnType == DataType.DEFAULT;
boolean isToDefault = toReturnType == DataType.DEFAULT;
- boolean isToUndefined = Undefined.isUndefined(toReturnType);
+ boolean isToUndefined = Undefined.isUndefined(getBaseDataType(toReturnType));
+
if (!forceApply && onlyReplaceUndefineds) {
if (!isToDefault && !isToUndefined) {
return returnParam; // can't do it because we should only replace undefined data types.
@@ -773,13 +969,28 @@ public class FunctionSignatureStringable extends Stringable {
return parameters;
}
+ /**
+ * If the given data type is a pointer, get the "pointed to" data type
+ * @param dataType the given data type
+ * @return if not a pointer, just return the same dataType, if a pointer, return the
+ * "pointed to" data type
+ */
+ private DataType getBaseDataType(DataType dataType) {
+
+ if (dataType instanceof Pointer) {
+ Pointer pointer = (Pointer) dataType;
+ dataType = pointer.getDataType();
+ }
+ return dataType;
+ }
+
private DataType getHighestPriorityDataType(DataType fromDataType, DataType toDataType,
boolean onlyReplaceUndefineds) {
// Priority from highest to lowest is Defined, Undefined with size, Default.
boolean fromIsDefault = fromDataType == DataType.DEFAULT;
boolean toIsDefault = toDataType == DataType.DEFAULT;
- boolean fromIsUndefined = Undefined.isUndefined(fromDataType);
- boolean toIsUndefined = Undefined.isUndefined(toDataType);
+ boolean fromIsUndefined = Undefined.isUndefined(getBaseDataType(fromDataType));
+ boolean toIsUndefined = Undefined.isUndefined(getBaseDataType(toDataType));
if (fromIsDefault) {
return toDataType;
}
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 d336e34f63..688c95243a 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
@@ -58,8 +58,6 @@ 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 =
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 f03903c6ad..abba8cf0bc 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
@@ -767,7 +767,7 @@ public class VTMatchTableProvider extends ComponentProviderAdapter
"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.");
+ null, USE_NAMESPACE_TOOLTIP);
vtOptions.getOptions(APPLY_MARKUP_OPTIONS_NAME)
.registerOptionsEditor(() -> new ApplyMarkupPropertyEditor(controller));
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 999ce5d331..2ad0385fa9 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
@@ -113,6 +113,9 @@ public class VTOptionDefines {
public static final String USE_NAMESPACE_FUNCTIONS =
APPLY_MARKUP_OPTIONS_NAME + ".Replace Namespace";
+ public static final String USE_NAMESPACE_TOOLTIP =
+ "Apply the non-Global source namespace to the destination function.";
+
// 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/VTMatchApplyFunctionSignatureTest.java b/Ghidra/Features/VersionTracking/src/test.slow/java/ghidra/feature/vt/api/VTMatchApplyFunctionSignatureTest.java
index 96d193dddc..3cd2e27172 100644
--- a/Ghidra/Features/VersionTracking/src/test.slow/java/ghidra/feature/vt/api/VTMatchApplyFunctionSignatureTest.java
+++ b/Ghidra/Features/VersionTracking/src/test.slow/java/ghidra/feature/vt/api/VTMatchApplyFunctionSignatureTest.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.
@@ -39,6 +39,7 @@ import ghidra.program.model.address.Address;
import ghidra.program.model.data.*;
import ghidra.program.model.lang.*;
import ghidra.program.model.listing.*;
+import ghidra.program.model.listing.Function.FunctionUpdateType;
import ghidra.program.model.symbol.SourceType;
import ghidra.program.util.DefaultLanguageService;
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
@@ -124,6 +125,38 @@ public class VTMatchApplyFunctionSignatureTest extends AbstractGhidraHeadedInteg
return struct;
}
+ //Create Gadget structure - slightly different than usual in last field to simplify test
+ private Structure createGadgetStruct() {
+
+ Structure gadgetStruct = new StructureDataType("Gadget", 0);
+ PointerDataType charPtr = new PointerDataType(new CharDataType());
+ gadgetStruct.add(charPtr, "name", "");
+ gadgetStruct.add(new IntegerDataType(), "type", "");
+ gadgetStruct.add(new BooleanDataType(), "deployed", "");
+ gadgetStruct.add(new DWordDataType(), "workingOn", "");
+
+ return gadgetStruct;
+
+ }
+
+ private Structure createDifferentGadgetStruct() {
+
+ Structure gadgetStruct = new StructureDataType("Gadget", 0);
+ PointerDataType charPtr = new PointerDataType(new CharDataType());
+ gadgetStruct.add(charPtr, "name", "");
+ gadgetStruct.add(new IntegerDataType(), "type", "");
+ gadgetStruct.add(new BooleanDataType(), "deployed", "");
+ gadgetStruct.add(new PointerDataType(), "workingOn", "");
+
+ return gadgetStruct;
+
+ }
+
+ private Structure createEmptyGadgetStruct() {
+ Structure gadgetStruct = new StructureDataType("Gadget", 0);
+ return gadgetStruct;
+ }
+
private Program createSourceProgram() throws Exception {
ProgramBuilder builder = new ProgramBuilder("Wallace", ProgramBuilder._X86, this);
@@ -133,6 +166,7 @@ public class VTMatchApplyFunctionSignatureTest extends AbstractGhidraHeadedInteg
builder.createClassNamespace("Gadget", null, SourceType.IMPORTED);
StructureDataType struct = getPersonStruct(p);
+ builder.addDataType(struct);
Pointer ptr1 = PointerDataType.getPointer(struct, p.getDataTypeManager());
Pointer ptr2 = PointerDataType.getPointer(ptr1, p.getDataTypeManager());
@@ -291,6 +325,7 @@ public class VTMatchApplyFunctionSignatureTest extends AbstractGhidraHeadedInteg
@Test
public void testApplyMatch_ReplaceSignature_SameNumParams_ThisToThis() throws Exception {
+
useMatch("0x00401040", "0x00401040");
// Check initial values
@@ -314,6 +349,8 @@ public class VTMatchApplyFunctionSignatureTest extends AbstractGhidraHeadedInteg
List matches = new ArrayList<>();
matches.add(testMatch);
+ checkClassDataType(false, false);
+
// Test Apply
ApplyMatchTask task = new ApplyMatchTask(controller, matches);
runTask(session, task);
@@ -324,6 +361,10 @@ public class VTMatchApplyFunctionSignatureTest extends AbstractGhidraHeadedInteg
assertEquals(VTAssociationStatus.ACCEPTED, testMatch.getAssociation().getStatus());
checkFunctionSignatureStatus(testMatch, VTMarkupItemStatus.REPLACED);
+ // since no option to apply function name there will be no class namespace applied to
+ // the destination so no Gadget class data type should exist
+ checkClassDataType(false, false);
+
// Test unapply
ClearMatchTask unapplyTask = new ClearMatchTask(controller, matches);
runTask(session, unapplyTask);
@@ -333,6 +374,464 @@ public class VTMatchApplyFunctionSignatureTest extends AbstractGhidraHeadedInteg
"undefined FUN_00401040(void * this, undefined4 param_1)");
assertEquals(VTAssociationStatus.AVAILABLE, testMatch.getAssociation().getStatus());
checkFunctionSignatureStatus(testMatch, VTMarkupItemStatus.UNAPPLIED);
+
+ }
+
+ // use case: test replace undefined for return and params when they are undefined ptrs
+ @Test
+ public void testApplyMatch_ReplaceSignature_SameNumParams_ThisToThis_ReplaceUndefinedPointers()
+ throws Exception {
+
+ useMatch("0x00401040", "0x00401040");
+
+ // update the source function to have a Gadget * type
+
+ Function srcFunction = sourceProgram.getFunctionManager()
+ .getFunctionAt(addr("0x00401040", sourceProgram));
+
+ DataType existingEmptyGadgetStruct =
+ sourceProgram.getDataTypeManager().getDataType(CategoryPath.ROOT, "Gadget");
+ assertNotNull(existingEmptyGadgetStruct);
+
+ Pointer ptr1 =
+ PointerDataType.getPointer(existingEmptyGadgetStruct,
+ sourceProgram.getDataTypeManager());
+
+ assertNotNull(ptr1);
+
+ int id = sourceProgram.startTransaction("Test");
+
+ srcFunction.setReturnType(ptr1, SourceType.USER_DEFINED);
+
+ sourceProgram.endTransaction(id, true);
+
+ // update the destination function to have undefined4 * return type and to have param_1
+ // also be a undefined4 *
+
+ id = destinationProgram.startTransaction("Test");
+
+ Function destFunction = destinationProgram.getFunctionManager()
+ .getFunctionAt(addr("0x00401040", destinationProgram));
+ ptr1 =
+ PointerDataType.getPointer(Undefined4DataType.dataType,
+ destinationProgram.getDataTypeManager());
+ destFunction.setReturnType(ptr1, SourceType.DEFAULT);
+
+ Parameter[] parameters = destFunction.getParameters();
+ parameters[1].setDataType(ptr1, parameters[1].getSource());
+
+ destFunction.replaceParameters(FunctionUpdateType.DYNAMIC_STORAGE_ALL_PARAMS, true,
+ SourceType.USER_DEFINED, parameters);
+
+ destinationProgram.endTransaction(id, true);
+
+ // Check initial values
+ checkSignatures("Gadget * use(Gadget * this, Person * person)",
+ "undefined4 * FUN_00401040(void * this, undefined4 * param_1)");
+
+ // Set the function signature options for this test
+ ToolOptions applyOptions = controller.getOptions();
+ applyOptions.setEnum(VTOptionDefines.FUNCTION_NAME,
+ FunctionNameChoices.REPLACE_ALWAYS);
+ applyOptions.setEnum(FUNCTION_SIGNATURE,
+ FunctionSignatureChoices.WHEN_SAME_PARAMETER_COUNT);
+ applyOptions.setEnum(CALLING_CONVENTION, CallingConventionChoices.SAME_LANGUAGE);
+ applyOptions.setEnum(PARAMETER_DATA_TYPES,
+ ParameterDataTypeChoices.REPLACE_UNDEFINED_DATA_TYPES_ONLY);
+ applyOptions.setEnum(PARAMETER_NAMES, SourcePriorityChoices.REPLACE);
+ applyOptions.setEnum(PARAMETER_COMMENTS, CommentChoices.APPEND_TO_EXISTING);
+ applyOptions.setEnum(NO_RETURN, ReplaceChoices.EXCLUDE);
+ applyOptions.setEnum(FUNCTION_RETURN_TYPE,
+ ParameterDataTypeChoices.REPLACE_UNDEFINED_DATA_TYPES_ONLY);
+ applyOptions.setBoolean(VTOptionDefines.USE_NAMESPACE_FUNCTIONS, true); //replace namespace
+
+ assertEquals(VTAssociationStatus.AVAILABLE, testMatch.getAssociation().getStatus());
+ checkFunctionSignatureStatus(testMatch, VTMarkupItemStatus.UNAPPLIED);
+
+ List matches = new ArrayList<>();
+ matches.add(testMatch);
+
+ checkClassDataType(false, false);
+
+ // Test Apply
+ ApplyMatchTask task = new ApplyMatchTask(controller, matches);
+ runTask(session, task);
+
+ // Verify apply.
+ checkSignatures("Gadget * use(Gadget * this, Person * person)",
+ "Gadget * use(Gadget * this, Person * person)");
+ assertEquals(VTAssociationStatus.ACCEPTED, testMatch.getAssociation().getStatus());
+ checkFunctionSignatureStatus(testMatch, VTMarkupItemStatus.REPLACED);
+
+ // since no option to apply function name there will be no class namespace applied to
+ // the destination so no Gadget class data type should exist
+ checkClassDataType(true, false);
+
+ // Test unapply
+ ClearMatchTask unapplyTask = new ClearMatchTask(controller, matches);
+ runTask(session, unapplyTask);
+
+ // Verify unapply.
+ checkSignatures("Gadget * use(Gadget * this, Person * person)",
+ "undefined4 * FUN_00401040(void * this, undefined4 * param_1)");
+ assertEquals(VTAssociationStatus.AVAILABLE, testMatch.getAssociation().getStatus());
+ checkFunctionSignatureStatus(testMatch, VTMarkupItemStatus.UNAPPLIED);
+
+ }
+
+ // Use case: Source has empty Gadget struct, dest has no Gadget struct
+ @Test
+ public void testApplyMatch_ReplaceSignature_AndName_SameNumParams_ThisToThis_NoDestGadgetStruct()
+ throws Exception {
+
+ useMatch("0x00401040", "0x00401040");
+
+ // Check initial values
+ checkSignatures("undefined use(Gadget * this, Person * person)",
+ "undefined FUN_00401040(void * this, undefined4 param_1)");
+
+ // Set the function signature options for this test
+ ToolOptions applyOptions = controller.getOptions();
+
+ // function name choices - note the only difference between this and the
+ // testApplyMatch_ReplaceSignature_SameNumParams_ThisToThis test
+ // is that the option to apply the function name was added which then put the
+ // destination function in the Gadget namespace which then causes the this param to be
+ // a Gadget *
+ applyOptions.setEnum(VTOptionDefines.FUNCTION_NAME,
+ FunctionNameChoices.REPLACE_DEFAULT_ONLY);
+ applyOptions.setEnum(FUNCTION_SIGNATURE,
+ FunctionSignatureChoices.WHEN_SAME_PARAMETER_COUNT);
+ applyOptions.setEnum(CALLING_CONVENTION, CallingConventionChoices.SAME_LANGUAGE);
+ applyOptions.setEnum(PARAMETER_DATA_TYPES, ParameterDataTypeChoices.REPLACE);
+ applyOptions.setEnum(PARAMETER_NAMES, SourcePriorityChoices.REPLACE);
+ applyOptions.setEnum(PARAMETER_COMMENTS, CommentChoices.APPEND_TO_EXISTING);
+ applyOptions.setEnum(NO_RETURN, ReplaceChoices.EXCLUDE);
+ applyOptions.setEnum(FUNCTION_RETURN_TYPE, ParameterDataTypeChoices.REPLACE);
+ applyOptions.setBoolean(VTOptionDefines.USE_NAMESPACE_FUNCTIONS, true); //replace namespace
+
+ assertEquals(VTAssociationStatus.AVAILABLE, testMatch.getAssociation().getStatus());
+ checkFunctionSignatureStatus(testMatch, VTMarkupItemStatus.UNAPPLIED);
+
+ List matches = new ArrayList<>();
+ matches.add(testMatch);
+
+ checkClassDataType(false, false);
+
+ // Test Apply
+ ApplyMatchTask task = new ApplyMatchTask(controller, matches);
+ runTask(session, task);
+
+ // Verify apply.
+ checkSignatures("undefined use(Gadget * this, Person * person)",
+ "undefined use(Gadget * this, Person * person)");
+ assertEquals(VTAssociationStatus.ACCEPTED, testMatch.getAssociation().getStatus());
+ checkFunctionSignatureStatus(testMatch, VTMarkupItemStatus.REPLACED);
+
+ // since there is option to apply function name there should be a class namespace applied to
+ // the destination so the Gadget class data type should exist
+ // In this test Gadget is an empty structure in the source so should be empty in dest
+ checkClassDataType(true, false);
+
+ // Test unapply
+ ClearMatchTask unapplyTask = new ClearMatchTask(controller, matches);
+ runTask(session, unapplyTask);
+
+ // Verify unapply.
+ checkSignatures("undefined use(Gadget * this, Person * person)",
+ "undefined FUN_00401040(void * this, undefined4 param_1)");
+ assertEquals(VTAssociationStatus.AVAILABLE, testMatch.getAssociation().getStatus());
+ checkFunctionSignatureStatus(testMatch, VTMarkupItemStatus.UNAPPLIED);
+
+ }
+
+ // Use case: Source has populated Gadget struct, dest has empty Gadget struct
+ @Test
+ public void testApplyMatch_ReplaceSignature_AndName_SameNumParams_ThisToThis_EmptyDestGadgetStruct()
+ throws Exception {
+
+ useMatch("0x00401040", "0x00401040");
+
+ ProgramBasedDataTypeManager sourceDtm = sourceProgram.getDataTypeManager();
+ ProgramBasedDataTypeManager destDtm = destinationProgram.getDataTypeManager();
+
+ DataType existingEmptyGadgetStruct = sourceDtm.getDataType(CategoryPath.ROOT, "Gadget");
+ assertNotNull(existingEmptyGadgetStruct);
+
+ // replace the source empty gadget with a populated one
+ Structure gadgetStruct = createGadgetStruct();
+
+ int id = sourceDtm.startTransaction("Test");
+ sourceDtm.addDataType(gadgetStruct, DataTypeConflictHandler.REPLACE_HANDLER);
+ sourceDtm.endTransaction(id, true);
+
+ // verify it took
+ DataType updatedSourceGadget = sourceDtm.getDataType(CategoryPath.ROOT, "Gadget");
+ assertTrue(updatedSourceGadget.isEquivalent(gadgetStruct));
+
+ // add empty gadget to the destination program
+ Structure emptyGadgetStruct = createEmptyGadgetStruct();
+
+ id = destDtm.startTransaction("Test");
+ destDtm.addDataType(emptyGadgetStruct, DataTypeConflictHandler.REPLACE_HANDLER);
+ destDtm.endTransaction(id, true);
+
+ // verify it took
+ DataType destGadget = destDtm.getDataType(CategoryPath.ROOT, "Gadget");
+ assertTrue(destGadget.isNotYetDefined());
+
+ // Check initial values
+ checkSignatures("undefined use(Gadget * this, Person * person)",
+ "undefined FUN_00401040(void * this, undefined4 param_1)");
+
+ // Set the function signature options for this test
+ ToolOptions applyOptions = controller.getOptions();
+
+ // function name choices - note the only difference between this and the
+ // testApplyMatch_ReplaceSignature_SameNumParams_ThisToThis test
+ // is that the option to apply the function name was added which then put the
+ // destination function in the Gadget namespace which then causes the this param to be
+ // a Gadget *
+ applyOptions.setEnum(VTOptionDefines.FUNCTION_NAME,
+ FunctionNameChoices.REPLACE_DEFAULT_ONLY);
+ applyOptions.setEnum(FUNCTION_SIGNATURE,
+ FunctionSignatureChoices.WHEN_SAME_PARAMETER_COUNT);
+ applyOptions.setEnum(CALLING_CONVENTION, CallingConventionChoices.SAME_LANGUAGE);
+ applyOptions.setEnum(PARAMETER_DATA_TYPES, ParameterDataTypeChoices.REPLACE);
+ applyOptions.setEnum(PARAMETER_NAMES, SourcePriorityChoices.REPLACE);
+ applyOptions.setEnum(PARAMETER_COMMENTS, CommentChoices.APPEND_TO_EXISTING);
+ applyOptions.setEnum(NO_RETURN, ReplaceChoices.EXCLUDE);
+ applyOptions.setEnum(FUNCTION_RETURN_TYPE, ParameterDataTypeChoices.REPLACE);
+ applyOptions.setBoolean(VTOptionDefines.USE_NAMESPACE_FUNCTIONS, true); //replace namespace
+
+ assertEquals(VTAssociationStatus.AVAILABLE, testMatch.getAssociation().getStatus());
+ checkFunctionSignatureStatus(testMatch, VTMarkupItemStatus.UNAPPLIED);
+
+ List matches = new ArrayList<>();
+ matches.add(testMatch);
+
+ // Test Apply
+ ApplyMatchTask task = new ApplyMatchTask(controller, matches);
+ runTask(session, task);
+
+ // Verify apply.
+ checkSignatures("undefined use(Gadget * this, Person * person)",
+ "undefined use(Gadget * this, Person * person)");
+ assertEquals(VTAssociationStatus.ACCEPTED, testMatch.getAssociation().getStatus());
+ checkFunctionSignatureStatus(testMatch, VTMarkupItemStatus.REPLACED);
+
+ // since there is option to apply function name there should be a class namespace applied to
+ // the destination so the Gadget class data type should exist
+ // In this test Gadget is a populated structure in the source replacing an empty
+ // one in the dest so the resulting one in dest should be same populated on from source
+ destGadget = destDtm.getDataType(CategoryPath.ROOT, "Gadget");
+ assertTrue(destGadget.isEquivalent(gadgetStruct));
+
+ // Test unapply
+ ClearMatchTask unapplyTask = new ClearMatchTask(controller, matches);
+ runTask(session, unapplyTask);
+
+ // Verify unapply.
+ checkSignatures("undefined use(Gadget * this, Person * person)",
+ "undefined FUN_00401040(void * this, undefined4 param_1)");
+ assertEquals(VTAssociationStatus.AVAILABLE, testMatch.getAssociation().getStatus());
+ checkFunctionSignatureStatus(testMatch, VTMarkupItemStatus.UNAPPLIED);
+
+ }
+
+ // Use case: Source has populated Gadget struct, dest has same Gadget struct
+ // make sure no .conflict created
+ @Test
+ public void testApplyMatch_ReplaceSignature_AndName_SameNumParams_ThisToThis_SameDestGadgetStruct()
+ throws Exception {
+
+ useMatch("0x00401040", "0x00401040");
+
+ ProgramBasedDataTypeManager sourceDtm = sourceProgram.getDataTypeManager();
+ ProgramBasedDataTypeManager destDtm = destinationProgram.getDataTypeManager();
+
+ DataType existingEmptyGadgetStruct = sourceDtm.getDataType(CategoryPath.ROOT, "Gadget");
+ assertNotNull(existingEmptyGadgetStruct);
+
+ // replace the source empty gadget with a populated one
+ Structure gadgetStruct = createGadgetStruct();
+
+ int id = sourceDtm.startTransaction("Test");
+ sourceDtm.addDataType(gadgetStruct, DataTypeConflictHandler.REPLACE_HANDLER);
+ sourceDtm.endTransaction(id, true);
+
+ // verify it took
+ DataType updatedSourceGadget = sourceDtm.getDataType(CategoryPath.ROOT, "Gadget");
+ assertTrue(updatedSourceGadget.isEquivalent(gadgetStruct));
+
+ // add same Gadget to the destination program
+
+ id = destDtm.startTransaction("Test");
+ destDtm.addDataType(gadgetStruct, DataTypeConflictHandler.REPLACE_HANDLER);
+ destDtm.endTransaction(id, true);
+
+ // verify it took
+ DataType destGadget = destDtm.getDataType(CategoryPath.ROOT, "Gadget");
+ assertTrue(destGadget.isEquivalent(gadgetStruct));
+
+ // Check initial values
+ checkSignatures("undefined use(Gadget * this, Person * person)",
+ "undefined FUN_00401040(void * this, undefined4 param_1)");
+
+ // Set the function signature options for this test
+ ToolOptions applyOptions = controller.getOptions();
+
+ // function name choices - note the only difference between this and the
+ // testApplyMatch_ReplaceSignature_SameNumParams_ThisToThis test
+ // is that the option to apply the function name was added which then put the
+ // destination function in the Gadget namespace which then causes the this param to be
+ // a Gadget *
+ applyOptions.setEnum(VTOptionDefines.FUNCTION_NAME,
+ FunctionNameChoices.REPLACE_DEFAULT_ONLY);
+ applyOptions.setEnum(FUNCTION_SIGNATURE,
+ FunctionSignatureChoices.WHEN_SAME_PARAMETER_COUNT);
+ applyOptions.setEnum(CALLING_CONVENTION, CallingConventionChoices.SAME_LANGUAGE);
+ applyOptions.setEnum(PARAMETER_DATA_TYPES, ParameterDataTypeChoices.REPLACE);
+ applyOptions.setEnum(PARAMETER_NAMES, SourcePriorityChoices.REPLACE);
+ applyOptions.setEnum(PARAMETER_COMMENTS, CommentChoices.APPEND_TO_EXISTING);
+ applyOptions.setEnum(NO_RETURN, ReplaceChoices.EXCLUDE);
+ applyOptions.setEnum(FUNCTION_RETURN_TYPE, ParameterDataTypeChoices.REPLACE);
+ applyOptions.setBoolean(VTOptionDefines.USE_NAMESPACE_FUNCTIONS, true); //replace namespace
+
+ assertEquals(VTAssociationStatus.AVAILABLE, testMatch.getAssociation().getStatus());
+ checkFunctionSignatureStatus(testMatch, VTMarkupItemStatus.UNAPPLIED);
+
+ List matches = new ArrayList<>();
+ matches.add(testMatch);
+
+ // Test Apply
+ ApplyMatchTask task = new ApplyMatchTask(controller, matches);
+ runTask(session, task);
+
+ // Verify apply.
+ checkSignatures("undefined use(Gadget * this, Person * person)",
+ "undefined use(Gadget * this, Person * person)");
+ assertEquals(VTAssociationStatus.ACCEPTED, testMatch.getAssociation().getStatus());
+ checkFunctionSignatureStatus(testMatch, VTMarkupItemStatus.REPLACED);
+
+ // since there is option to apply function name there should be a class namespace applied to
+ // the destination so the Gadget class data type should exist
+ // In this test Gadget is a populated structure in the source and the same one in
+ // one in the dest so the resulting one in dest should be same populated on from source
+ destGadget = destDtm.getDataType(CategoryPath.ROOT, "Gadget");
+ assertTrue(destGadget.isEquivalent(gadgetStruct));
+
+ DataType gadgetConflict = destDtm.getDataType(CategoryPath.ROOT, "Gadget.conflict");
+ assertNull(gadgetConflict);
+
+ // Test unapply
+ ClearMatchTask unapplyTask = new ClearMatchTask(controller, matches);
+ runTask(session, unapplyTask);
+
+ // Verify unapply.
+ checkSignatures("undefined use(Gadget * this, Person * person)",
+ "undefined FUN_00401040(void * this, undefined4 param_1)");
+ assertEquals(VTAssociationStatus.AVAILABLE, testMatch.getAssociation().getStatus());
+ checkFunctionSignatureStatus(testMatch, VTMarkupItemStatus.UNAPPLIED);
+
+ }
+
+ // Use case: Source has populated Gadget struct, dest has different non-empty Gadget struct
+ // make sure .conflict IS created
+ @Test
+ public void testApplyMatch_ReplaceSignature_AndName_SameNumParams_ThisToThis_DiffDestGadgetStruct()
+ throws Exception {
+
+ useMatch("0x00401040", "0x00401040");
+
+ ProgramBasedDataTypeManager sourceDtm = sourceProgram.getDataTypeManager();
+ ProgramBasedDataTypeManager destDtm = destinationProgram.getDataTypeManager();
+
+ DataType existingEmptyGadgetStruct = sourceDtm.getDataType(CategoryPath.ROOT, "Gadget");
+ assertNotNull(existingEmptyGadgetStruct);
+
+ // replace the source empty gadget with a populated one
+ Structure gadgetStruct = createGadgetStruct();
+
+ int id = sourceDtm.startTransaction("Test");
+ sourceDtm.addDataType(gadgetStruct, DataTypeConflictHandler.REPLACE_HANDLER);
+ sourceDtm.endTransaction(id, true);
+
+ // verify it took
+ DataType updatedSourceGadget = sourceDtm.getDataType(CategoryPath.ROOT, "Gadget");
+ assertTrue(updatedSourceGadget.isEquivalent(gadgetStruct));
+
+ // add different Gadget to the destination program
+ Structure differentGadgetStruct = createDifferentGadgetStruct();
+ id = destDtm.startTransaction("Test");
+ destDtm.addDataType(differentGadgetStruct, DataTypeConflictHandler.REPLACE_HANDLER);
+ destDtm.endTransaction(id, true);
+
+ // verify it took
+ DataType destGadget = destDtm.getDataType(CategoryPath.ROOT, "Gadget");
+ assertTrue(destGadget.isEquivalent(differentGadgetStruct));
+
+ // Check initial values
+ checkSignatures("undefined use(Gadget * this, Person * person)",
+ "undefined FUN_00401040(void * this, undefined4 param_1)");
+
+ // Set the function signature options for this test
+ ToolOptions applyOptions = controller.getOptions();
+
+ // function name choices - note the only difference between this and the
+ // testApplyMatch_ReplaceSignature_SameNumParams_ThisToThis test
+ // is that the option to apply the function name was added which then put the
+ // destination function in the Gadget namespace which then causes the this param to be
+ // a Gadget *
+ applyOptions.setEnum(VTOptionDefines.FUNCTION_NAME,
+ FunctionNameChoices.REPLACE_DEFAULT_ONLY);
+ applyOptions.setEnum(FUNCTION_SIGNATURE,
+ FunctionSignatureChoices.WHEN_SAME_PARAMETER_COUNT);
+ applyOptions.setEnum(CALLING_CONVENTION, CallingConventionChoices.SAME_LANGUAGE);
+ applyOptions.setEnum(PARAMETER_DATA_TYPES, ParameterDataTypeChoices.REPLACE);
+ applyOptions.setEnum(PARAMETER_NAMES, SourcePriorityChoices.REPLACE);
+ applyOptions.setEnum(PARAMETER_COMMENTS, CommentChoices.APPEND_TO_EXISTING);
+ applyOptions.setEnum(NO_RETURN, ReplaceChoices.EXCLUDE);
+ applyOptions.setEnum(FUNCTION_RETURN_TYPE, ParameterDataTypeChoices.REPLACE);
+ applyOptions.setBoolean(VTOptionDefines.USE_NAMESPACE_FUNCTIONS, true); //replace namespace
+
+ assertEquals(VTAssociationStatus.AVAILABLE, testMatch.getAssociation().getStatus());
+ checkFunctionSignatureStatus(testMatch, VTMarkupItemStatus.UNAPPLIED);
+
+ List matches = new ArrayList<>();
+ matches.add(testMatch);
+
+ // Test Apply
+ ApplyMatchTask task = new ApplyMatchTask(controller, matches);
+ runTask(session, task);
+
+ // Verify apply.
+ checkSignatures("undefined use(Gadget * this, Person * person)",
+ "undefined use(Gadget * this, Person * person)");
+ assertEquals(VTAssociationStatus.ACCEPTED, testMatch.getAssociation().getStatus());
+ checkFunctionSignatureStatus(testMatch, VTMarkupItemStatus.REPLACED);
+
+ // since there is option to apply function name there should be a class namespace applied to
+ // the destination so the Gadget class data type should exist
+ // In this test Gadget is a populated structure in the source replacing a different non-empty
+ // one in the dest so the resulting one in dest should be the source one with the previous
+ // source one named .conflict
+ destGadget = destDtm.getDataType(CategoryPath.ROOT, "Gadget");
+ assertTrue(destGadget.isEquivalent(differentGadgetStruct));
+
+ DataType gadgetConflict = destDtm.getDataType(CategoryPath.ROOT, "Gadget.conflict");
+ assertTrue(gadgetConflict.isEquivalent(gadgetStruct));
+
+ // Test unapply
+ ClearMatchTask unapplyTask = new ClearMatchTask(controller, matches);
+ runTask(session, unapplyTask);
+
+ // Verify unapply.
+ checkSignatures("undefined use(Gadget * this, Person * person)",
+ "undefined FUN_00401040(void * this, undefined4 param_1)");
+ assertEquals(VTAssociationStatus.AVAILABLE, testMatch.getAssociation().getStatus());
+ checkFunctionSignatureStatus(testMatch, VTMarkupItemStatus.UNAPPLIED);
+
}
@Test
@@ -379,6 +878,8 @@ public class VTMatchApplyFunctionSignatureTest extends AbstractGhidraHeadedInteg
List matches = new ArrayList<>();
matches.add(testMatch);
+ checkClassDataType(false, false);
+
// Test Apply
ApplyMatchTask task = new ApplyMatchTask(controller, matches);
runTask(session, task);
@@ -389,6 +890,10 @@ public class VTMatchApplyFunctionSignatureTest extends AbstractGhidraHeadedInteg
assertEquals(VTAssociationStatus.ACCEPTED, testMatch.getAssociation().getStatus());
checkFunctionSignatureStatus(testMatch, VTMarkupItemStatus.REPLACED);
+ // since there is custom storage in this case the this param will be copied over even
+ // though the function isn't in the class namespace so the class data type should exist
+ checkClassDataType(true, true);
+
assertTrue(destinationFunction.hasCustomVariableStorage());
// Test unapply
@@ -405,6 +910,83 @@ public class VTMatchApplyFunctionSignatureTest extends AbstractGhidraHeadedInteg
assertFalse(destinationFunction.hasCustomVariableStorage());
}
+ @Test
+ public void testApplyMatch_ReplaceSignature_AndName_CustomSourceNormalDest_SameNumParams_ThisToThis()
+ throws Exception {
+ useMatch("0x00401040", "0x00401040");
+
+ // Check initial values
+ checkSignatures("undefined use(Gadget * this, Person * person)",
+ "undefined FUN_00401040(void * this, undefined4 param_1)");
+
+ tx(sourceProgram, () -> {
+ sourceFunction.setCustomVariableStorage(true);
+
+ sourceFunction.getParameter(0)
+ .setDataType(sourceFunction.getParameter(1).getDataType(),
+ SourceType.USER_DEFINED);
+ });
+
+ DataType personType = sourceProgram.getDataTypeManager().getDataType("/Person");
+ assertNotNull(personType);
+
+ tx(destinationProgram, () -> {
+ destinationFunction.setReturnType(personType, SourceType.USER_DEFINED);
+ });
+
+ // Check modified values
+ checkSignatures("undefined use(Person * this, Person * person)",
+ "Person * FUN_00401040(void * this, Person * __return_storage_ptr__, undefined4 param_1)");
+
+ // Set the function signature options for this test
+ ToolOptions applyOptions = controller.getOptions();
+ applyOptions.setEnum(VTOptionDefines.FUNCTION_NAME,
+ FunctionNameChoices.REPLACE_DEFAULT_ONLY);
+ applyOptions.setEnum(FUNCTION_SIGNATURE, FunctionSignatureChoices.REPLACE);
+ applyOptions.setEnum(CALLING_CONVENTION, CallingConventionChoices.SAME_LANGUAGE);
+ applyOptions.setEnum(PARAMETER_DATA_TYPES, ParameterDataTypeChoices.REPLACE);
+ applyOptions.setEnum(PARAMETER_NAMES, SourcePriorityChoices.REPLACE);
+ applyOptions.setEnum(PARAMETER_COMMENTS, CommentChoices.APPEND_TO_EXISTING);
+ applyOptions.setEnum(NO_RETURN, ReplaceChoices.EXCLUDE);
+ applyOptions.setEnum(FUNCTION_RETURN_TYPE, ParameterDataTypeChoices.REPLACE);
+
+ assertEquals(VTAssociationStatus.AVAILABLE, testMatch.getAssociation().getStatus());
+ checkFunctionSignatureStatus(testMatch, VTMarkupItemStatus.UNAPPLIED);
+
+ List matches = new ArrayList<>();
+ matches.add(testMatch);
+ checkClassDataType(false, false);
+
+ // Test Apply
+ ApplyMatchTask task = new ApplyMatchTask(controller, matches);
+ runTask(session, task);
+
+ // Verify apply. (return type not replaced with undefined due to lower priority)
+ checkSignatures("undefined use(Person * this, Person * person)",
+ "Person * use(Person * this, Person * person)");
+ assertEquals(VTAssociationStatus.ACCEPTED, testMatch.getAssociation().getStatus());
+ checkFunctionSignatureStatus(testMatch, VTMarkupItemStatus.REPLACED);
+
+ assertTrue(destinationFunction.hasCustomVariableStorage());
+
+ // since there is option to apply function name there should be a class namespace applied to
+ // the destination so the Gadget class data type should exist
+ checkClassDataType(true, true);
+
+ // Test unapply
+ ClearMatchTask unapplyTask = new ClearMatchTask(controller, matches);
+ runTask(session, unapplyTask);
+
+ // Verify unapply.
+ checkSignatures("undefined use(Person * this, Person * person)",
+ "Person * FUN_00401040(void * this, Person * __return_storage_ptr__, undefined4 param_1)");
+
+ assertEquals(VTAssociationStatus.AVAILABLE, testMatch.getAssociation().getStatus());
+ checkFunctionSignatureStatus(testMatch, VTMarkupItemStatus.UNAPPLIED);
+
+ assertFalse(destinationFunction.hasCustomVariableStorage());
+ }
+
@Test
public void testApplyMatch_ReplaceSignature_CustomSourceAndDest() throws Exception {
@@ -1698,6 +2280,39 @@ public class VTMatchApplyFunctionSignatureTest extends AbstractGhidraHeadedInteg
expectedStatus, vtMarkupItem.getStatus());
}
+ private void checkClassDataType(boolean shouldExist, boolean shouldBeNonEmpty) {
+
+ Parameter srcParam = sourceFunction.getParameter(0);
+ DataTypePath dataTypePath = srcParam.getDataType().getDataTypePath();
+
+ ProgramBasedDataTypeManager dstDtman = destinationProgram.getDataTypeManager();
+ DataType dstDataType = dstDtman.getDataType(dataTypePath);
+
+ if (shouldExist) {
+ assertTrue(dstDataType instanceof Pointer);
+
+ Pointer dstDataTypePtr = (Pointer) dstDataType;
+
+ DataType pointedToDataType = dstDataTypePtr.getDataType();
+
+ assertTrue(pointedToDataType instanceof Structure);
+
+ Structure struct = (Structure) pointedToDataType;
+
+ assertNotNull(struct);
+ if (shouldBeNonEmpty) {
+ assertFalse(struct.isNotYetDefined());
+ }
+ else {
+ assertTrue(struct.isNotYetDefined());
+ }
+ }
+ else {
+ assertNull(dstDataType);
+ }
+
+ }
+
private VTMarkupItem getFunctionSignatureMarkup(VTMatch match) {
MatchInfo matchInfo = controller.getMatchInfo(match);
Collection appliableMarkupItems =