FileDocCategorySizeDatePackage
MethodAnnotater.javaAPI DocGlassfish v2 API104350Fri May 04 22:34:38 BST 2007com.sun.jdo.api.persistence.enhancer.impl

MethodAnnotater

public class MethodAnnotater extends com.sun.jdo.api.persistence.enhancer.util.Support implements AnnotationConstants
MethodAnnotater controls the code annotation for a single method within a class.

Fields Summary
private final ClassAction
ca
private final ClassMethod
method
private final ConstantPool
pool
private final Environment
env
private int
annotate
private Vector
tmpRegisters
private Vector
tmpDoubleRegisters
private short
annotationStack
private Map
insnNotes
Constructors Summary
MethodAnnotater(ClassAction ca, ClassMethod method, Environment env)
Constructor

        this.ca = ca;
        this.method = method;
        this.env = env;
        this.pool = ca.classFile().pool();
    
Methods Summary
private final voidaddNoteList(com.sun.jdo.api.persistence.enhancer.impl.InsnNote note)
Add a list of notes to the note list.

        insnNotes.put(note.insn, note);
    
private voidannotateClone(CodeAttribute codeAttr, java.lang.String superName)
Optimize out obviously redundant fetch(this) and dirty(this) annotations. These are repeated fetches and dirties which occur in straight-line code with no intervening branch targets or method calls.

        if (false) {
            final String methodName = method.name().asString();
            final String methodSig = method.signature().asString();
            System.out.println("annotateClone()");//NOI18N
            System.out.println("    methodName = " + methodName);//NOI18N
            System.out.println("    methodSig = " + methodSig);//NOI18N
            System.out.println("    superName = " + superName);//NOI18N
        }

        Insn insn;
        for (insn = codeAttr.theCode();
             insn != null;
             insn = insn.next()) {

            // Found the clone method.  See if it is the flavor of clone()
            // which does a super.clone() call, and if it is, add
            // field initializations for the jdoStateManager and jdoFlags
            // fields.
            if (insn.opcode() != opc_invokespecial)
                continue;

            final InsnConstOp invoke = (InsnConstOp)insn;
            final ConstMethodRef methodRef = (ConstMethodRef)invoke.value();
            final ConstNameAndType methodNT = methodRef.nameAndType();
            final String methodName = methodNT.name().asString();
            final String methodSig = methodNT.signature().asString();

            if (!(methodName.equals("clone")//NOI18N
                  && methodSig.equals("()Ljava/lang/Object;")))//NOI18N
                continue;

            if (false) {
                final ConstClass methodClass = methodRef.className();
                final String methodClassName = methodClass.asString();
                System.out.println("        found invocation of: "//NOI18N
                                   + methodClassName
                                   + "." + methodName + methodSig);//NOI18N
            }

            // check whether next instruction already is a downcast to a
            // class implementing PersistenceCapable
            final String thisClass = ca.className();
            final Insn checkCastInsn = insn.next();
            final boolean needCheckcast;
            if (checkCastInsn.opcode() != opc_checkcast) {
                needCheckcast = true;
            } else {
                ConstClass target =
                    (ConstClass) ((InsnConstOp) checkCastInsn).value();
                if (target.asString().equals(thisClass)) {
                    insn = checkCastInsn;
                    needCheckcast = false;
                } else {
                    needCheckcast = true;
                }
            }

            // clear jdo fields of clone
            {
                // duplicate downcastet reference
                final Insn newInsn = Insn.create(opc_dup);
                if (needCheckcast) {
                    newInsn.append(Insn.create(opc_checkcast,
                                               pool.addClass(thisClass)));
                }
                newInsn.append(Insn.create(opc_dup));

                // clear jdo fields
                newInsn.append(Insn.create(opc_aconst_null));
                newInsn.append(Insn.create(
                    opc_putfield,
                    pool.addFieldRef(thisClass,
                                     JDOMetaData.JDOStateManagerFieldName,
                                     JDOMetaData.JDOStateManagerFieldSig)));
                newInsn.append(Insn.create(opc_iconst_0));
                newInsn.append(Insn.create(
                    opc_putfield,
                    pool.addFieldRef(thisClass,
                                     JDOMetaData.JDOFlagsFieldName,
                                     JDOMetaData.JDOFlagsFieldSig)));

                // insert code
                insn.insert(newInsn);
                noteStack(3);
            }
        }
    
voidannotateMethod()
Annotate the class method. For now, brute force rules.

        //@olsen: cosmetics
        final CodeAttribute codeAttr = method.codeAttribute();
        if (codeAttr == null || !needsAnnotation())
            return;

//@olsen: disabled feature
/*
        if ((annotate & MakeThisTransient) != 0) {
            makeThisTransient(codeAttr);
            if (annotate == MakeThisTransient)
                return;
        }
*/

        //@olsen: added printing output
        env.message(
            "annotating method " + ca.userClassName()//NOI18N
            + "." + method.name().asString()//NOI18N
            + Descriptor.userMethodArgs(method.signature().asString()));

//@olsen: disabled feature
/*
        clearThisAnnotation();
        removeRedundantThisAnnotation();
*/

        Insn firstInsn = codeAttr.theCode();

        // First instruction is a target
        Insn insn = firstInsn.next();

        while (insn != null) {
            switch(insn.opcode()) {
//@olsen: disabled feature
/*
            case opc_invokestatic:
            case opc_invokespecial:
            case opc_invokevirtual:
            case opc_invokeinterface:
*/
            case opc_getfield:
            case opc_putfield:
//@olsen: disabled feature
/*
            case opc_aaload:
            case opc_baload:
            case opc_caload:
            case opc_saload:
            case opc_iaload:
            case opc_laload:
            case opc_faload:
            case opc_daload:
            case opc_aastore:
            case opc_bastore:
            case opc_castore:
            case opc_sastore:
            case opc_iastore:
            case opc_lastore:
            case opc_fastore:
            case opc_dastore:
*/
                insn = insnAnnotation(insn);
                break;
            default:
                break;
            }

            insn = insn.next();
        }

        //@olsen: do special annotation if detected super.clone()
        if ((annotate & SuperClone) != 0) {
            final String superName = ca.classFile().superName().asString();
            annotateClone(codeAttr, superName);
        }

