@Override
	public boolean apply() throws EmptyCompositeException, InvalidDataTypeException {

		// commit changes for any fields under edit
		if (isEditingField()) {
			endFieldEditing();
		}

		clearStatus();

		if (avoidApplyingConflictingData()) {
			return false;
		}

		OffsetPairs offsetSelection = getRelOffsetSelection();
		int transID = startTransaction("Apply \"" + getCompositeName() + "\" Stack Edits");
		try {
			if (!isValidName() || !hasChanges()) {
				return false;
			}

			Variable[] newVars = viewComposite.getStackVariables();
			List<Variable> newVarsList = Arrays.asList(newVars);
			Collections.sort(newVarsList, StackVariableComparator.get()); // sort for use with getVariableContaining

			StackFrame functionStackFrame = function.getStackFrame();
			functionStackFrame.setLocalSize(viewComposite.getLocalSize());
			functionStackFrame.setReturnAddressOffset(viewComposite.getReturnAddressOffset());

			// first-pass: remove deleted params from end of param list if possible
			// to avoid custom storage enablement
			Parameter[] origParams = function.getParameters();
			for (int i = origParams.length - 1; i >= 0; --i) {

				Parameter p = origParams[i];
				if (!p.hasStackStorage() || p.isAutoParameter()) {
					break;
				}
				int offset = (int) p.getVariableStorage().getLastVarnode().getAddress().getOffset();
				if (getVariableContaining(offset, newVarsList) == null) {
					p.getSymbol().delete();
				}
				else {
					break; // quit on first in-bound param
				}
			}

			// second-pass: remove deleted locals/params from stack frame,
			// this will impose custom storage if any params removed this way
			for (Variable var : function.getAllVariables()) {
				if (!var.hasStackStorage() || (var.getSymbol() == null)) {
					continue;
				}
				int offset =
					(int) var.getVariableStorage().getLastVarnode().getAddress().getOffset();
				if (getVariableContaining(offset, newVarsList) == null) {
					if ((var instanceof Parameter) && !function.hasCustomVariableStorage()) {
						// force custom storage to prevent param shift
						function.setCustomVariableStorage(true);
					}
					var.getSymbol().delete();
				}
			}

			for (Variable sv : newVars) {
				Variable newSv = null;
				try {
					DataType dt = originalDTM.resolve(sv.getDataType(), null);
					Variable var = functionStackFrame.getVariableContaining(sv.getStackOffset());
// TODO: Handle case where new size is smaller but stack alignment will prevent variable shuffle on setDataType - could be problamatic
					if (var != null && var.getStackOffset() == sv.getStackOffset() &&
						var.getLength() == sv.getLength()) {
						newSv = var;
						if (!newSv.getName().equals(sv.getName())) {
							newSv.setName(sv.getName(), SourceType.USER_DEFINED);
						}
						if (!dt.equals(newSv.getDataType())) {
							newSv.setDataType(dt, SourceType.USER_DEFINED);
						}
					}
					else {
						if (functionStackFrame.isParameterOffset(sv.getStackOffset()) ||
							(var instanceof Parameter)) {
							// about to make param change - must enable custom storage
							functionStackFrame.getFunction().setCustomVariableStorage(true);
						}
						if (var != null) {
							functionStackFrame.clearVariable(var.getStackOffset());
						}
						newSv = functionStackFrame.createVariable(sv.getName(), sv.getStackOffset(),
							dt, SourceType.USER_DEFINED);
					}
					newSv.setComment(sv.getComment());
				}
				catch (DuplicateNameException e) {
					Msg.showError(this, null, "Stack Edit Error",
						"Stack variable error at offset " + sv.getStackOffset() + ": " +
							e.getMessage());
					continue;
				}
				catch (InvalidInputException e) {
					Msg.showError(this, null, "Stack Edit Error",
						"Stack variable error at offset " + sv.getStackOffset() + ": " +
							e.getMessage());
					continue;
				}
				String comment = sv.getComment();
				if (comment != null) {
					newSv.setComment(comment);
				}
			}
			load(function);
			clearStatus();
			return true;
		}
		finally {
			endTransaction(transID);
			setRelOffsetSelection(offsetSelection);
		}
	}
Adds the specified data type at the specified component index. Whether
an insert or replace occurs depends on whether the indicated index is
in a selection and whether in locked or unlocked mode.

@param index the component index of where to add the data type.
@param dt the data type to add

@return true if the component is added, false if it doesn't.
@throws UsrException if add fails
Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/stackeditor/StackEditorModel.java