public static ExecutionStack mergeStack(ExecutionStack stack1, ExecutionStack stack2) {
    if (stack1 == stack2) {
      // Easy out.
      return stack1;
    }

    int sz = stack1.size();
    ExecutionStack result = null;

    if (stack2.size() != sz) {
      throw new SimException("mismatched stack depths");
    }

    for (int i = 0; i < sz; i++) {
      TypeBearer tb1 = stack1.peek(i);
      TypeBearer tb2 = stack2.peek(i);
      TypeBearer resultType = mergeType(tb1, tb2);
      if (resultType != tb1) {
        /*
         * We only need to do anything when the result differs
         * from what is in the first stack, since that's what the
         * result gets initialized to.
         */
        if (result == null) {
          result = stack1.copy();
        }

        try {
          if (resultType == null) {
            throw new SimException("incompatible: " + tb1 + ", " + tb2);
          } else {
            result.change(i, resultType);
          }
        } catch (SimException ex) {
          ex.addContext("...while merging stack[" + Hex.u2(i) + "]");
          throw ex;
        }
      }
    }

    if (result == null) {
      return stack1;
    }

    result.setImmutable();
    return result;
  }
Merges two stacks. If the merged result is the same as the first argument, then return the
first argument (not a copy).

@param stack1 {@code non-null;} a stack
@param stack2 {@code non-null;} another stack
@return {@code non-null;} the result of merging the two stacks
android/android_framework/dex-maker/src/main/java/com/android/dx/cf/code/Merger.java