//@olsen: disabled feature
/*
        if (methodIsInitializer()) {
        } else {
            // Pre- fetch/dirty this if needed
            if (fetchThis || dirtyThis) {
                // Optimize a fetch(this) or dirty(this) to the start of
                // the method.
                // For fetch calls this is:
                // if (jdoFlags < 0)
                // Implementation.fetch(this);
                //
                // For dirty calls this is:
                // if ((jdoFlags&PersistenceCapable.writeBarrierSet) != 0)
                // Implementation.dirty(this);

                Insn newInsn = Insn.create(opc_aload_0);
                Insn annotationStart = newInsn;
                InsnTarget afterCondition = null;

//@olsen: disabled feature
///
                if (ca.getFlagsMemberValid() &&
                    ca.getFlagsMember() != null) {
///

                    //@olsen: changed to use JDOMetaData
                    final String className = ca.className();
                    final String pcRootClass
                        = env.getJDOMetaData().getPersistenceCapableRootClass(className);
                    newInsn = newInsn.append(
                        Insn.create(opc_getfield,
                                    pool.addFieldRef(
                                        pcRootClass,
                                        JDOMetaData.JDOFlagsFieldName,
                                        JDOMetaData.JDOFlagsFieldSig)));
//@olsen: disabled feature
///
                    ClassControl flagsCC
                        = ca.getFlagsMemberClassControl();
                    newInsn = newInsn.append(
                        Insn.create(opc_getfield,
                                    pool.addFieldRef(
                                        flagsCC.className(),
                                        ca.getFlagsMember(),
                                        "B")));
///

                    afterCondition = new InsnTarget();
                    if (dirtyThis) {
                        newInsn = newInsn.append(Insn.create(opc_iconst_2));
                        newInsn = newInsn.append(Insn.create(opc_iand));
                        newInsn = newInsn.append(Insn.create(opc_ifeq, afterCondition));
                        newInsn = newInsn.append(Insn.create(opc_aload_0));
                    } else {
                        newInsn = newInsn.append(Insn.create(opc_ifge, afterCondition));
                        newInsn = newInsn.append(Insn.create(opc_aload_0));
                    }
//@olsen: disabled feature
///
                }
///

                newInsn = newInsn.append(
                    Insn.create(
                        opc_invokestatic,
                        pool.addMethodRef("com/sun/forte4j/persistence/internal/Implementation",
                                          (dirtyThis ? "dirty" : "fetch"),
                                          "(" + JDOMetaData.JDOPersistenceCapableSig + ")V")));

                if (afterCondition != null)
                    newInsn = newInsn.append(afterCondition);
                firstInsn.insert(annotationStart);
                noteStack(2);
            }

//@olsen: disabled feature
///
            if (methodName.equals("clone") &&
                methodSig.equals("()Ljava/lang/Object;") &&
                !ca.getNeedsClone()) {
                annotateClone(codeAttr, superName);
            }
///
//@olsen: disabled feature
        }
*/

//@olsen: disabled feature
/*
        //^olsen: caches -> int[] ?
        if (caches != null && caches.size() > 0) {
            // Generate fetch/dirty cache initializers
            Insn initInsn = null;
            //^olsen: optimize traversal ?
            for (int i = 0; i < caches.size(); i++) {
                int slot = ((Integer) caches.elementAt(i)).intValue();
                Insn nullInsn = Insn.create(opc_aconst_null);
                if (initInsn == null)
                    initInsn = nullInsn;
                else
                    initInsn.append(nullInsn);
                initInsn.append(InsnUtils.aStore(slot, pool));
            }

            // These initializations must not occur in an
            // exception handler or the code may fail to verify.  If an
            // exception handler starts at offset 0, our initializations
            // will fall into the exception handler block.  For
            // simplicity, just add a new target as the initial
            // instruction - it doesn't cost anything in the
            // generated code.
            InsnTarget newFirstInsn = new InsnTarget();
            initInsn.append(firstInsn);
            newFirstInsn.append(initInsn);
            firstInsn = newFirstInsn;
            codeAttr.setTheCode(firstInsn);
        }
*/

        if (annotationStack > 0)
            codeAttr.setStackUsed(codeAttr.stackUsed() + annotationStack);
    
private booleanavoidAnnotation()
Check to see if this is a special case that should not be annotated.

        //@olsen: cosmetics

//@olsen: allow for annotating initializers+finalizers
/*
        final String methodName = method.name().asString();
        final String methodSig = method.signature().asString();

        if (methodName.equals("<clinit>") || methodIsFinalize())
            // Never annotate class initializers or finalizers
            return true;
*/

//^olsen: enable feature, rather use HashMap for lookup
//@olsen: disabled feature
/*
        if (ca.persistCapable()) {
            if ((methodName.equals("initializeContents") &&
                 methodSig.equals("(Lcom/sun/forte4j/persistence/internal/ObjectContents;)V")) ||
                (methodName.equals("flushContents") &&
                 methodSig.equals("(Lcom/sun/forte4j/persistence/internal/ObjectContents;)V")) ||
                (methodName.equals("clearContents") &&
                 methodSig.equals("()V")) ||
                (methodName.equals("postInitializeContents") &&
                 methodSig.equals("()V")) ||
                (methodName.equals("preFlushContents") &&
                 methodSig.equals("()V")) ||
                (methodName.equals("preClearContents") &&
                 methodSig.equals("()V")) ||
                (methodName.equals("jdoGetStateManager") &&
                 methodSig.equals("()Lcom/sun/forte4j/persistence/internal/StateManager;")) ||
                (methodName.equals("jdoSetStateManager") &&
                 methodSig.equals("(Lcom/sun/forte4j/persistence/internal/StateManager;)V")) ||
                (methodName.equals("jdoGetFlags") &&
                 methodSig.equals("()B")) ||
                (methodName.equals("jdoSetFlags") &&
                 methodSig.equals("(B)V")))
                // This is one of the special persistence actions.
                // Don't annotate it
                return true;
        }
*/

        return false;
    
