private Function replaceFunction(Address originEntryPoint, TaskMonitor monitor)
			throws CancelledException, UnsupportedOperationException {
		if (!originToResultTranslator.isOneForOneTranslator()) {
			String message = originToResultTranslator.getClass().getName() +
				" is not a one for one translator and can't replace a function.";
			throw new UnsupportedOperationException(message);
		}
		Address resultEntryPoint = originToResultTranslator.getAddress(originEntryPoint);
		Function originFunction = originListing.getFunctionAt(originEntryPoint);
		Function resultFunction = resultListing.getFunctionAt(resultEntryPoint);
		// Check for removing function or no function
		if (originFunction == null) {
			if (resultFunction != null) {
				resultListing.removeFunction(resultEntryPoint); // remove the original function
			}
			return null;
		}
		if (ProgramDiff.equivalentFunctions(originFunction, resultFunction, false)) {
			return resultFunction;
		}

		boolean isDefaultThunk = false;
		if (originFunction.isThunk()) {
			isDefaultThunk = originFunction.getSymbol().getSource() == SourceType.DEFAULT;
			Function thunkedFunction = originFunction.getThunkedFunction(false);
			Address thunkedEntryPoint = thunkedFunction.getEntryPoint();
			Address resultThunkedEntryPoint =
				originToResultTranslator.getAddress(thunkedEntryPoint);
			if (resultThunkedEntryPoint == null) {
				errorMsg.append("Can't replace thunk function @ " + originEntryPoint + ". " +
					"Can't determine equivalent thunked function entry point address " +
					"for thunked function @ " + thunkedEntryPoint + ".\n");
				return null;
			}
			Function resultThunkedFunction = resultListing.getFunctionAt(resultThunkedEntryPoint);
			if (resultThunkedFunction == null) {
				errorMsg.append("Can't replace thunk function @ " + originEntryPoint +
					". No function at pointed to address " + thunkedEntryPoint + ".\n");
				return null;
			}
		}

		Function newResultFunction = resultFunction; // initially assume the body hasn't changed.
		AddressSetView originBody = originFunction.getBody();
		AddressSet newResultBody = originToResultTranslator.getAddressSet(originBody);

		// Check for function body overlap conflict.
		if (ProgramMerge.overlapsOtherFunctions(originToResultTranslator, originFunction)) {
			errorMsg.append("Can't replace function @ " + originEntryPoint +
				". It would overlap another function.\n");
			return null;
		}

//        VariableReference[] restoreRefs = new VariableReference[0];
		String originName = originFunction.getName();
		Namespace desiredToNamespace = resultProgram.getGlobalNamespace();
		if (!isDefaultThunk) {
			try {
				desiredToNamespace = symbolMerge
						.resolveNamespace(originFunction.getParentNamespace(), conflictSymbolIDMap);
			}
			catch (DuplicateNameException e1) {
				Msg.error(this, "Unexpected Exception: " + e1.getMessage(), e1);
			}
			catch (InvalidInputException e1) {
				Msg.error(this, "Unexpected Exception: " + e1.getMessage(), e1);
			}
		}

		AddressSetView oldResultBody = (resultFunction == null) ? null : resultFunction.getBody();
		boolean sameBody = newResultBody.equals(oldResultBody);
		if (!sameBody) {
			try {
				// Changing function?
				if (resultFunction != null) {
					resultFunction.setBody(newResultBody);
				}
				else {
					try {
						newResultFunction = resultListing.createFunction(originName,
							desiredToNamespace, originEntryPoint, newResultBody,
							originFunction.getSymbol().getSource());
					}
					catch (InvalidInputException e) {
						String errorMessage = "Error creating function \"" + originName + "\" at " +
							originEntryPoint.toString(true) + ".\n  ";
						errorMsg.append(errorMessage + e.getMessage());
						return null;
					}
					/* createFunction appears to throw an IllegalArgumentException
					 * if data exists where you are trying to create the function.
					 * This should really happen via the InvalidInputException above.
					 */
					catch (IllegalArgumentException e) {
						String errorMessage = "Error creating function \"" + originName + "\" at " +
							originEntryPoint.toString(true) + ".\n  ";
						errorMsg.append(errorMessage + e.getMessage());
						return null;
					}
				}
			}
			catch (OverlappingFunctionException e) {
				errorMsg.append("Address = " + resultEntryPoint + ": " + e.getMessage() + "\n");
				return null;
			}
		}
		if (newResultFunction == null) {
			return null;
		}

		// Handle thunk
		if (originFunction.isThunk()) {
			Function originThunkedFunction = originFunction.getThunkedFunction(false);
			Function originThunkedFunctionInResult =
				DiffUtility.getFunction(originThunkedFunction, resultProgram);
			if (originThunkedFunctionInResult == null) {
				// No matching function to thunk to.
				errorMsg.append(
					"Thunked function not found at " + originThunkedFunction.getEntryPoint() +
						" for function at " + originFunction.getEntryPoint() + ".\n");
				return null;
			}
			Function currentThunkedFunction = newResultFunction.getThunkedFunction(false);
			if (currentThunkedFunction != originThunkedFunctionInResult) {
				newResultFunction.setThunkedFunction(originThunkedFunctionInResult);
			}
		}
		else if (newResultFunction.isThunk()) {
			newResultFunction.setThunkedFunction(null);
		}

		if (newResultFunction.isThunk()) {
			// A thunk Function has its own name and body but refers to another function,
			// so we don't want to affect the return type, calling convention, variables, etc.
			// Body was handled above and the name will get handled later.
			return newResultFunction;
		}

		// Do not replace the function name here, since the symbols need to get sorted out first.
		// After the symbols are merged you will need to replace the function symbol in case the
		// merge of symbols was not chosen.
		try {
			newResultFunction.updateFunction(originFunction.getCallingConventionName(),
				originFunction.getReturn(),
				originFunction.hasCustomVariableStorage() ? FunctionUpdateType.CUSTOM_STORAGE
						: FunctionUpdateType.DYNAMIC_STORAGE_ALL_PARAMS,
				true, originFunction.getSignatureSource(), originFunction.getParameters());
		}
		catch (DuplicateNameException e) {
			// should not occur
			errorMsg.append("Address = " + resultEntryPoint + ": " + e.getMessage() + "\n");
		}
		catch (InvalidInputException e) {
			errorMsg.append("Address = " + resultEntryPoint + ": " + e.getMessage() + "\n");
		}

		StackFrame originFrame = originFunction.getStackFrame();
		StackFrame newToFrame = newResultFunction.getStackFrame();
		newToFrame.setLocalSize(originFrame.getLocalSize());
		newToFrame.setReturnAddressOffset(originFrame.getReturnAddressOffset());

		newResultFunction.setStackPurgeSize(originFunction.getStackPurgeSize());
		newResultFunction.setVarArgs(originFunction.hasVarArgs());
		newResultFunction.setInline(originFunction.isInline());
		newResultFunction.setNoReturn(originFunction.hasNoReturn());

		replaceLocals(newResultFunction, originFunction, monitor);

//		restoreVariableRefs(newResultFunction, restoreRefs);

		newResultFunction.setSignatureSource(originFunction.getSignatureSource());

		return newResultFunction;
	}
Adds/Replaces/Removes a function at the specified address in the
resultListing based on the function in the origListing.
<br>Note: This method will replace the function, but does not create
the parent namespace or put the function in the parent namespace.
This must be done separately.
@param originEntryPoint the address of the functions entry point.
This address should be derived from the origin program.
@param monitor the task monitor for notifying the user of this merge's progress.
@return the new function that was created in the resultListing or null
if no function was created. If null is returned you should call
getErrorMessage() to see if an error occurred.
@throws CancelledException if user cancels via the monitor.
@throws UnsupportedOperationException if the ProgramMerge translators are not
"one for one translators".
Ghidra/Features/Base/src/main/java/ghidra/program/util/ProgramMerge.java