private com.sun.jdo.api.persistence.enhancer.impl.AnnotationFragmentbuildAccessAnnotation(com.sun.jdo.api.persistence.enhancer.impl.InsnNote note)

        final int requiredStack;
        final Insn annotation;

        final String targetClassName = note.targetClassName;
        final String targetFieldName = note.targetFieldName;
        final String targetPCRootClass = note.targetPCRootClass;

        //@olsen: not needed to ensure: note.dirtyThis() && !method.isStatic()
        final boolean fetch = (note.fetchPersistent() || note.fetchThis());
        final boolean dirty = (note.dirtyPersistent() || note.dirtyThis());
        //@olsen: added consistency check
        affirm((fetch ^ dirty),
               "Inconsistent fetch/dirty flags.");//NOI18N

        //@olsen: added println() for debugging
        if (false) {
            final boolean dfgField = note.dfgFieldAccess();
            final boolean pkField = note.pkFieldAccess();

            System.out.println("    build access annotation: "//NOI18N
                               + targetClassName
                               + "." + targetFieldName + " : "//NOI18N
                               + (pkField ? "pk," : "!pk,")//NOI18N
                               + (dfgField ? "dfg," : "!dfg,")//NOI18N
                               + (fetch ? "fetch " : "dirty ")//NOI18N
                               + (note.fetchPersistent()
                                  ? "persistent" : "this")//NOI18N
                               + ";");//NOI18N
        }

        final int argSize = note.arg();
        final String fieldSig = note.sig();
        final int fieldType = Descriptor.elementType(fieldSig);
        final int fieldIndex = note.targetFieldIndex;
        if (false) {
            System.out.println("        argSize = " + argSize);//NOI18N
            System.out.println("        fieldSig = " + fieldSig);//NOI18N
            System.out.println("        fieldType = " + fieldType);//NOI18N
            System.out.println("        fieldIndex = " + fieldIndex);//NOI18N
        }

        if (fetch) {
            // get jdoStateManager
            Insn insn = annotation = Insn.create(opc_dup);
            insn = insn.append(
                Insn.create(opc_getfield,
                            pool.addFieldRef(
                                targetPCRootClass,
                                JDOMetaData.JDOStateManagerFieldName,
                                JDOMetaData.JDOStateManagerFieldSig)));

            // test jdoStateManager
            // load/dirty field if nonnull
            InsnTarget fetchDirty = new InsnTarget();
            InsnTarget afterFetchDirty = new InsnTarget();
            insn = insn.append(Insn.create(opc_dup));
            insn = insn.append(
                Insn.create(opc_ifnonnull, fetchDirty));

            // pop jdoStateManager and skip loading/dirtying
            insn = insn.append(Insn.create(opc_pop));
            insn = insn.append(
                Insn.create(opc_goto, afterFetchDirty));

            // invoke StateManager's fetch method
            insn = insn.append(fetchDirty);

            // push field's unique index onto stack (1st arg)
            insn = insn.append(InsnUtils.integerConstant(fieldIndex, pool));

            // call stateManager's void prepareGetField(int fieldID) method
            requiredStack = 2;
            insn = insn.append(
                new InsnInterfaceInvoke(
                    pool.addInterfaceMethodRef(
                        JDOMetaData.JDOStateManagerPath,
                        "prepareGetField",//NOI18N
                        "(I)V"),//NOI18N
                    requiredStack));

            insn = insn.append(afterFetchDirty);
            insn = insn.append(note.insn);
        } else {
            //affirm(dirty);
            int singleRegs = 0;
            int doubleRegs = 0;

            // move current value into temp registers
            affirm(argSize > 0);
            final int reg = ((argSize == 1)
                             ? tmpReg(singleRegs++)
                             : tmpReg2(doubleRegs++));
            Insn insn = annotation = InsnUtils.store(fieldType, reg, pool);

            // get jdoStateManager
            insn = insn.append(Insn.create(opc_dup));
            insn = insn.append(
                Insn.create(opc_getfield,
                            pool.addFieldRef(
                                targetPCRootClass,
                                JDOMetaData.JDOStateManagerFieldName,
                                JDOMetaData.JDOStateManagerFieldSig)));

            // test jdoStateManager
            // load/dirty field if nonnull
            InsnTarget fetchDirty = new InsnTarget();
            InsnTarget afterFetchDirty = new InsnTarget();
            insn = insn.append(Insn.create(opc_dup));
            insn = insn.append(
                Insn.create(opc_ifnonnull, fetchDirty));

            // pop jdoStateManager and skip loading/dirtying
            insn = insn.append(Insn.create(opc_pop));
            // restore value from registers
            affirm(argSize > 0);
            insn = insn.append(InsnUtils.load(fieldType, reg, pool));
            //@olsen: 4429769, insert the original putfield instruction here
            insn = insn.append(note.insn);
            insn = insn.append(
                Insn.create(opc_goto, afterFetchDirty));

            // invoke StateManager's load method
            insn = insn.append(fetchDirty);

            // push field's unique index onto stack (1st arg)
            insn = insn.append(InsnUtils.integerConstant(fieldIndex, pool));

            // restore value from registers (2nd arg)
            affirm(argSize > 0);
            insn = insn.append(InsnUtils.load(fieldType, reg, pool));

            // call stateManager's set<Type>Field(index, value) method
            switch(fieldType) {
            case T_BOOLEAN:
                //boolean setBooleanField(int fieldNumber, boolean value);
                requiredStack = 3;
                insn = insn.append(
                    new InsnInterfaceInvoke(
                        pool.addInterfaceMethodRef(
                            JDOMetaData.JDOStateManagerPath,
                            "setBooleanField",//NOI18N
                            "(IB)B"),//NOI18N
                        requiredStack));
                //@olsen: 4429769, disregard object and setField's return value
                insn = insn.append(Insn.create(opc_pop2));
                break;
            case T_CHAR:
                //char setCharField(int fieldNumber, char 3);
                requiredStack = 3;
                insn = insn.append(
                    new InsnInterfaceInvoke(
                        pool.addInterfaceMethodRef(
                            JDOMetaData.JDOStateManagerPath,
                            "setCharField",//NOI18N
                            "(IC)C"),//NOI18N
                        requiredStack));
                //@olsen: 4429769, disregard object and setField's return value
                insn = insn.append(Insn.create(opc_pop2));
                break;
            case T_BYTE:
                //byte setByteField(int fieldNumber, byte value);
                requiredStack = 3;
                insn = insn.append(
                    new InsnInterfaceInvoke(
                        pool.addInterfaceMethodRef(
                            JDOMetaData.JDOStateManagerPath,
                            "setByteField",//NOI18N
                            "(IZ)Z"),//NOI18N
                        requiredStack));
                //@olsen: 4429769, disregard object and setField's return value
                insn = insn.append(Insn.create(opc_pop2));
                break;
            case T_SHORT:
                //short setShortField(int fieldNumber, short value);
                requiredStack = 3;
                insn = insn.append(
                    new InsnInterfaceInvoke(
                        pool.addInterfaceMethodRef(
                            JDOMetaData.JDOStateManagerPath,
                            "setShortField",//NOI18N
                            "(IS)S"),//NOI18N
                        requiredStack));
                //@olsen: 4429769, disregard object and setField's return value
                insn = insn.append(Insn.create(opc_pop2));
                break;
            case T_INT:
                //int setIntField(int fieldNumber, int value);
                requiredStack = 3;
                insn = insn.append(
                    new InsnInterfaceInvoke(
                        pool.addInterfaceMethodRef(
                            JDOMetaData.JDOStateManagerPath,
                            "setIntField",//NOI18N
                            "(II)I"),//NOI18N
                        requiredStack));
                //@olsen: 4429769, disregard object and setField's return value
                insn = insn.append(Insn.create(opc_pop2));
                break;
            case T_LONG:
                //long setLongField(int fieldNumber, long value);
                requiredStack = 4;
                insn = insn.append(
                    new InsnInterfaceInvoke(
                        pool.addInterfaceMethodRef(
                            JDOMetaData.JDOStateManagerPath,
                            "setLongField",//NOI18N
                            "(IJ)J"),//NOI18N
                        requiredStack));
                //@olsen: 4429769, disregard object and setField's return value
                insn = insn.append(Insn.create(opc_pop2));
                insn = insn.append(Insn.create(opc_pop));
                break;
            case T_FLOAT:
                //float setFloatField(int fieldNumber, float value);
                requiredStack = 3;
                insn = insn.append(
                    new InsnInterfaceInvoke(
                        pool.addInterfaceMethodRef(
                            JDOMetaData.JDOStateManagerPath,
                            "setFloatField",//NOI18N
                            "(IF)F"),//NOI18N
                        requiredStack));
                //@olsen: 4429769, disregard object and setField's return value
                insn = insn.append(Insn.create(opc_pop2));
                break;
            case T_DOUBLE:
                //double setDoubleField(int fieldNumber, double value);
                requiredStack = 4;
                insn = insn.append(
                    new InsnInterfaceInvoke(
                        pool.addInterfaceMethodRef(
                            JDOMetaData.JDOStateManagerPath,
                            "setDoubleField",//NOI18N
                            "(ID)D"),//NOI18N
                        requiredStack));
                //@olsen: 4429769, disregard object and setField's return value
                insn = insn.append(Insn.create(opc_pop2));
                insn = insn.append(Insn.create(opc_pop));
                break;
            case TC_OBJECT:
            case TC_INTERFACE:
                //Object setObjectField(int fieldNumber, Object value);
                requiredStack = 3;
                insn = insn.append(
                    new InsnInterfaceInvoke(
                        pool.addInterfaceMethodRef(
                            JDOMetaData.JDOStateManagerPath,
                            "setObjectField",//NOI18N
                            "(ILjava/lang/Object;)Ljava/lang/Object;"),//NOI18N
                        requiredStack));

                //@olsen: 4429769, no need to downcast anymore
/*
                // add a down-cast to the field's type
                affirm((fieldSig.charAt(0) == 'L'
                        && fieldSig.charAt(fieldSig.length() - 1) == ';'),
                       "Inconsistent field signature");//NOI18N
                final String fieldTypeClassName
                    = fieldSig.substring(1, fieldSig.length() - 1);
                final ConstClass fieldTypeConstClass
                    = pool.addClass(fieldTypeClassName);
                insn = insn.append(
                    Insn.create(opc_checkcast, fieldTypeConstClass));
*/
                //@olsen: 4429769, disregard object and setField's return value
                insn = insn.append(Insn.create(opc_pop2));
                break;
            default:
                throw new InternalError("Unexpected field type");//NOI18N
            }

            insn = insn.append(afterFetchDirty);
        }

        //@olsen: added println() for debugging
        if (false) {
            System.out.println("        built annotation, "//NOI18N
                               + "required stack = "//NOI18N
                               + requiredStack);
        }

        return new AnnotationFragment(annotation, requiredStack);
    
private com.sun.jdo.api.persistence.enhancer.impl.AnnotationFragmentbuildBasicAnnotation(com.sun.jdo.api.persistence.enhancer.impl.InsnNote note)
Assuming that an object reference is on the top of stack, generate an instruction sequence to perform the annotation indicated by the note.

        int requiredStack = 2;
        Insn basicAnnotation = null;

        //@olsen: changed to use JDOMetaData
        final String targetClassName = note.targetClassName;
        final String targetFieldName = note.targetFieldName;
        final String targetPCRootClass = note.targetPCRootClass;

        //@olsen: not needed to ensure: note.dirtyThis() && !method.isStatic()
        final boolean fetch = (note.fetchPersistent() || note.fetchThis());
        final boolean dirty = (note.dirtyPersistent() || note.dirtyThis());
        //@olsen: added consistency check
        affirm((fetch ^ dirty),
               "Inconsistent fetch/dirty flags.");//NOI18N

        //@olsen: added println() for debugging
        if (false) {
            final boolean dfgField = note.dfgFieldAccess();
            final boolean pkField = note.pkFieldAccess();

            System.out.println("    build basic annotation: "//NOI18N
                               + targetClassName
                               + "." + targetFieldName + " : "//NOI18N
                               + (pkField ? "pk," : "!pk,")//NOI18N
                               + (dfgField ? "dfg," : "!dfg,")//NOI18N
                               + (fetch ? "fetch " : "dirty ")//NOI18N
                               + (note.fetchPersistent()
                                  ? "persistent" : "this")//NOI18N
                               + ";");//NOI18N
        }

        //@olsen: changed code for annotation
        {
            Insn insn = null;

            //requiredStack = 2;

            // get jdoFlags
            basicAnnotation = insn = Insn.create(opc_dup);
            insn = insn.append(
                Insn.create(opc_getfield,
                            pool.addFieldRef(
                                targetPCRootClass,
                                JDOMetaData.JDOFlagsFieldName,
                                JDOMetaData.JDOFlagsFieldSig)));

            // test jdoFlags
            // skip loading for read if <= 0 / for update if == 0
            InsnTarget afterFetchDirty = new InsnTarget();
            insn = insn.append(
                Insn.create((fetch ? opc_ifle : opc_ifeq),
                            afterFetchDirty));

            // get jdoStateManager
            insn = insn.append(Insn.create(opc_dup));
            insn = insn.append(
                Insn.create(opc_getfield,
                            pool.addFieldRef(
                                targetPCRootClass,
                                JDOMetaData.JDOStateManagerFieldName,
                                JDOMetaData.JDOStateManagerFieldSig)));

            // invoke StateManager's load method
            insn = insn.append(
                new InsnInterfaceInvoke(
                    pool.addInterfaceMethodRef(
                        JDOMetaData.JDOStateManagerPath,
                        (fetch ? "loadForRead" : "loadForUpdate"),//NOI18N
                        "()V"),//NOI18N
                    1));

            insn = insn.append(afterFetchDirty);
        }

        //@olsen: added println() for debugging
        if (false) {
            System.out.println("        built annotation, "//NOI18N
                               + "required stack = "//NOI18N
                               + requiredStack);
        }

        return new AnnotationFragment(basicAnnotation, requiredStack);
    
voidcheckCode(CodeAttribute codeAttr)
Check the code attribute for possible annotations

        //@olsen: cosmetics
        Insn firstInsn = codeAttr.theCode();

        // mark branch targets so we can distinguish them from
        // targets which exist for the benefit of line numbers,
        // local variables, etc.
        for (Insn markInsn = firstInsn;
             markInsn != null;
             markInsn = markInsn.next()) {
            markInsn.markTargets();
        }

        int allFlags = 0;
//@olsen: disabled feature
/*
        boolean branchesSeen = false;
*/

        for (Insn insn = firstInsn; insn != null; insn = insn.next() ) {
            InsnNote note = null;

            switch(insn.opcode()) {
//@olsen: disabled feature
/*
            case opc_invokestatic:
            case opc_invokespecial:
            case opc_invokevirtual:
            case opc_invokeinterface:
                note = noteInvokeAnnotation(insn);
                break;
*/
            case opc_getfield:
                note = noteGetFieldAnnotation(insn);
                break;
            case opc_putfield:
                note = notePutFieldAnnotation(insn);
                break;
//@olsen: disabled feature
/*
            case opc_aaload:
            case opc_baload:
            case opc_caload:
            case opc_saload:
            case opc_iaload:
            case opc_laload:
            case opc_faload:
            case opc_daload:
                note = noteArrayLoadAnnotation(insn);
                break;
            case opc_aastore:
            case opc_bastore:
            case opc_castore:
            case opc_sastore:
            case opc_iastore:
            case opc_lastore:
            case opc_fastore:
            case opc_dastore:
                note = noteArrayStoreAnnotation(insn);
                break;
*/
            default:
                break;
            }

            if (note != null) {
                addNoteList(note);

                //@olsen: ensured to use single note only (as instantiated)
                affirm((note.next() == null),
                       "Number of annotation notes for instruction > 1.");//NOI18N
                allFlags |= note.insnFlags;

//@olsen: ensured to use single note only (as instantiated)
/*
                for (InsnNote aNote = note;
                     aNote != null;
                     aNote = aNote.next()) {
//@olsen: disabled feature
///
                    if (branchesSeen == false)
                        aNote.insnFlags |= Unconditional;
///

//@olsen: disabled feature
///
                    if (largestLoop != null && largestLoop.contains(insn))
                        aNote.insnFlags |= InLoop;
///

//@olsen: disabled feature
///
                    // annotating based on thisOptimization will be done later
                    if (aNote.dirtyThis() && aNote.unconditional())
                        dirtyThis = true;
                    else if (aNote.dirtyThis() || aNote.fetchThis())
                        fetchThis = true;
///

                    allFlags |= aNote.insnFlags;
                }
*/
            }

//@olsen: disabled feature
/*
            if (insn.branches())
                branchesSeen = true;
*/
        }

//@olsen: disabled feature
/*
        if (methodIsInitializer()) {
            // An inititalizer -  either force the fetchThis, dirtyThis flags
            // on or off.
            if (env.doInitializerOptimization()) {
                // turn on the fetchThis, dirtyThis flags to inhibit fetches
                // and stores of this if enabled.  We won't really insert the
                // fetch/dirty, since it isn't needed.
                fetchThis = true;
                dirtyThis = true;
            } else {
                // Make sure that the fetchThis, dirtyThis flags are turned off
                fetchThis = false;
                dirtyThis = false;
            }
        }
*/

        //^olsen: prepare for inheritance on PC classes
        //@olsen: check for annotating of clone()
        final String methodName = method.name().asString();
        final String methodSig = method.signature().asString();
        //^olsen: annotate persistence-capable root classes only
        //        until the JDO spec precisely defines how to treat
        //        user-defined clone methods in transient classes
        final boolean implementsPersistence = ca.getImplementsPersistence();
        if (false) {
            System.out.println("    check for annotating clone()");//NOI18N
            System.out.println("    methodName = " + methodName);//NOI18N
            System.out.println("    methodSig = " + methodSig);//NOI18N
            System.out.println("    implementsPersistence = "//NOI18N
                               + implementsPersistence);
        }
        if (methodName.equals("clone")//NOI18N
            && methodSig.equals("()Ljava/lang/Object;")//NOI18N
            && implementsPersistence) {
            //^olsen: rather scan for 'invokespecial clone()Ljava/lang/Object;'
            //        in instruction loop above
            allFlags |= SuperClone;
        }

//@olsen: disabled feature
/*
        if (methodName.equals("clone") &&
            methodSig.equals("()Ljava/lang/Object;") &&
            ca.persistCapable()) {
            allFlags |= FetchThis;
            fetchThis = true;
        }
*/
        annotate = allFlags;
    
voidcheckMethod()
Examine the method to determine what sort of annotations are needed

        //@olsen: added printing output
        env.message(
            "checking method " + ca.userClassName()//NOI18N
            + "." + method.name().asString()//NOI18N
            + Descriptor.userMethodArgs(method.signature().asString()));

        //@olsen: cosmetics
        annotate = 0;
        final CodeAttribute codeAttr = method.codeAttribute();
        if (codeAttr == null) {
            return;
        }

//^olsen: make robust
/*
        if (isAnnotated(codeAttr)) {
            env.message("Method " + ca.userClassName() +
                        "." + method.name().asString() +
                        Descriptor.userMethodArgs(method.signature().asString()) +
                        " is already annotated.");
            return;
        }
*/

        // look for certain special cases to avoid
        //@olsen: cosmetics
        if (avoidAnnotation()) {
            return;
        }
        checkCode(codeAttr);

//@olsen: disabled feature
/*
        if (!avoidAnnotation()) {
            largestLoop = Loop.checkLoops(codeAttr.theCode());
            checkCode(codeAttr);
        } else if (methodIsPersistentFinalize()) {
            annotate = MakeThisTransient;
        }
*/
    
private final voidcopyStack(java.util.Stack fromStack, java.util.Stack toStack)

        while (!toStack.empty())
            toStack.pop();

        // take advantage of Stack's inheritance from Vector
        for (int i=0; i<fromStack.size(); i++)
            toStack.addElement(fromStack.elementAt(i));
    
private InsnfindArgDepositer(Insn currInsn, int argDepth)
Attempt to locate the instruction which deposits to the top of stack the 1 word stack argument to currInsn which is argDepth deep on the stack (top of stack == 0). If unable to determine this with confidence, return null. Note that this method will not look back past a target. Also, the operations performed by the dup2, dup_x1, dup_x2, dup2_x1, dup2_x2 instructions are currently not understood by this method so we don't attempt to chain back through these instructions.

        Insn depositer = null;
        for (Insn i = currInsn.prev(); argDepth >= 0; i = i.prev()) {
            // At control flow branch/merge points, abort the search for the
            // target operand.
            if (i.branches() ||
                ((i instanceof InsnTarget) && ((InsnTarget)i).isBranchTarget()))
                break;

            int nArgs = i.nStackArgs();
            int nResults = i.nStackResults();

            if (argDepth - nResults < 0) {
                // This instruction does deposit the value
                // For now, don't return depositers other than opc_dup which
                // deposit more than one value.  These are the
                // long/doubleinstructions (which can't be depositing a one
                // word value) and the dupX variants
                if (nResults > 1 && i.opcode() != opc_dup)
                    break;
                depositer = i;

                // consider special cases which may cause us to look further
                switch (i.opcode()) {
                case opc_dup:
                    if (argDepth == 0)
                        // keep going to find the real depositer at a greater depth
                        argDepth++;
                    break;
                case opc_checkcast:
                    // keep going to find the real depositer
                    break;
                default:
                    return i;
                }
            }

            argDepth += (nArgs - nResults);
        }

        return depositer;
    
private final com.sun.jdo.api.persistence.enhancer.impl.InsnNotegetNoteList(Insn insn)
Find the note list for the specified instruction.

        return (InsnNote)insnNotes.get(insn);
    
private InsninsnAnnotation(Insn insn)
Generate annotations if needed for the instruction.

        // The note list should be sorted in order of decreasing arg depth

        int initialSingleRegs = 0;

//@olsen: ensured to use single note only (as instantiated)
/*
        for (InsnNote note = getNoteList(insn);
             note != null;
             note = note.next()) { ... }
*/
        InsnNote note = getNoteList(insn);
        if (note == null)
            return insn;

        //@olsen: ensured to use single note only (as instantiated)
        affirm(insn == note.insn);
        affirm((note.next() == null),
               "Number of annotation notes for instruction > 1.");//NOI18N

        //@olsen: not needed to ensure: note.dirtyThis() && !method.isStatic()
        final boolean fetch = (note.fetchPersistent() || note.fetchThis());
        final boolean dirty = (note.dirtyPersistent() || note.dirtyThis());
        //@olsen: added consistency check
        affirm((fetch ^ dirty),
               "Inconsistent fetch/dirty flags.");//NOI18N

        //@olsen: added checks
        final boolean dfgField = note.dfgFieldAccess();
        final boolean pkField = note.pkFieldAccess();

        //@olsen: added println() for debugging
        if (false) {
            final String targetClassName = note.targetClassName;
            final String targetFieldName = note.targetFieldName;
            //final String targetPCRootClass = note.targetPCRootClass;

            System.out.println("    build annotation: "//NOI18N
                               + targetClassName
                               + "." + targetFieldName + " : "//NOI18N
                               + (pkField ? "pk," : "!pk,")//NOI18N
                               + (dfgField ? "dfg," : "!dfg,")//NOI18N
                               + (fetch ? "fetch " : "dirty ")//NOI18N
                               + (note.fetchPersistent()
                                  ? "persistent" : "this")//NOI18N
                               + ";");//NOI18N
        }

        //@olsen: improved control flow
        //@olsen: 4385427: do not enhance PK read access at all
        if (pkField && fetch) {
            return insn;
        }

        //@olsen: enhance for mediated access
        //@olsen: enhance PK write as mediated access
        //@olsen: added: mediated getfield/putfield insn annotation
        if (pkField || !dfgField) {
            //insn.prev().insert(Insn.create(opc_nop));
            //@olsen: 4429769: drop putfield instruction on mediated write
            //        access; isolate the get/putfield instruction to allow
            //        to now be inserted by buildAccessAnnotation() itself
            final Insn prev = insn.prev();
            insn.remove();

            //@olsen: changed not to return null
            final AnnotationFragment frag1 = buildAccessAnnotation(note);
            affirm(frag1, "Illegal annotation of PK or non-dfg field.");//NOI18N

            //@olsen: 4429769, replace current instruction with fragment
            //insn.prev().insert(frag1.annotation);
            //noteStack(frag1.stackRequired - note.arg());
            //return insn;
            final Insn last = prev.insert(frag1.annotation);
            noteStack(frag1.stackRequired - note.arg());
            return last;
        }

        // do basic annotation
        //@olsen: enhance for non-mediated access
        final AnnotationFragment frag0 = buildBasicAnnotation(note);
        //@olsen: changed not to return null
        affirm(frag0, "Illegal annotation of dfg field.");//NOI18N
        //if (frag0 != null) {
        {
            // Attempt to find an instruction where the argument is known
            // to be on the top of stack
            StackState state
                = new StackState(note.arg(), note.sig(), insn.prev());
            minimizeStack(state);

            if (false) {
                System.out.println("        state.argDepth =  "//NOI18N
                                   + state.argDepth);
                System.out.print("        state.insn = ");//NOI18N
                state.insn.printInsn(System.out);
                System.out.print("        insn = ");//NOI18N
                insn.printInsn(System.out);
            }

            // generate the necessary instructions
            Insn annotation = null;
            if (state.argDepth == 0) {
                // The value is on top of the stack - the dup in the basic
                // annotation fragment will suffice
                annotation = frag0.annotation;
                noteStack(frag0.stackRequired);
            } else if (state.argDepth == 1) {
                // The value on top of the stack is one deep.  Because the
                // operand of interest is also a single word value we can
                // simply execute a swap operation to get access to the
                // operand on top of the stack
                annotation = Insn.create(opc_swap);
                annotation.append(frag0.annotation);
                annotation.append(Insn.create(opc_swap));

                // reduce the code fragment's stack requirements by
                // the amount that minimizeStack reduced the stack depth,
                // since that is the context in which the code fragment
                // will run.
                noteStack(frag0.stackRequired - (note.arg()-1));
            }  else {
                // The value is hidden by 2 or more stack operands.  Move
                // the obscuring values into temporaries to get access to
                // the value - put them back when done
                Stack stackTypes = state.stackTypes;
                int depth = state.argDepth;
                int elem = stackTypes.size()-1;

                int singleRegs = initialSingleRegs;
                int doubleRegs = 0;
                int regnums[] = new int[depth];
                int regtotal = 0;

                // Now, move values into temp registers
                while (depth > 0) {
                    int elemType =
                        ((Integer)stackTypes.elementAt(elem--)).intValue();
                    int elemSize = Descriptor.elementSize(elemType);
                    depth -= elemSize;
                    int reg = ((elemSize == 1)
                               ? tmpReg(singleRegs++)
                               : tmpReg2(doubleRegs++));
                    regnums[regtotal++] = reg;

                    Insn store = InsnUtils.store(elemType, reg, pool);
                    if (annotation == null)
                        annotation = store;
                    else
                        annotation.append(store);
                }
                affirm((depth >= 0),
                       "Stack underflow while computing save registers");//NOI18N

                annotation.append(frag0.annotation);

                while (regtotal > 0)
                    annotation.append(InsnUtils.load(
                        ((Integer)stackTypes.elementAt(++elem)).intValue(),
                        regnums[--regtotal], pool));

                noteStack(frag0.stackRequired - note.arg());
            }

            state.insn.insert(annotation);
        }

        return insn;
    
private final booleanknownTypes(java.util.Stack stack, int nWords)

        // take advantage of Stack's inheritance from Vector
        for (int i=stack.size()-1; i>= 0 && nWords > 0; i--) {
            int words = 0;
            switch (((Integer)stack.elementAt(i)).intValue()) {
            case T_UNKNOWN:
            case T_WORD:
            case T_TWOWORD:
                return false;

            case T_BOOLEAN:
            case T_CHAR:
            case T_FLOAT:
            case T_BYTE:
            case T_SHORT:
            case T_INT:
            case TC_OBJECT:
            case TC_INTERFACE:
            case TC_STRING:
                words = 1;

            case T_DOUBLE:
            case T_LONG:
                words = 2;

            default:
                break;
            }
            nWords -= words;
        }
        return true;
    
private voidminimizeStack(com.sun.jdo.api.persistence.enhancer.impl.StackState state)
Assume that after the execution of state.insn there is a word on the stack which is state.argDepth words deep. Scan backwards through the instruction sequence, attempting to locate an instruction after which the argument is at a minimal depth w.r.t. the top of stack. Update the state to indicate progress. Note that this method will not look back past a target.

        Insn i = state.insn;
        int argDepth = state.argDepth;

        Stack argTypesStack = new Stack();
        Stack resultTypesStack = new Stack();
        Stack stackTypes = new Stack();
        copyStack(state.stackTypes, stackTypes);

        for (; argDepth > 0; i = i.prev()) {
            // At control flow branch/merge points, abort the search for the
            // target operand.  The caller will have to make do with the best
            // stack state computed thus far.
            if (i.branches() ||
                ((i instanceof InsnTarget)
                 && ((InsnTarget)i).isBranchTarget()))
                break;

            int nArgs = i.nStackArgs();
            int nResults = i.nStackResults();
            String argTypes = i.argTypes();
            String resultTypes = i.resultTypes();

            argDepth -= nResults;
            // If the target argument was placed there by an instruction which
            // deposited multiple results (one of the dup type instructions)
            // then we don't have the smarts to figure out where it came from
            // so just quit looking
            if (argDepth < 0)
                break;
            argDepth += nArgs;

            if (i.opcode() == opc_swap) {
                Object x = stackTypes.pop();
                Object y = stackTypes.pop();
                stackTypes.push(x);
                stackTypes.push(y);
            } else {
                // Make sure the arg types and result types stacks are empty
                while (!argTypesStack.empty()) argTypesStack.pop();
                while (!resultTypesStack.empty()) resultTypesStack.pop();

                Descriptor.computeStackTypes(argTypes, argTypesStack);
                Descriptor.computeStackTypes(resultTypes, resultTypesStack);

                int expectWords = 0;
                while (!resultTypesStack.empty())
                    expectWords += Descriptor.elementSize(
                        ((Integer) resultTypesStack.pop()).intValue());

                while (expectWords > 0)
                    expectWords -= Descriptor.elementSize(
                        ((Integer) stackTypes.pop()).intValue());

                if (expectWords < 0) {
                    // perhaps we ought to signal an exception, but returning
                    // will keep things going just fine.
                    return;
                }

                transferStackArgs(argTypesStack, stackTypes);
            }

            if (argDepth >= 0 && argDepth < state.argDepth &&
                knownTypes(stackTypes, argDepth)) {
                state.argDepth = argDepth;
                state.insn = i.prev();
                copyStack(stackTypes, state.stackTypes);
            }
        }
    
booleanneedsAnnotation()
Is any annotation needed for this method? The result of this method isn't valid until after checkMethod has been run.


    /* The largest loop contained within the method, or null.  */
//@olsen: disabled feature
/*
    private Loop largestLoop;
*/

    // package accessors

                              
      
        return annotate != 0;
    
private com.sun.jdo.api.persistence.enhancer.impl.InsnNotenoteGetFieldAnnotation(Insn insn)
make note of annotations if needed for the getField instruction.

        //@olsen: cosmetics
        final InsnConstOp getFieldInsn = (InsnConstOp)insn;
        final ConstFieldRef fieldRef = (ConstFieldRef)getFieldInsn.value();
        final String fieldOf = fieldRef.className().asString();

        //@olsen: changed to use JDOMetaData
        final String fieldName
            = fieldRef.nameAndType().name().asString();
        final JDOMetaData meta = env.getJDOMetaData();
        if (!meta.isPersistentField(fieldOf, fieldName))
            return null;

//@olsen: disabled feature
/*
        final ClassControl cc = env.findClass(fieldOf);

        if (cc == null || !cc.persistCapable())
            return null;
*/

        //@olsen: added checks
        final boolean dfgField
            = meta.isDefaultFetchGroupField(fieldOf, fieldName);
        final boolean pkField
            = meta.isPrimaryKeyField(fieldOf, fieldName);
        final int fieldIndex
            = meta.getFieldNo(fieldOf, fieldName);
        final String targetPCRootClass
            = meta.getPersistenceCapableRootClass(fieldOf);

        int flags = 0;
        //@olsen: added variables
        final String fieldSig = fieldRef.nameAndType().signature().asString();
        // there's no field value on the stack yet
        final int stackArgSize = 0;

        //@olsen: added println() for debugging
        if (false) {
            System.out.println("    get field "//NOI18N
                               + fieldOf + "." + fieldName//NOI18N
                               + "[" + fieldIndex + "]"//NOI18N
                               + "<" + fieldSig + ">"//NOI18N
                               + " : p"//NOI18N
                               + (dfgField ? ",dfg" : ",!dfg")//NOI18N
                               + (pkField ? ",pk" : ",!pk")//NOI18N
                               + ";");//NOI18N
        }

        Insn dep = findArgDepositer(insn, stackArgSize);
        if (dep != null
            && dep.opcode() == opc_aload_0
            && !method.isStatic())
            // This represents a fetch of "this"
            flags |= FetchThis;
        else
            flags |= FetchPersistent;

        //@olsen: added test
        if (dfgField)
            flags |= DFGField;

        //@olsen: added test
        if (pkField)
            flags |= PKField;

        //@olsen: changed to use JDOMetaData
        return new InsnNote(insn, flags,
                            stackArgSize,
                            fieldSig, fieldOf, fieldName, fieldIndex,
                            targetPCRootClass);
//@olsen: disabled feature
/*
        return new InsnNote(insn, flags, 0, "", cc.action());
*/
    
private com.sun.jdo.api.persistence.enhancer.impl.InsnNotenotePutFieldAnnotation(Insn insn)
Generate annotations if needed for the putField instruction.

        //@olsen: cosmetics
        final InsnConstOp putFieldInsn = (InsnConstOp)insn;
        final ConstFieldRef fieldRef = (ConstFieldRef)putFieldInsn.value();
        final String fieldOf = fieldRef.className().asString();

        //@olsen: changed to use JDOMetaData
        final String fieldName
            = fieldRef.nameAndType().name().asString();
        final JDOMetaData meta = env.getJDOMetaData();
        if (!meta.isPersistentField(fieldOf, fieldName))
            return null;

//@olsen: disabled feature
/*
        final ClassControl cc = env.findClass(fieldOf);

        if (cc == null || !cc.persistCapable())
            return null;
*/

        //@olsen: added checks
        final boolean dfgField
            = meta.isDefaultFetchGroupField(fieldOf, fieldName);
        final boolean pkField
            = meta.isPrimaryKeyField(fieldOf, fieldName);
        final int fieldIndex
            = meta.getFieldNo(fieldOf, fieldName);
        final String targetPCRootClass
            = meta.getPersistenceCapableRootClass(fieldOf);

        int flags = 0;
        //@olsen: added variables
        final String fieldSig = fieldRef.nameAndType().signature().asString();
        // size of field value on the stack
        final int stackArgSize
            = (fieldSig.equals("J") || fieldSig.equals("D")) ? 2 : 1;//NOI18N

        //@olsen: added println() for debugging
        if (false) {
            System.out.println("    put field "//NOI18N
                               + fieldOf + "." + fieldName//NOI18N
                               + "[" + fieldIndex + "]"//NOI18N
                               + "<" + fieldSig + ">"//NOI18N
                               + " : p"//NOI18N
                               + (dfgField ? ",dfg" : ",!dfg")//NOI18N
                               + (pkField ? ",pk" : ",!pk")//NOI18N
                               + ";");//NOI18N
        }

        Insn dep = findArgDepositer(insn, stackArgSize);
        if (dep != null
            && dep.opcode() == opc_aload_0
            && !method.isStatic())
            // This represents a dirtyfication of "this"
            flags |= DirtyThis;
        else
            flags |= DirtyPersistent;

        //@olsen: added test
        if (dfgField)
            flags |= DFGField;

        //@olsen: added test
        if (pkField)
            flags |= PKField;

        //@olsen: changed to use JDOMetaData
        return new InsnNote(insn, flags,
                            stackArgSize,
                            fieldSig, fieldOf, fieldName, fieldIndex,
                            targetPCRootClass);
//@olsen: disabled feature
/*
        return new InsnNote(insn, flags, stackArgSize, fieldSig, cc.action());
*/
    
private voidnoteStack(int stk)
Note the following amount of stack used by a single annotation.

        if (stk > annotationStack)
            annotationStack = (short)stk;
    
private inttmpReg(int idx)
Allocate a one word temporary register

param
idx the index of the temporary register to return. If the specified temporary hasn't been allocated, allocated it now.

        if (tmpRegisters == null)
            tmpRegisters = new Vector(3);

        // allocate as many registers as necessary in order to
        // make idx be a valid index
        while (tmpRegisters.size() <= idx) {
            final CodeAttribute codeAttr = method.codeAttribute();
            final int reg = codeAttr.localsUsed();
            tmpRegisters.addElement(new Integer(reg));
            codeAttr.setLocalsUsed(reg+1);
        }
        return ((Integer)tmpRegisters.elementAt(idx)).intValue();
    
private inttmpReg2(int idx)
Allocate a two word temporary register

param
idx the index of the temporary register to return. If the specified temporary hasn't been allocated, allocated it now.

        if (tmpDoubleRegisters == null)
            tmpDoubleRegisters = new Vector(3);

        // allocated as many 2 register pairs as necessary in order to
        // make idx be a valid index
        while (tmpDoubleRegisters.size() <= idx) {
            final CodeAttribute codeAttr = method.codeAttribute();
            final int reg = codeAttr.localsUsed();
            tmpDoubleRegisters.addElement(new Integer(reg));
            codeAttr.setLocalsUsed(reg+2);
        }

        return ((Integer)tmpDoubleRegisters.elementAt(idx)).intValue();
    
private final voidtransferStackArgs(java.util.Stack fromStack, java.util.Stack toStack)

        if (!fromStack.empty()) {
            Object o = fromStack.pop();
            transferStackArgs(fromStack, toStack);
            toStack.push(o);
        }