You are viewing a plain text version of this content. The canonical link for it is here.
Posted to jdo-commits@db.apache.org by mb...@apache.org on 2005/05/22 19:55:54 UTC

svn commit: r171351 [10/16] - in /incubator/jdo/trunk/enhancer20: ./ src/ src/conf/ src/java/ src/java/org/ src/java/org/apache/ src/java/org/apache/jdo/ src/java/org/apache/jdo/enhancer/ src/java/org/apache/jdo/impl/ src/java/org/apache/jdo/impl/enhancer/ src/java/org/apache/jdo/impl/enhancer/classfile/ src/java/org/apache/jdo/impl/enhancer/core/ src/java/org/apache/jdo/impl/enhancer/generator/ src/java/org/apache/jdo/impl/enhancer/meta/ src/java/org/apache/jdo/impl/enhancer/meta/model/ src/java/org/apache/jdo/impl/enhancer/meta/prop/ src/java/org/apache/jdo/impl/enhancer/meta/util/ src/java/org/apache/jdo/impl/enhancer/util/ test/ test/sempdept/ test/sempdept/src/ test/sempdept/src/empdept/

Added: incubator/jdo/trunk/enhancer20/src/java/org/apache/jdo/impl/enhancer/core/Builder.java
URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/enhancer20/src/java/org/apache/jdo/impl/enhancer/core/Builder.java?rev=171351&view=auto
==============================================================================
--- incubator/jdo/trunk/enhancer20/src/java/org/apache/jdo/impl/enhancer/core/Builder.java (added)
+++ incubator/jdo/trunk/enhancer20/src/java/org/apache/jdo/impl/enhancer/core/Builder.java Sun May 22 10:55:51 2005
@@ -0,0 +1,4716 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ * 
+ * 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. 
+ * See the License for the specific language governing permissions and 
+ * limitations under the License.
+ */
+
+package org.apache.jdo.impl.enhancer.core;
+
+import java.util.HashMap;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Enumeration;
+
+import org.apache.jdo.impl.enhancer.classfile.AttributeVector;
+import org.apache.jdo.impl.enhancer.classfile.ClassField;
+import org.apache.jdo.impl.enhancer.classfile.ClassFile;
+import org.apache.jdo.impl.enhancer.classfile.CodeAttribute;
+import org.apache.jdo.impl.enhancer.classfile.ConstClass;
+import org.apache.jdo.impl.enhancer.classfile.ConstFieldRef;
+import org.apache.jdo.impl.enhancer.classfile.ConstNameAndType;
+import org.apache.jdo.impl.enhancer.classfile.ConstUtf8;
+import org.apache.jdo.impl.enhancer.classfile.ConstantPool;
+import org.apache.jdo.impl.enhancer.classfile.Descriptor;
+import org.apache.jdo.impl.enhancer.classfile.ExceptionRange;
+import org.apache.jdo.impl.enhancer.classfile.ExceptionTable;
+import org.apache.jdo.impl.enhancer.classfile.ExceptionsAttribute;
+import org.apache.jdo.impl.enhancer.classfile.Insn;
+import org.apache.jdo.impl.enhancer.classfile.InsnIInc;
+import org.apache.jdo.impl.enhancer.classfile.InsnInterfaceInvoke;
+import org.apache.jdo.impl.enhancer.classfile.InsnLookupSwitch;
+import org.apache.jdo.impl.enhancer.classfile.InsnTableSwitch;
+import org.apache.jdo.impl.enhancer.classfile.InsnTarget;
+import org.apache.jdo.impl.enhancer.classfile.InsnUtils;
+import org.apache.jdo.impl.enhancer.classfile.VMConstants;
+import org.apache.jdo.impl.enhancer.util.InternalError;
+import org.apache.jdo.impl.enhancer.util.Support;
+
+/**
+ * Helper object to create the generic JDO methods for a class.
+ */
+class Builder
+    extends Support
+    implements VMConstants, JDOConstants, EnhancerConstants
+{
+    /**
+     * The augmentation controller for this class.
+     */
+    private final Augmenter augmenter;
+
+    /**
+     * The class analyzer for this class.
+     */
+    private final Analyzer analyzer;
+
+    /**
+     * The classfile to be annotated.
+     */
+    private final ClassFile classFile;
+
+    /**
+     * The class name in VM form.
+     */
+    private final String className;
+
+    /**
+     * The class name in user ('.' delimited) form.
+     */
+    private final String userClassName;
+
+    /**
+     * The classfile's constant pool.
+     */
+    private final ConstantPool pool;
+
+    /**
+     * Repository for the enhancement options.
+     */
+    private final Environment env;
+
+    /**
+     * The constant utf8 string for the CodeAttribute.
+     */
+    private ConstUtf8 codeAttributeUtf8;
+    /**
+
+     * The constant field ref for the jdoStateManager field.
+     */
+    private ConstFieldRef jdoStateManagerFieldRef;
+
+    /**
+     * The constant field ref for the jdoFlags field.
+     */
+    private ConstFieldRef jdoFlagsFieldRef;
+
+    /**
+     * The constant field ref for the jdoFieldNames field.
+     */
+    private ConstFieldRef jdoFieldNamesFieldRef;
+
+    /**
+     * The constant field ref for the jdoFieldTypes field.
+     */
+    private ConstFieldRef jdoFieldTypesFieldRef;
+
+    /**
+     * The constant field ref for the jdoFieldFlags field.
+     */
+    private ConstFieldRef jdoFieldFlagsFieldRef;
+
+    /**
+     * The constant field ref for the jdoPersistenceCapableSuperclass field.
+     */
+    private ConstFieldRef jdoPersistenceCapableSuperclassFieldRef;
+
+    /**
+     * The constant field refs for the annotated fields sorted by their
+     * relative field index.
+     */
+    private ConstFieldRef[] annotatedFieldRefs;
+
+    /**
+     * The constant field refs for the key fields sorted by
+     * ascending relative field index.
+     */
+    private ConstFieldRef[] keyFieldRefs;
+
+    /**
+     * The constant field refs on the key class for the key fields sorted by
+     * ascending relative field index.
+     */
+    private ConstFieldRef[] keyClassKeyFieldRefs;
+
+    /**
+     * Constructor.
+     */
+    public Builder(Analyzer analyzer,
+                   Augmenter augmenter,
+                   Environment env)
+    {
+        affirm(analyzer != null);
+        affirm(augmenter != null);
+        affirm(env != null);
+
+        this.analyzer = analyzer;
+        this.augmenter = augmenter;
+        this.classFile = analyzer.getClassFile();
+        this.className = classFile.classNameString();
+        this.userClassName = classFile.userClassName();
+        this.pool = classFile.pool();
+        this.env = env;
+
+        affirm(classFile != null);
+        affirm(className != null);
+        affirm(userClassName != null);
+        affirm(pool != null);
+    }
+
+    // ----------------------------------------------------------------------
+    // Internal Helper Methods
+    // ----------------------------------------------------------------------
+
+    /**
+     * Holder object for returning a size info from a code generation method.
+     */
+    static private class SizeHolder
+    {
+        int size;
+    }
+
+    /**
+     * Returns the minimum of two numbers.
+     */
+    static private int min(int i, int j) {
+        return (i < j ? i : j);
+    }
+
+    /**
+     * Returns the maximum of two numbers.
+     */
+    static private int max(int i, int j) {
+        return (i < j ? j : i);
+    }
+
+    /**
+     * Count the size of the arguments to an invokevirtual method call.
+     */
+    static private int countMethodArgWords(String sig) 
+    {
+        // the 'this' pointer is to be accounted too
+        return Descriptor.countMethodArgWords(sig) + 1;
+    }    
+
+    /**
+     * Returns the utf8 string for the CodeAttribute.
+     */
+    private ConstUtf8 getCodeAttributeUtf8()
+    {
+        // create utf8 in constant pool if not done yet
+        if (codeAttributeUtf8 == null) {
+            codeAttributeUtf8 = pool.addUtf8(CodeAttribute.expectedAttrName);
+        }
+        return codeAttributeUtf8;
+    }
+
+    /**
+     * Returns the constant field ref for the jdoStateManager field.
+     */
+    private ConstFieldRef getjdoStateManagerFieldRef()
+    {
+        //^olsen: javac uses the truelly declaring class
+        final String pcRootName = analyzer.getPCRootClassName();
+        affirm(pcRootName != null);
+
+        // create field reference in constant pool if not done yet
+        if (jdoStateManagerFieldRef == null) {
+            jdoStateManagerFieldRef
+                = pool.addFieldRef(pcRootName, //className,
+                                   JDO_PC_jdoStateManager_Name,
+                                   JDO_PC_jdoStateManager_Sig);
+        }
+        return jdoStateManagerFieldRef;
+    }
+
+    /**
+     * Returns the constant field ref for the jdoFlags field.
+     */
+    private ConstFieldRef getjdoFlagsFieldRef()
+    {
+        //^olsen: javac uses the truelly declaring class
+        final String pcRootName = analyzer.getPCRootClassName();
+        affirm(pcRootName != null);
+
+        // create field reference in constant pool if not done yet
+        if (jdoFlagsFieldRef == null) {
+            jdoFlagsFieldRef
+                = pool.addFieldRef(pcRootName, //className,
+                                   JDO_PC_jdoFlags_Name,
+                                   JDO_PC_jdoFlags_Sig);
+        }
+        return jdoFlagsFieldRef;
+    }
+
+    /**
+     * Returns the constant field ref for the jdoFieldNames field.
+     */
+    private ConstFieldRef getjdoFieldNamesFieldRef()
+    {
+        // create field reference in constant pool if not done yet
+        if (jdoFieldNamesFieldRef == null) {
+            jdoFieldNamesFieldRef
+                = pool.addFieldRef(className,
+                                   JDO_PC_jdoFieldNames_Name,
+                                   JDO_PC_jdoFieldNames_Sig);
+        }
+        return jdoFieldNamesFieldRef;
+    }
+
+    /**
+     * Returns the constant field ref for the jdoFieldTypes field.
+     */
+    private ConstFieldRef getjdoFieldTypesFieldRef()
+    {
+        // create field reference in constant pool if not done yet
+        if (jdoFieldTypesFieldRef == null) {
+            jdoFieldTypesFieldRef
+                = pool.addFieldRef(className,
+                                   JDO_PC_jdoFieldTypes_Name,
+                                   JDO_PC_jdoFieldTypes_Sig);
+        }
+        return jdoFieldTypesFieldRef;
+    }
+
+    /**
+     * Returns the constant field ref for the jdoFieldFlags field.
+     */
+    private ConstFieldRef getjdoFieldFlagsFieldRef()
+    {
+        // create field reference in constant pool if not done yet
+        if (jdoFieldFlagsFieldRef == null) {
+            jdoFieldFlagsFieldRef
+                = pool.addFieldRef(className,
+                                   JDO_PC_jdoFieldFlags_Name,
+                                   JDO_PC_jdoFieldFlags_Sig);
+        }
+        return jdoFieldFlagsFieldRef;
+    }
+
+    /**
+     * Returns the constant field ref for the jdoPersistenceCapableSuperclass field.
+     */
+    private ConstFieldRef getjdoPersistenceCapableSuperclassFieldRef()
+    {
+        // create field reference in constant pool if not done yet
+        if (jdoPersistenceCapableSuperclassFieldRef == null) {
+            jdoPersistenceCapableSuperclassFieldRef
+                = pool.addFieldRef(className,
+                                   JDO_PC_jdoPersistenceCapableSuperclass_Name,
+                                   JDO_PC_jdoPersistenceCapableSuperclass_Sig);
+        }
+        return jdoPersistenceCapableSuperclassFieldRef;
+    }
+
+    /**
+     * Returns the constant field refs for the annotated fields.
+     */
+    private ConstFieldRef[] getAnnotatedFieldRefs()
+    {
+        // create field references in constant pool if not done yet
+        if (annotatedFieldRefs == null) {
+            final int annotatedFieldCount = analyzer.getAnnotatedFieldCount();
+            final String[] annotatedFieldNames
+                = analyzer.getAnnotatedFieldNames();
+            final String[] annotatedFieldSigs
+                = analyzer.getAnnotatedFieldSigs();
+            affirm(annotatedFieldNames.length == annotatedFieldCount);
+            affirm(annotatedFieldSigs.length == annotatedFieldCount);
+            
+            // add field references to constant pool
+            annotatedFieldRefs = new ConstFieldRef[annotatedFieldCount];
+            for (int i = 0; i < annotatedFieldCount; i++) {
+                final String name = annotatedFieldNames[i];
+                final String sig = annotatedFieldSigs[i];
+                annotatedFieldRefs[i] = pool.addFieldRef(className, name, sig);
+                affirm(annotatedFieldRefs[i] != null);
+            }
+        }
+        affirm(annotatedFieldRefs != null);
+        return annotatedFieldRefs;
+    }
+
+    /**
+     * Returns the constant field refs for the key fields.
+     */
+    private ConstFieldRef[] getKeyFieldRefs()
+    {
+        // get field references if not done yet
+        if (keyFieldRefs == null) {
+            final ConstFieldRef[] annotatedFieldRefs = getAnnotatedFieldRefs();
+            final int keyFieldCount = analyzer.getKeyFieldCount();
+            final int[] keyFieldIndexes = analyzer.getKeyFieldIndexes();
+            affirm(keyFieldIndexes.length == keyFieldCount);
+            
+            // add field references
+            keyFieldRefs = new ConstFieldRef[keyFieldCount];
+            for (int i = 0; i < keyFieldCount; i++) {
+                keyFieldRefs[i] = annotatedFieldRefs[keyFieldIndexes[i]];
+                affirm(keyFieldRefs[i] != null);
+            }
+        }
+        affirm(keyFieldRefs != null);
+        return keyFieldRefs;
+    }
+
+    /**
+     * Returns the constant field refs for the key fields of the key class.
+     */
+    private ConstFieldRef[] getKeyClassKeyFieldRefs()
+    {
+        // get field references if not done yet
+        if (keyClassKeyFieldRefs == null) {
+            final String keyClassName = analyzer.getKeyClassName();
+            affirm(keyClassName != null);
+            final int keyFieldCount = analyzer.getKeyFieldCount();
+            final ConstFieldRef[] keyFieldRefs = getKeyFieldRefs();
+            affirm(keyFieldRefs.length == keyFieldCount);
+            
+            // add field references
+            keyClassKeyFieldRefs = new ConstFieldRef[keyFieldCount];
+            for (int i = 0; i < keyFieldCount; i++) {
+                final ConstNameAndType nt = keyFieldRefs[i].nameAndType();
+                final String name = nt.name().asString();
+                final String sig = nt.signature().asString();
+                keyClassKeyFieldRefs[i]
+                    = pool.addFieldRef(keyClassName, name, sig);
+                affirm(keyClassKeyFieldRefs[i] != null);
+            }
+        }
+        affirm(keyClassKeyFieldRefs != null);
+        return keyClassKeyFieldRefs;
+    }
+
+    /**
+     * Adds the code for throwing a IllegalArgumentException.
+     */
+    private Insn appendThrowJavaException(Insn insn,
+                                          String exceptionName,
+                                          String exceptionText)
+    {
+        affirm(insn != null);
+        affirm(exceptionName != null);
+        affirm(exceptionText != null);
+
+        // throw exception
+        final String exceptionCtorName
+            = NameHelper.constructorName();
+        final String exceptionCtorSig
+            = NameHelper.constructorSig(JAVA_String_Sig);
+        insn = insn.append(
+            Insn.create(opc_new,
+                        pool.addClass(exceptionName)));
+        insn = insn.append(Insn.create(opc_dup));
+        insn = insn.append(
+            InsnUtils.stringConstant(
+                exceptionText, pool));
+        insn = insn.append(
+            Insn.create(opc_invokespecial,
+                        pool.addMethodRef(
+                            exceptionName,
+                            exceptionCtorName,
+                            exceptionCtorSig)));
+        insn = insn.append(Insn.create(opc_athrow));
+
+        affirm(insn != null);
+        return insn;
+    }
+
+    /**
+     * Adds the code for handling if jdoStateManager field is null.
+     */
+    private Insn appendCheckStateManager(Insn insn,
+                                         int argStart,
+                                         String exceptionName,
+                                         String exceptionText)
+    {
+        affirm(insn != null);
+        affirm(exceptionName != null);
+        affirm(exceptionText != null);
+        
+        // throw exception if sm == null
+        final InsnTarget body = new InsnTarget();
+        insn = insn.append(InsnUtils.aLoad(argStart, pool));
+        insn = insn.append(
+            Insn.create(
+                opc_getfield,
+                getjdoStateManagerFieldRef()));
+        insn = insn.append(Insn.create(opc_ifnonnull, body));
+        insn = appendThrowJavaException(insn, exceptionName, exceptionText);
+        insn = insn.append(body);
+
+        affirm(insn != null);
+        return insn;
+    }
+
+    /**
+     * Adds the code for handling if an argument is null.
+     */
+    private Insn appendCheckVarNonNull(Insn insn,
+                                       int argStart,
+                                       String exceptionName,
+                                       String exceptionText)
+    {
+        affirm(insn != null);
+        affirm(exceptionName != null);
+        affirm(exceptionText != null);
+
+        // throw exception if obj == null
+        final InsnTarget body = new InsnTarget();
+        insn = insn.append(InsnUtils.aLoad(argStart, pool));
+        insn = insn.append(Insn.create(opc_ifnonnull, body));
+        insn = appendThrowJavaException(insn, exceptionName, exceptionText);
+        insn = insn.append(body);
+
+        affirm(insn != null);
+        return insn;
+    }
+
+    /**
+     * Adds the code for handling if an argument is instance of a class.
+     */
+    private Insn appendCheckVarInstanceOf(Insn insn,
+                                          int argStart,
+                                          ConstClass constClass,
+                                          String exceptionName,
+                                          String exceptionText)
+    {
+        affirm(insn != null);
+        affirm(constClass != null);
+        affirm(exceptionName != null);
+        affirm(exceptionText != null);
+
+        // throw exception if obj not instance of class
+        final InsnTarget body = new InsnTarget();
+        insn = insn.append(InsnUtils.aLoad(argStart, pool));
+        insn = insn.append(Insn.create(opc_instanceof, constClass));
+        insn = insn.append(Insn.create(opc_ifne, body));
+        insn = appendThrowJavaException(insn, exceptionName, exceptionText);
+        insn = insn.append(body);
+
+        affirm(insn != null);
+        return insn;
+    }
+
+    // ----------------------------------------------------------------------
+
+    /**
+     * Builds an empty method (for debugging).
+     *
+     * public void XXX() {
+     * }
+     */
+    public void addNullMethod(final String methodName,
+                              final String methodSig,
+                              final int accessFlags)
+    {
+        // assumed nonstatic call; otherwise subtract 'this' from maxStack
+        affirm((accessFlags & ACCStatic) == 0);
+        final ExceptionsAttribute exceptAttr = null;
+
+        // begin of method body
+        final InsnTarget begin = new InsnTarget();
+        Insn insn = begin;
+
+        // end of method body
+        insn = insn.append(Insn.create(opc_return));
+
+        final CodeAttribute codeAttr
+            = new CodeAttribute(getCodeAttributeUtf8(),
+                                0, // maxStack
+                                countMethodArgWords(methodSig), // maxLocals
+                                begin,
+                                new ExceptionTable(),
+                                new AttributeVector());
+        augmenter.addMethod(methodName, methodSig, accessFlags,
+                            codeAttr, exceptAttr);
+    }
+
+    // ----------------------------------------------------------------------
+    // Generic Augmentation
+    // ----------------------------------------------------------------------
+
+    /**
+     * Build the jdoSetStateManager method for the class.
+     *
+     * public final synchronized void jdoReplaceStateManager(javax.jdo.StateManager sm)
+     * {
+     *     final javax.jdo.StateManager s = this.jdoStateManager;
+     *     if (s != null) {
+     *         this.jdoStateManager = s.replacingStateManager(this, sm);
+     *         return;
+     *     }
+     *     // throws exception if not authorized
+     *     JDOImplHelper.checkAuthorizedStateManager(sm);
+     *     this.jdoStateManager = sm;
+     *     this.jdoFlags = LOAD_REQUIRED;
+     * }
+     */
+    public void addJDOReplaceStateManager()
+    {
+        final String methodName = JDO_PC_jdoReplaceStateManager_Name;
+        final String methodSig = JDO_PC_jdoReplaceStateManager_Sig;
+        final int accessFlags = JDO_PC_jdoReplaceStateManager_Mods;
+        final ExceptionsAttribute exceptAttr = null;
+
+        //^olsen: exceptAttr != null ???
+
+        // begin of method body
+        final InsnTarget begin = new InsnTarget();
+        Insn insn = begin;
+
+        // store the sm field into local var
+        insn = insn.append(Insn.create(opc_aload_0));
+        insn = insn.append(
+            Insn.create(
+                opc_getfield,
+                getjdoStateManagerFieldRef()));
+        insn = insn.append(Insn.create(opc_astore_2));
+
+        // test the sm field and call the sm if nonnull
+        final InsnTarget check = new InsnTarget();
+        insn = insn.append(Insn.create(opc_aload_2));
+        insn = insn.append(Insn.create(opc_ifnull, check));
+
+        // load 'this' on the stack
+        insn = insn.append(Insn.create(opc_aload_0));
+
+        // call the sm's method with 'this' and 'sm' arguments
+        insn = insn.append(Insn.create(opc_aload_2));
+        insn = insn.append(Insn.create(opc_aload_0));
+        insn = insn.append(Insn.create(opc_aload_1));
+        insn = insn.append(
+            new InsnInterfaceInvoke(
+                pool.addInterfaceMethodRef(
+                    JDO_StateManager_Path,
+                    JDO_SM_replacingStateManager_Name,
+                    JDO_SM_replacingStateManager_Sig),
+                countMethodArgWords(JDO_SM_replacingStateManager_Sig)));
+
+        // put result value to sm field and return
+        insn = insn.append(
+            Insn.create(opc_putfield,
+                        getjdoStateManagerFieldRef()));
+        insn = insn.append(Insn.create(opc_return));
+
+        // invoke JDOImplHelper.checkAuthorizedStateManager with 'sm' argument
+        insn = insn.append(check);
+        insn = insn.append(Insn.create(opc_aload_1));
+        insn = insn.append(
+            Insn.create(opc_invokestatic,
+                        pool.addMethodRef(
+                            JDO_JDOImplHelper_Path,
+                            JDO_JDOImplHelper_checkAuthorizedStateManager_Name,
+                            JDO_JDOImplHelper_checkAuthorizedStateManager_Sig)));
+
+        // put argument value to jdoStateManager field
+        insn = insn.append(Insn.create(opc_aload_0));
+        insn = insn.append(Insn.create(opc_aload_1));
+        insn = insn.append(
+            Insn.create(opc_putfield,
+                        getjdoStateManagerFieldRef()));
+
+        // reset flags to LOAD_REQUIRED
+        insn = insn.append(Insn.create(opc_aload_0));
+        insn = insn.append(Insn.create(opc_iconst_1));
+        insn = insn.append(
+            Insn.create(opc_putfield,
+                        getjdoFlagsFieldRef()));
+
+        // end of method body
+        insn = insn.append(Insn.create(opc_return));
+
+        final CodeAttribute codeAttr
+            = new CodeAttribute(getCodeAttributeUtf8(),
+                                4, // maxStack
+                                3, // maxLocals
+                                begin,
+                                new ExceptionTable(),
+                                new AttributeVector());
+        augmenter.addMethod(methodName, methodSig, accessFlags,
+                            codeAttr, exceptAttr);
+    }
+
+    // ----------------------------------------------------------------------
+
+    /**
+     * Build the jdoReplaceFlags method for the class.
+     *
+     * public final void jdoReplaceFlags()
+     * {
+     *     final StateManager sm = this.jdoStateManager;
+     *     if (sm != null) {
+     *         this.jdoFlags = sm.replacingFlags(this);
+     *     }
+     * }
+     */
+    public void addJDOReplaceFlags()
+    {
+        final String methodName = JDO_PC_jdoReplaceFlags_Name;
+        final String methodSig = JDO_PC_jdoReplaceFlags_Sig;
+        final int accessFlags = JDO_PC_jdoReplaceFlags_Mods;
+        final ExceptionsAttribute exceptAttr = null;
+
+        // begin of method body
+        final InsnTarget begin = new InsnTarget();
+        Insn insn = begin;
+
+        // store the sm field into local var
+        insn = insn.append(Insn.create(opc_aload_0));
+        insn = insn.append(
+            Insn.create(
+                opc_getfield,
+                getjdoStateManagerFieldRef()));
+        insn = insn.append(Insn.create(opc_astore_1));
+
+        // test the sm field and goto end if null
+        final InsnTarget end = new InsnTarget();
+        insn = insn.append(Insn.create(opc_aload_1));
+        insn = insn.append(Insn.create(opc_ifnull, end));
+
+        // load 'this' on the stack
+        insn = insn.append(Insn.create(opc_aload_0));
+
+        // call the sm's method with 'this' argument
+        insn = insn.append(Insn.create(opc_aload_1));
+        insn = insn.append(Insn.create(opc_aload_0));
+        insn = insn.append(
+            new InsnInterfaceInvoke(
+                pool.addInterfaceMethodRef(
+                    JDO_StateManager_Path,
+                    JDO_SM_replacingFlags_Name,
+                    JDO_SM_replacingFlags_Sig),
+                countMethodArgWords(JDO_SM_replacingFlags_Sig)));
+
+        // put result value to flags field
+        insn = insn.append(
+            Insn.create(opc_putfield,
+                        getjdoFlagsFieldRef()));
+
+        // end of method body
+        insn = insn.append(end);
+        insn = insn.append(Insn.create(opc_return));
+
+        final CodeAttribute codeAttr
+            = new CodeAttribute(getCodeAttributeUtf8(),
+                                3, // maxStack
+                                2, // maxLocals
+                                begin,
+                                new ExceptionTable(),
+                                new AttributeVector());
+        augmenter.addMethod(methodName, methodSig, accessFlags,
+                            codeAttr, exceptAttr);
+    }
+
+    // ----------------------------------------------------------------------
+
+    /**
+     * Build the jdoMakeDirty method for the class.
+     *
+     * public final void jdoMakeDirty(java.lang.String fieldname)
+     * {
+     *     final javax.jdo.StateManager sm = this.jdoStateManager;
+     *     if (sm != null) {
+     *         sm.makeDirty(this, fieldname);
+     *     }
+     * }
+     */
+    public void addJDOMakeDirtyMethod()
+    {
+        final String methodName = JDO_PC_jdoMakeDirty_Name;
+        final String methodSig = JDO_PC_jdoMakeDirty_Sig;
+        final int accessFlags = JDO_PC_jdoMakeDirty_Mods;
+        final ExceptionsAttribute exceptAttr = null;
+
+        // begin of method body
+        final InsnTarget begin = new InsnTarget();
+        Insn insn = begin;
+
+        // store the sm field into local var
+        insn = insn.append(Insn.create(opc_aload_0));
+        insn = insn.append(
+            Insn.create(
+                opc_getfield,
+                getjdoStateManagerFieldRef()));
+        insn = insn.append(Insn.create(opc_astore_2));
+
+        // test the sm field and goto end if null
+        final InsnTarget end = new InsnTarget();
+        insn = insn.append(Insn.create(opc_aload_2));
+        insn = insn.append(Insn.create(opc_ifnull, end));
+
+        // call the sm's method with 'this' and 'fieldname' arguments
+        insn = insn.append(Insn.create(opc_aload_2));
+        insn = insn.append(Insn.create(opc_aload_0));
+        insn = insn.append(Insn.create(opc_aload_1));
+        insn = insn.append(
+            new InsnInterfaceInvoke(
+                pool.addInterfaceMethodRef(
+                    JDO_StateManager_Path,
+                    JDO_SM_makeDirty_Name,
+                    JDO_SM_makeDirty_Sig),
+                countMethodArgWords(JDO_SM_makeDirty_Sig)));
+
+        // end of method body
+        insn = insn.append(end);
+        insn = insn.append(Insn.create(opc_return));
+
+        final CodeAttribute codeAttr
+            = new CodeAttribute(getCodeAttributeUtf8(),
+                                3, // maxStack
+                                3, // maxLocals
+                                begin,
+                                new ExceptionTable(),
+                                new AttributeVector());
+        augmenter.addMethod(methodName, methodSig, accessFlags,
+                            codeAttr, exceptAttr);
+    }
+
+    // ----------------------------------------------------------------------
+
+    /**
+     * Build the jdoPreSerialize method for the class.
+     *
+     * protected final void jdoPreSerialize()
+     * {
+     *     final javax.jdo.StateManager sm = this.jdoStateManager;
+     *     if (sm != null) {
+     *         sm.preSerialize(this);
+     *     }
+     * }
+     */
+    public void addJDOPreSerializeMethod()
+    {
+        final String methodName = JDO_PC_jdoPreSerialize_Name;
+        final String methodSig = JDO_PC_jdoPreSerialize_Sig;
+        final int accessFlags = JDO_PC_jdoPreSerialize_Mods;
+        final ExceptionsAttribute exceptAttr = null;
+
+        // begin of method body
+        final InsnTarget begin = new InsnTarget();
+        Insn insn = begin;
+
+        // store the sm field into local var
+        insn = insn.append(Insn.create(opc_aload_0));
+        insn = insn.append(
+            Insn.create(
+                opc_getfield,
+                getjdoStateManagerFieldRef()));
+        insn = insn.append(Insn.create(opc_astore_1));
+
+        // test the sm field and goto end if null
+        final InsnTarget end = new InsnTarget();
+        insn = insn.append(Insn.create(opc_aload_1));
+        insn = insn.append(Insn.create(opc_ifnull, end));
+
+        // call the sm's method with 'this' argument
+        insn = insn.append(Insn.create(opc_aload_1));
+        insn = insn.append(Insn.create(opc_aload_0));
+        insn = insn.append(
+            new InsnInterfaceInvoke(
+                pool.addInterfaceMethodRef(
+                    JDO_StateManager_Path,
+                    JDO_SM_preSerialize_Name,
+                    JDO_SM_preSerialize_Sig),
+                countMethodArgWords(JDO_SM_preSerialize_Sig)));
+
+        // end of method body
+        insn = insn.append(end);
+        insn = insn.append(Insn.create(opc_return));
+
+        final CodeAttribute codeAttr
+            = new CodeAttribute(getCodeAttributeUtf8(),
+                                2, // maxStack
+                                2, // maxLocals
+                                begin,
+                                new ExceptionTable(),
+                                new AttributeVector());
+        augmenter.addMethod(methodName, methodSig, accessFlags,
+                            codeAttr, exceptAttr);
+    }
+
+    // ----------------------------------------------------------------------
+
+    /**
+     * Build the writeObject method for the class.
+     *
+     * private void writeObject(java.io.ObjectOutputStream out) 
+     *    throws java.io.IOException
+     * {
+     *    jdoPreSerialize();
+     *    out.defaultWriteObject();
+     * }
+     */
+    public void addWriteObjectMethod()
+    {
+        final String methodName = JAVA_Object_writeObject_Name;
+        final String methodSig = JAVA_Object_writeObject_Sig;
+        final int accessFlags = JAVA_Object_writeObject_Mods;
+        final ExceptionsAttribute exceptAttr
+            = new ExceptionsAttribute(
+                pool.addUtf8(ExceptionsAttribute.expectedAttrName),
+                pool.addClass("java/io/IOException"));
+        
+        // begin of method body
+        final InsnTarget begin = new InsnTarget();
+        Insn insn = begin;
+
+        // call jdoPreSerialize 
+        insn = insn.append(Insn.create(opc_aload_0));
+        insn = insn.append(
+            Insn.create(opc_invokevirtual,
+                        pool.addMethodRef(
+                            className,
+                            JDO_PC_jdoPreSerialize_Name,
+                            JDO_PC_jdoPreSerialize_Sig)));
+
+        // call out.defaultWriteObject();
+        insn = insn.append(Insn.create(opc_aload_1));
+        insn = insn.append(
+            Insn.create(opc_invokevirtual,
+                        pool.addMethodRef(
+                            JAVA_ObjectOutputStream_Path,
+                            JAVA_ObjectOutputStream_defaultWriteObject_Name,
+                            JDO_PC_jdoPreSerialize_Sig)));
+        
+        // end of method body
+        insn = insn.append(Insn.create(opc_return));
+
+        final CodeAttribute codeAttr
+            = new CodeAttribute(getCodeAttributeUtf8(),
+                                1, // maxStack
+                                2, // maxLocals
+                                begin,
+                                new ExceptionTable(),
+                                new AttributeVector());
+        augmenter.addMethod(methodName, methodSig, accessFlags,
+                            codeAttr, exceptAttr);
+        
+    }
+
+    // ----------------------------------------------------------------------
+
+    /**
+     * Adds a call to jdoPreSerialize as first statement to the existing method.
+     */
+    public void addJDOPreSerializeCall(String methodName, String methodSig)
+    {
+        final ExceptionsAttribute exceptAttr = null;
+
+        // begin of method body
+        final InsnTarget begin = new InsnTarget();
+        Insn insn = begin;
+        
+        // invoke jdoPreSerialize
+        insn = insn.append(Insn.create(opc_aload_0));
+        insn = insn.append(
+            Insn.create(opc_invokevirtual,
+                        pool.addMethodRef(
+                            className,
+                            JDO_PC_jdoPreSerialize_Name,
+                            JDO_PC_jdoPreSerialize_Sig)));
+
+        // create code block to be added
+        final CodeAttribute codeAttr
+            = new CodeAttribute(getCodeAttributeUtf8(),
+                                1, // maxStack
+                                0, // maxLocals
+                                begin,
+                                new ExceptionTable(),
+                                new AttributeVector());
+
+        augmenter.prependMethod(methodName, methodSig, codeAttr, exceptAttr);
+    }
+
+    // ----------------------------------------------------------------------
+
+    /**
+     * Build an interrogative method for the class.
+     */
+    public void addJDOIsPersistentMethod()
+    {
+        addJDOInterrogativeMethod(JDO_PC_jdoIsPersistent_Name,
+                                  JDO_PC_jdoIsPersistent_Sig,
+                                  JDO_PC_jdoIsPersistent_Mods,
+                                  JDO_SM_isPersistent_Name,
+                                  JDO_SM_isPersistent_Sig);
+    }
+    
+    /**
+     * Build an interrogative method for the class.
+     */
+    public void addJDOIsTransactionalMethod()
+    {
+        addJDOInterrogativeMethod(JDO_PC_jdoIsTransactional_Name,
+                                  JDO_PC_jdoIsTransactional_Sig,
+                                  JDO_PC_jdoIsTransactional_Mods,
+                                  JDO_SM_isTransactional_Name,
+                                  JDO_SM_isTransactional_Sig);
+    }
+    
+    /**
+     * Build an interrogative method for the class.
+     */
+    public void addJDOIsNewMethod()
+    {
+        addJDOInterrogativeMethod(JDO_PC_jdoIsNew_Name,
+                                  JDO_PC_jdoIsNew_Sig,
+                                  JDO_PC_jdoIsNew_Mods,
+                                  JDO_SM_isNew_Name,
+                                  JDO_SM_isNew_Sig);
+    }
+    
+    /**
+     * Build an interrogative method for the class.
+     */
+    public void addJDOIsDeletedMethod()
+    {
+        addJDOInterrogativeMethod(JDO_PC_jdoIsDeleted_Name,
+                                  JDO_PC_jdoIsDeleted_Sig,
+                                  JDO_PC_jdoIsDeleted_Mods,
+                                  JDO_SM_isDeleted_Name,
+                                  JDO_SM_isDeleted_Sig);
+    }
+    
+    /**
+     * Build an interrogative method for the class.
+     */
+    public void addJDOIsDirtyMethod()
+    {
+        addJDOInterrogativeMethod(JDO_PC_jdoIsDirty_Name,
+                                  JDO_PC_jdoIsDirty_Sig,
+                                  JDO_PC_jdoIsDirty_Mods,
+                                  JDO_SM_isDirty_Name,
+                                  JDO_SM_isDirty_Sig);
+    }
+    
+    /**
+     * Build an interrogative method named methodName for the class.
+     *
+     * public boolean isXXX() {
+     *     final StateManager sm = this.jdoStateManager;
+     *     if (sm == null)
+     *         return false;
+     *     return sm.isXXXX(this);
+     * }
+     */
+    private void addJDOInterrogativeMethod(final String methodName,
+                                           final String methodSig,
+                                           final int accessFlags,
+                                           final String delegateName,
+                                           final String delegateSig)
+    {
+        final ExceptionsAttribute exceptAttr = null;
+
+        // begin of method body
+        final InsnTarget begin = new InsnTarget();
+        Insn insn = begin;
+
+        // store the sm field into local var
+        insn = insn.append(Insn.create(opc_aload_0));
+        insn = insn.append(
+            Insn.create(
+                opc_getfield,
+                getjdoStateManagerFieldRef()));
+        insn = insn.append(Insn.create(opc_astore_1));
+
+        // test the sm field and do the call if nonnull
+        InsnTarget noncall = new InsnTarget();
+        insn = insn.append(Insn.create(opc_aload_1));
+        insn = insn.append(Insn.create(opc_ifnull, noncall));
+
+        // call the sm's method with 'this' argument and return
+        insn = insn.append(Insn.create(opc_aload_1));
+        insn = insn.append(Insn.create(opc_aload_0));
+        insn = insn.append(
+            new InsnInterfaceInvoke(
+                pool.addInterfaceMethodRef(
+                    JDO_StateManager_Path,
+                    delegateName,
+                    delegateSig),
+                countMethodArgWords(delegateSig)));
+        insn = insn.append(Insn.create(opc_ireturn));
+
+        // return false
+        insn = insn.append(noncall);
+        insn = insn.append(Insn.create(opc_iconst_0));
+
+        // end of method body
+        insn = insn.append(Insn.create(opc_ireturn));
+
+        final CodeAttribute codeAttr
+            = new CodeAttribute(getCodeAttributeUtf8(),
+                                2, // maxStack
+                                2, // maxLocals
+                                begin,
+                                new ExceptionTable(),
+                                new AttributeVector());
+        augmenter.addMethod(methodName, methodSig, accessFlags,
+                            codeAttr, exceptAttr);
+    }
+
+    // ----------------------------------------------------------------------
+
+    /**
+     * Build an object query method for the class.
+     */
+    public void addJDOGetPersistenceManagerMethod()
+    {
+        addJDOObjectQueryMethod(JDO_PC_jdoGetPersistenceManager_Name,
+                                JDO_PC_jdoGetPersistenceManager_Sig,
+                                JDO_PC_jdoGetPersistenceManager_Mods,
+                                JDO_SM_getPersistenceManager_Name,
+                                JDO_SM_getPersistenceManager_Sig);
+    }
+
+    /**
+     * Build an object query method for the class.
+     */
+    public void addJDOGetObjectIdMethod()
+    {
+        addJDOObjectQueryMethod(JDO_PC_jdoGetObjectId_Name,
+                                JDO_PC_jdoGetObjectId_Sig,
+                                JDO_PC_jdoGetObjectId_Mods,
+                                JDO_SM_getObjectId_Name,
+                                JDO_SM_getObjectId_Sig);
+    }
+
+    /**
+     * Build an object query method for the class.
+     */
+    public void addJDOGetTransactionalObjectIdMethod()
+    {
+        addJDOObjectQueryMethod(JDO_PC_jdoGetTransactionalObjectId_Name,
+                                JDO_PC_jdoGetTransactionalObjectId_Sig,
+                                JDO_PC_jdoGetTransactionalObjectId_Mods,
+                                JDO_SM_getTransactionalObjectId_Name,
+                                JDO_SM_getTransactionalObjectId_Sig);
+    }
+
+    /**
+     * Build an object query method for the class.
+     *
+     * public final XXX jdoGetYYY()
+     * {
+     *     final javax.jdo.StateManager sm = this.jdoStateManager;
+     *     if (sm != null) {
+     *         return sm.getYYY(this);
+     *     }
+     *     return null;
+     * }
+     */
+    private void addJDOObjectQueryMethod(final String methodName,
+                                         final String methodSig,
+                                         final int accessFlags,
+                                         final String delegateName,
+                                         final String delegateSig)
+    {
+        final ExceptionsAttribute exceptAttr = null;
+
+        // begin of method body
+        final InsnTarget begin = new InsnTarget();
+        Insn insn = begin;
+
+        // store the sm field into local var
+        insn = insn.append(Insn.create(opc_aload_0));
+        insn = insn.append(
+            Insn.create(
+                opc_getfield,
+                getjdoStateManagerFieldRef()));
+        insn = insn.append(Insn.create(opc_astore_1));
+
+        // test the sm field and do the call if nonnull
+        InsnTarget noncall = new InsnTarget();
+        insn = insn.append(Insn.create(opc_aload_1));
+        insn = insn.append(Insn.create(opc_ifnull, noncall));
+
+        // call the sm's method with 'this' argument and return
+        insn = insn.append(Insn.create(opc_aload_1));
+        insn = insn.append(Insn.create(opc_aload_0));
+        insn = insn.append(
+            new InsnInterfaceInvoke(
+                pool.addInterfaceMethodRef(
+                    JDO_StateManager_Path,
+                    delegateName,
+                    delegateSig),
+                countMethodArgWords(delegateSig)));
+        insn = insn.append(Insn.create(opc_areturn));
+
+        // return null
+        insn = insn.append(noncall);
+        insn = insn.append(Insn.create(opc_aconst_null));
+
+        // end of method body
+        insn = insn.append(Insn.create(opc_areturn));
+
+        final CodeAttribute codeAttr
+            = new CodeAttribute(getCodeAttributeUtf8(),
+                                2, // maxStack
+                                2, // maxLocals
+                                begin,
+                                new ExceptionTable(),
+                                new AttributeVector());
+        augmenter.addMethod(methodName, methodSig, accessFlags,
+                            codeAttr, exceptAttr);
+    }
+
+    // ----------------------------------------------------------------------
+
+    /**
+     * Build the jdoArrayArgumentIteration method for the class.
+     */
+    public void addJDOProvideFieldsMethod()
+    {
+        addJDOArrayArgumentIterationMethod(JDO_PC_jdoProvideFields_Name,
+                                           JDO_PC_jdoProvideFields_Sig,
+                                           JDO_PC_jdoProvideFields_Mods,
+                                           JDO_PC_jdoProvideField_Name,
+                                           JDO_PC_jdoProvideField_Sig);
+    }
+
+    /**
+     * Build the jdoArrayArgumentIteration method for the class.
+     */
+    public void addJDOReplaceFieldsMethod()
+    {
+        addJDOArrayArgumentIterationMethod(JDO_PC_jdoReplaceFields_Name,
+                                           JDO_PC_jdoReplaceFields_Sig,
+                                           JDO_PC_jdoReplaceFields_Mods,
+                                           JDO_PC_jdoReplaceField_Name,
+                                           JDO_PC_jdoReplaceField_Sig);
+    }
+
+    /**
+     * Build the jdoArrayArgumentIteration method for the class.
+     *
+     * public final void jdoXXXFields(int[] fieldnumbers)
+     * {
+     *     final int n = fieldnumbers.length;
+     *     for (int i = 0; i < n; i++) {
+     *         this.jdoXXXField(fieldnumbers[i]);
+     *     }
+     * }
+     */
+    public void addJDOArrayArgumentIterationMethod(final String methodName,
+                                                   final String methodSig,
+                                                   final int accessFlags,
+                                                   final String delegateName,
+                                                   final String delegateSig)
+    {
+        final ExceptionsAttribute exceptAttr = null;
+
+        // begin of method body
+        final InsnTarget begin = new InsnTarget();
+        Insn insn = begin;
+
+        // check arg
+        insn = appendCheckVarNonNull(insn, 1,
+                                     JAVA_IllegalArgumentException_Path,
+                                     "arg1");
+
+        // store the array argument length into local var
+        insn = insn.append(Insn.create(opc_aload_1));
+        insn = insn.append(Insn.create(opc_arraylength));
+        insn = insn.append(Insn.create(opc_istore_2));
+
+        // init loop counter and goto loop check
+        final InsnTarget loopcheck = new InsnTarget();
+        insn = insn.append(Insn.create(opc_iconst_0));
+        insn = insn.append(Insn.create(opc_istore_3));
+        insn = insn.append(Insn.create(opc_goto, loopcheck));
+
+        // loop body: call self-delegating method with array element
+        final InsnTarget loopbody = new InsnTarget();
+        insn = insn.append(loopbody);
+        insn = insn.append(Insn.create(opc_aload_0));
+
+        // select element from array argument at loop counter
+        insn = insn.append(Insn.create(opc_aload_1));
+        insn = insn.append(Insn.create(opc_iload_3));
+        insn = insn.append(Insn.create(opc_iaload));
+
+        // call self-delegating method
+        insn = insn.append(
+            Insn.create(opc_invokevirtual,
+                        pool.addMethodRef(
+                            className,
+                            delegateName,
+                            delegateSig)));
+
+        // loop counter increment
+        insn = insn.append(new InsnIInc(3, 1));
+
+        // loop termination check
+        insn = insn.append(loopcheck);
+        insn = insn.append(Insn.create(opc_iload_3));
+        insn = insn.append(Insn.create(opc_iload_2));
+        insn = insn.append(Insn.create(opc_if_icmplt, loopbody));
+
+        // end of method body
+        insn = insn.append(Insn.create(opc_return));
+
+        final CodeAttribute codeAttr
+            = new CodeAttribute(getCodeAttributeUtf8(),
+                                3, // maxStack
+                                4, // maxLocals
+                                begin,
+                                new ExceptionTable(),
+                                new AttributeVector());
+        augmenter.addMethod(methodName, methodSig, accessFlags,
+                            codeAttr, exceptAttr);
+    }
+
+    // ----------------------------------------------------------------------
+
+    /**
+     * Build the sunjdoClassForName method for the class.
+     *
+     * public final Class sunjdoClassForName(java.lang.String classname)
+     * {
+     *     try {
+     *         return Class.forName(classname);
+     *     catch (ClassNotFoundException ex) {
+     *         throw new NoClassDefFoundError(ex.getMessage());
+     *     }
+     * }
+     */
+    public void addSunJDOClassForNameMethod()
+    {
+        final String methodName = SUNJDO_PC_sunjdoClassForName_Name;
+        final String methodSig = SUNJDO_PC_sunjdoClassForName_Sig;
+        final int accessFlags = SUNJDO_PC_sunjdoClassForName_Mods;
+        final ExceptionsAttribute exceptAttr = null;
+
+        // begin of method body
+        final InsnTarget begin = new InsnTarget();
+        Insn insn = begin;
+
+        // invoke the Class.forName(String) method with argument
+        insn = insn.append(Insn.create(opc_aload_0));
+        insn = insn.append(
+            Insn.create(opc_invokestatic,
+                        pool.addMethodRef(
+                            JAVA_Class_Path,
+                            JAVA_Class_forName_Name,
+                            JAVA_Class_forName_Sig)));
+
+        // end of method body
+        insn = insn.append(Insn.create(opc_areturn));
+
+        // begin of exception handler
+        final InsnTarget end = new InsnTarget();
+        final InsnTarget beginHandler = end;
+        insn = insn.append(beginHandler);
+
+        // create NoClassDefFoundError with message from caught exception
+        insn = insn.append(Insn.create(opc_astore_1));
+        insn = insn.append(
+            Insn.create(opc_new,
+                        pool.addClass(JAVA_NoClassDefFoundError_Path)));
+        insn = insn.append(Insn.create(opc_dup));
+        insn = insn.append(Insn.create(opc_aload_1));
+        insn = insn.append(
+            Insn.create(
+                opc_invokevirtual,
+                pool.addMethodRef(
+                    JAVA_Throwable_Path,
+                    JAVA_Throwable_getMessage_Name,
+                    JAVA_Throwable_getMessage_Sig)));
+        insn = insn.append(
+            Insn.create(
+                opc_invokespecial,
+                pool.addMethodRef(
+                    JAVA_NoClassDefFoundError_Path,
+                    JAVA_NoClassDefFoundError_NoClassDefFoundError_Name,
+                    JAVA_NoClassDefFoundError_NoClassDefFoundError_Sig)));
+
+        // end of exception handler
+        insn = insn.append(Insn.create(opc_athrow));
+
+        // create exception table
+        final ConstClass catchType
+            = pool.addClass(JAVA_ClassNotFoundException_Path);
+        final ExceptionRange exceptionRange
+            = new ExceptionRange(begin, end, beginHandler, catchType);
+        final ExceptionTable exceptionTable
+            = new ExceptionTable();
+        exceptionTable.addElement(exceptionRange);
+
+        final CodeAttribute codeAttr
+            = new CodeAttribute(getCodeAttributeUtf8(),
+                                3, // maxStack
+                                3, // maxLocals
+                                begin,
+                                exceptionTable,
+                                new AttributeVector());
+        augmenter.addMethod(methodName, methodSig, accessFlags,
+                            codeAttr, exceptAttr);
+    }
+
+    // ----------------------------------------------------------------------
+    // Specific Augmentation
+    // ----------------------------------------------------------------------
+    
+    /**
+     * Build the jdoGetManagedFieldCount method for the class.
+     *
+     * protected static int jdoGetManagedFieldCount()
+     * {
+     *     return jdoInheritedFieldCount + X;
+     * }
+     */
+    public void addJDOGetManagedFieldCountMethod()
+    {
+        final String methodName = JDO_PC_jdoGetManagedFieldCount_Name;
+        final String methodSig = JDO_PC_jdoGetManagedFieldCount_Sig;
+        final int accessFlags = JDO_PC_jdoGetManagedFieldCount_Mods;
+        final ExceptionsAttribute exceptAttr = null;
+
+        final int managedFieldCount = analyzer.getManagedFieldCount();
+        affirm(managedFieldCount >= 0);
+
+        // begin of method body
+        final InsnTarget begin = new InsnTarget();
+        Insn insn = begin;
+
+        // push total (absolute) number of managed fields
+        final boolean isPCRoot = analyzer.isAugmentableAsRoot();
+        if (isPCRoot) {
+            insn = insn.append(InsnUtils.integerConstant(managedFieldCount, pool));
+        }
+        else {
+            final ConstClass superConstClass = classFile.superName();
+            affirm(superConstClass != null);
+            final String superClassName = superConstClass.asString();
+            affirm(superClassName != null);
+            // call the superclass' jdoGetManagedFieldCount method
+            insn = insn.append(
+                Insn.create(opc_invokestatic,
+                            pool.addMethodRef(
+                                superClassName,
+                                JDO_PC_jdoGetManagedFieldCount_Name,
+                                JDO_PC_jdoGetManagedFieldCount_Sig)));
+            insn = insn.append(InsnUtils.integerConstant(managedFieldCount, pool));
+            insn = insn.append(Insn.create(opc_iadd));
+        }
+
+        // end of method body
+        insn = insn.append(Insn.create(opc_ireturn));
+
+        final CodeAttribute codeAttr
+            = new CodeAttribute(getCodeAttributeUtf8(),
+                                isPCRoot ? 1 : 2, // maxStack
+                                0, // maxLocals
+                                begin,
+                                new ExceptionTable(),
+                                new AttributeVector());
+        augmenter.addMethod(methodName, methodSig, accessFlags,
+                            codeAttr, exceptAttr);
+    }
+
+    // ----------------------------------------------------------------------
+
+    /**
+     * Adds the initialization code for the jdoInheritedFieldCount field.
+     */
+    private Insn initJdoInheritedFieldCount(Insn insn) 
+    {
+        affirm(insn != null);
+
+        // invoke jdoGetManagedFieldCount if not PCRoot class
+        final boolean isPCRoot = analyzer.isAugmentableAsRoot();
+        if (isPCRoot) {
+            insn = insn.append(Insn.create(opc_iconst_0));
+        } else {
+            final ConstClass superConstClass = classFile.superName();
+            affirm(superConstClass != null);
+            final String superClassName = superConstClass.asString();
+            affirm(superClassName != null);
+            insn = insn.append(
+                Insn.create(opc_invokestatic,
+                            pool.addMethodRef(
+                                superClassName,
+                                JDO_PC_jdoGetManagedFieldCount_Name,
+                                JDO_PC_jdoGetManagedFieldCount_Sig)));
+        }
+
+        // store to field
+        insn = insn.append(
+            Insn.create(opc_putstatic,
+                        pool.addFieldRef(
+                            className,
+                            JDO_PC_jdoInheritedFieldCount_Name,
+                            JDO_PC_jdoInheritedFieldCount_Sig)));
+
+        affirm(insn != null);
+        return insn;
+    }
+    
+    /**
+     * Adds the initialization code for the jdoFieldNames field.
+     */
+    private Insn initJdoFieldNames(Insn insn)
+    {
+        affirm(insn != null);
+
+        final int managedFieldCount = analyzer.getManagedFieldCount();
+        final String[] managedFieldNames = analyzer.getAnnotatedFieldNames();
+        affirm(managedFieldNames.length >= managedFieldCount);
+        
+        // create array
+        affirm(NameHelper.elementPathForSig(JDO_PC_jdoFieldNames_Sig)
+               .equals(JAVA_String_Path));
+        insn = insn.append(InsnUtils.integerConstant(managedFieldCount, pool));
+        insn = insn.append(
+            Insn.create(opc_anewarray,
+                        pool.addClass(JAVA_String_Path)));
+
+        // initialize elements
+        for (int i = 0; i < managedFieldCount; i++) {
+            insn = insn.append(Insn.create(opc_dup));
+            insn = insn.append(InsnUtils.integerConstant(i, pool));
+            final String name = managedFieldNames[i];
+            affirm(name != null);
+            insn = insn.append(
+                InsnUtils.stringConstant(name, pool));
+            insn = insn.append(Insn.create(opc_aastore));
+        }
+
+        // store to field
+        insn = insn.append(
+            Insn.create(opc_putstatic,
+                        getjdoFieldNamesFieldRef()));
+
+        affirm(insn != null);
+        return insn;
+    }
+    
+    /**
+     * Adds the initialization code for the jdoFieldTypes field.
+     */
+    private Insn initJdoFieldTypes(Insn insn) 
+    {
+        affirm(insn != null);
+
+        final int managedFieldCount = analyzer.getManagedFieldCount();
+        final String[] managedFieldSigs = analyzer.getAnnotatedFieldSigs();
+        affirm(managedFieldSigs.length >= managedFieldCount);
+        
+        // create array
+        affirm(NameHelper.elementPathForSig(JDO_PC_jdoFieldTypes_Sig)
+               .equals(JAVA_Class_Path));
+        insn = insn.append(InsnUtils.integerConstant(managedFieldCount, pool));
+        insn = insn.append(
+            Insn.create(opc_anewarray,
+                        pool.addClass(JAVA_Class_Path)));
+
+        // initialize elements
+        for (int i = 0; i < managedFieldCount; i++) {
+            insn = insn.append(Insn.create(opc_dup));
+            insn = insn.append(InsnUtils.integerConstant(i, pool));
+            final String sig = managedFieldSigs[i];
+            affirm(sig != null && sig.length() > 0);
+
+            // push the class object
+            // If the field is of primitive type then access the
+            // corresponding wrapper class' static 'TYPE' field;
+            // otherwise call generated, static method sunjdoClassForName.
+            switch (sig.charAt(0)) {
+            case 'Z':
+                // for primitive types, the wrapper's TYPE field is pushed
+                insn = insn.append(
+                    Insn.create(opc_getstatic,
+                                pool.addFieldRef(
+                                    JAVA_Boolean_Path,
+                                    JAVA_Boolean_TYPE_Name,
+                                    JAVA_Boolean_TYPE_Sig)));
+                break;
+            case 'C':
+                // for primitive types, the wrapper's TYPE field is pushed
+                insn = insn.append(
+                    Insn.create(opc_getstatic,
+                                pool.addFieldRef(
+                                    JAVA_Character_Path,
+                                    JAVA_Character_TYPE_Name,
+                                    JAVA_Character_TYPE_Sig)));
+                break;
+            case 'B':
+                // for primitive types, the wrapper's TYPE field is pushed
+                insn = insn.append(
+                    Insn.create(opc_getstatic,
+                                pool.addFieldRef(
+                                    JAVA_Byte_Path,
+                                    JAVA_Byte_TYPE_Name,
+                                    JAVA_Byte_TYPE_Sig)));
+                break;
+            case 'S':
+                // for primitive types, the wrapper's TYPE field is pushed
+                insn = insn.append(
+                    Insn.create(opc_getstatic,
+                                pool.addFieldRef(
+                                    JAVA_Short_Path,
+                                    JAVA_Short_TYPE_Name,
+                                    JAVA_Short_TYPE_Sig)));
+                break;
+            case 'I':
+                // for primitive types, the wrapper's TYPE field is pushed
+                insn = insn.append(
+                    Insn.create(opc_getstatic,
+                                pool.addFieldRef(
+                                    JAVA_Integer_Path,
+                                    JAVA_Integer_TYPE_Name,
+                                    JAVA_Integer_TYPE_Sig)));
+                break;
+            case 'J':
+                // for primitive types, the wrapper's TYPE field is pushed
+                insn = insn.append(
+                    Insn.create(opc_getstatic,
+                                pool.addFieldRef(
+                                    JAVA_Long_Path,
+                                    JAVA_Long_TYPE_Name,
+                                    JAVA_Long_TYPE_Sig)));
+                break;
+            case 'F':
+                // for primitive types, the wrapper's TYPE field is pushed
+                insn = insn.append(
+                    Insn.create(opc_getstatic,
+                                pool.addFieldRef(
+                                    JAVA_Float_Path,
+                                    JAVA_Float_TYPE_Name,
+                                    JAVA_Float_TYPE_Sig)));
+                break;
+            case 'D':
+                // for primitive types, the wrapper's TYPE field is pushed
+                insn = insn.append(
+                    Insn.create(opc_getstatic,
+                                pool.addFieldRef(
+                                    JAVA_Double_Path,
+                                    JAVA_Double_TYPE_Name,
+                                    JAVA_Double_TYPE_Sig)));
+                break;
+            case 'L':
+                // for object types, the signature is simply converted 
+                // into a type name, e.g.:
+                //     ldc #13 <String "java.lang.String">
+                insn = insn.append(
+                    InsnUtils.stringConstant(
+                        NameHelper.typeForSig(sig), pool));
+
+                // push class object using the generated helper method
+                insn = insn.append(
+                    Insn.create(opc_invokestatic,
+                                pool.addMethodRef(
+                                    className,
+                                    SUNJDO_PC_sunjdoClassForName_Name,
+                                    SUNJDO_PC_sunjdoClassForName_Sig)));
+                break;
+            case '[':
+                // for array types, the element's signature is simply
+                // converted into a type name, e.g.:
+                //     ldc #10 <String "[I">
+                //     ldc #15 <String "[Ljava.lang.String;">
+                insn = insn.append(
+                    InsnUtils.stringConstant(
+                        NameHelper.typeForPath(sig), pool));
+
+                // push class object using the generated helper method
+                insn = insn.append(
+                    Insn.create(opc_invokestatic,
+                                pool.addMethodRef(
+                                    className,
+                                    SUNJDO_PC_sunjdoClassForName_Name,
+                                    SUNJDO_PC_sunjdoClassForName_Sig)));
+                break;
+            default:
+                affirm(false, "Illegal field type: " + sig);
+            }
+
+            insn = insn.append(Insn.create(opc_aastore));
+        }
+
+        // store to field
+        insn = insn.append(
+            Insn.create(opc_putstatic,
+                        getjdoFieldTypesFieldRef()));
+
+        affirm(insn != null);
+        return insn;
+    }
+    
+    /**
+     * Adds the initialization code for the jdoFieldFlags field.
+     */
+    private Insn initJdoFieldFlags(Insn insn)
+    {
+        affirm(insn != null);
+
+        final int managedFieldCount = analyzer.getManagedFieldCount();
+        final int[] managedFieldFlags = analyzer.getAnnotatedFieldFlags();
+        affirm(managedFieldFlags.length >= managedFieldCount);
+        
+        // create array
+        affirm(NameHelper.elementSigForSig(JDO_PC_jdoFieldFlags_Sig)
+               .equals("B"));
+        insn = insn.append(InsnUtils.integerConstant(managedFieldCount, pool));
+        insn = insn.append(
+            Insn.create(opc_newarray, T_BYTE));
+
+        // initialize elements
+        for (int i = 0; i < managedFieldCount; i++) {
+            insn = insn.append(Insn.create(opc_dup));
+            insn = insn.append(InsnUtils.integerConstant(i, pool));
+            final int flags = managedFieldFlags[i];
+
+            // ensure we're using [opc_iconst_x .. opc_bipush]
+            affirm(-128 <= flags && flags < 128);
+            insn = insn.append(InsnUtils.integerConstant(flags, pool));
+            insn = insn.append(Insn.create(opc_bastore));
+        }
+
+        // store to field
+        insn = insn.append(
+            Insn.create(opc_putstatic,
+                        getjdoFieldFlagsFieldRef()));
+
+        affirm(insn != null);
+        return insn;
+    }
+    
+    /**
+     * Adds the initialization code for the jdoPersistenceCapableSuperclass
+     * field.
+     */
+    private Insn initJdoPersistenceCapableSuperclass(Insn insn) 
+    {
+        affirm(insn != null);
+
+        final String pcSuperName = analyzer.getPCSuperClassName();
+        final String pcRootName = analyzer.getPCRootClassName();
+        affirm(pcSuperName == null || pcRootName != null);
+        //final ConstClass superConstClass = classFile.superName();
+        //affirm(pcSuperName == null || superConstClass != null);
+        //affirm(pcRootName == null || superConstClass != null);
+
+        if (pcSuperName == null) {
+            insn = insn.append(Insn.create(opc_aconst_null));
+        } else {
+            // the type name is used for loading, e.g.:
+            //     ldc #13 <String "java.lang.String">
+            insn = insn.append(
+                InsnUtils.stringConstant(
+                    NameHelper.typeForPath(pcSuperName), pool));
+            
+            //^olsen: decide on whether to use PCRoot class or superclass
+            // push class object using the generated helper method
+            insn = insn.append(
+                Insn.create(opc_invokestatic,
+                            pool.addMethodRef(
+                                pcRootName, //superConstClass.asString(),
+                                SUNJDO_PC_sunjdoClassForName_Name,
+                                SUNJDO_PC_sunjdoClassForName_Sig)));
+        }
+        
+        // store to field
+        insn = insn.append(
+            Insn.create(opc_putstatic,
+                        getjdoPersistenceCapableSuperclassFieldRef()));
+        
+        affirm(insn != null);
+        return insn;
+    }
+    
+    /**
+     * Adds the code for the jdoPersistenceCapableSuperclass
+     * field.
+     */
+    private Insn registerClass(Insn insn) 
+    {
+        affirm(insn != null);
+
+        final String pcRootName = analyzer.getPCRootClassName();
+        //final ConstClass superConstClass = classFile.superName();
+        //affirm(pcRootName == null || superConstClass != null);
+
+        // push the class object for this class
+        // the type name is used for loading, e.g.:
+        //     ldc #13 <String "java.lang.String">
+        insn = insn.append(
+            InsnUtils.stringConstant(
+                NameHelper.typeForPath(className), pool));
+        
+        //^olsen: decide on whether to use PCRoot class or superclass
+        // push class object using the generated helper method
+        insn = insn.append(
+            Insn.create(opc_invokestatic,
+                        pool.addMethodRef(
+                            pcRootName, //superConstClass.asString(),
+                            SUNJDO_PC_sunjdoClassForName_Name,
+                            SUNJDO_PC_sunjdoClassForName_Sig)));
+
+        // push the jdoFieldNames field
+        insn = insn.append(
+            Insn.create(opc_getstatic,
+                        getjdoFieldNamesFieldRef()));
+
+        // push the jdoFieldTypes field
+        insn = insn.append(
+            Insn.create(opc_getstatic,
+                        getjdoFieldTypesFieldRef()));
+
+        // push the jdoFieldFlags field
+        insn = insn.append(
+            Insn.create(opc_getstatic,
+                        getjdoFieldFlagsFieldRef()));
+
+        // push the jdoPersistenceCapableSuperclass field
+        insn = insn.append(
+            Insn.create(opc_getstatic,
+                        getjdoPersistenceCapableSuperclassFieldRef()));
+
+        // push a newly created an instance of this class or null if
+        // class is abstract
+        if (classFile.isAbstract()) {
+            insn = insn.append(Insn.create(opc_aconst_null));
+        } else {
+            final ConstClass thisConstClass = classFile.className();
+            affirm(thisConstClass != null);
+            insn = insn.append(Insn.create(opc_new, thisConstClass));
+            insn = insn.append(Insn.create(opc_dup));
+            insn = insn.append(
+                Insn.create(opc_invokespecial,
+                            pool.addMethodRef(
+                                className,
+                                NameHelper.constructorName(),
+                                NameHelper.constructorSig())));
+        }
+        
+        // invoke registerClass
+        insn = insn.append(
+            Insn.create(opc_invokestatic,
+                        pool.addMethodRef(
+                            JDO_JDOImplHelper_Path,
+                            JDO_JDOImplHelper_registerClass_Name,
+                            JDO_JDOImplHelper_registerClass_Sig)));
+        
+        affirm(insn != null);
+        return insn;
+    }
+    
+    /**
+     * Build the static initialization code for the class.
+     *
+     * static
+     * {
+     *     jdoInheritedFieldCount = 0 | super.jdoGetManagedFieldCount();
+     *     jdoFieldNames = new String[]{ ... };
+     *     jdoFieldTypes = new Class[]{ ... };
+     *     jdoFieldFlags = new byte[]{ ... };
+     *     jdoPersistenceCapableSuperclass = ...;
+     *     javax.jdo.JDOImplHelper.registerClass(
+     *         XXX.class, 
+     *         jdoFieldNames, 
+     *         jdoFieldTypes, 
+     *         jdoFieldFlags, 
+     *         jdoPersistenceCapableSuperclass, 
+     *         new XXX()
+     *     );
+     * }
+     */
+    public void addStaticInitialization()
+    {
+        final String methodName = JAVA_clinit_Name;
+        final String methodSig = JAVA_clinit_Sig;
+        final int accessFlags = JAVA_clinit_Mods;
+        final ExceptionsAttribute exceptAttr = null;
+
+        // begin of method body
+        final InsnTarget begin = new InsnTarget();
+        Insn insn = begin;
+
+        // initialize jdo fields
+        insn = initJdoInheritedFieldCount(insn);
+        insn = initJdoFieldNames(insn);
+        insn = initJdoFieldTypes(insn);
+        insn = initJdoFieldFlags(insn);
+        insn = initJdoPersistenceCapableSuperclass(insn);
+
+        // invoke registerClass
+        insn = registerClass(insn);
+
+        // add or extend the static initializer
+        final CodeAttribute codeAttr
+            = new CodeAttribute(getCodeAttributeUtf8(),
+                                7, // maxStack
+                                0, // maxLocals
+                                begin,
+                                new ExceptionTable(),
+                                new AttributeVector());
+
+        if (analyzer.hasStaticInitializer()) {
+            // not end of method body
+            augmenter.prependMethod(methodName, methodSig, 
+                                    codeAttr, exceptAttr);
+        } else {
+            // end of method body
+            insn = insn.append(Insn.create(opc_return));
+            augmenter.addMethod(methodName, methodSig, accessFlags,
+                                codeAttr, exceptAttr);
+        }
+    }
+
+    // ----------------------------------------------------------------------
+
+    /**
+     * Build the jdoNewInstance method for the class.
+     *
+     * public PersistenceCapable jdoNewInstance(StateManager sm)
+     * {
+     *     final XXX pc = new XXX();
+     *     pc.jdoFlags = 1; // == LOAD_REQUIRED
+     *     pc.jdoStateManager = sm;
+     *     return pc;
+     * }
+     */
+    public void addJDONewInstanceMethod()
+    {
+        final String methodName = JDO_PC_jdoNewInstance_Name;
+        final String methodSig = JDO_PC_jdoNewInstance_Sig;
+        final int accessFlags = JDO_PC_jdoNewInstance_Mods;
+        final ExceptionsAttribute exceptAttr = null;
+
+        // begin of method body
+        final InsnTarget begin = new InsnTarget();
+        Insn insn = begin;
+
+        // push a newly created an instance of this class
+        final ConstClass thisConstClass = classFile.className();
+        affirm(thisConstClass != null);
+        insn = insn.append(Insn.create(opc_new, thisConstClass));
+        insn = insn.append(Insn.create(opc_dup));
+        insn = insn.append(
+            Insn.create(opc_invokespecial,
+                        pool.addMethodRef(
+                            className,
+                            NameHelper.constructorName(),
+                            NameHelper.constructorSig())));
+        insn = insn.append(Insn.create(opc_astore_2));
+
+        // init jdo flags and assign argument to sm
+        insn = insn.append(Insn.create(opc_aload_2));
+        insn = insn.append(Insn.create(opc_iconst_1));
+        insn = insn.append(
+            Insn.create(opc_putfield,
+                        getjdoFlagsFieldRef()));
+        insn = insn.append(Insn.create(opc_aload_2));
+        insn = insn.append(Insn.create(opc_aload_1));
+        insn = insn.append(
+            Insn.create(opc_putfield,
+                        getjdoStateManagerFieldRef()));
+
+        // end of method body
+        insn = insn.append(Insn.create(opc_aload_2));
+        insn = insn.append(Insn.create(opc_areturn));
+
+        final CodeAttribute codeAttr
+            = new CodeAttribute(getCodeAttributeUtf8(),
+                                2, // maxStack
+                                3, // maxLocals
+                                begin,
+                                new ExceptionTable(),
+                                new AttributeVector());
+        augmenter.addMethod(methodName, methodSig, accessFlags,
+                            codeAttr, exceptAttr);
+    }
+
+    /**
+     * Build the jdoNewInstance method for the class.
+     *
+     * public PersistenceCapable jdoNewInstance(StateManager sm, Object oid)
+     * {
+     *     final XXX pc = new XXX();
+     *     pc.jdoCopyKeyFieldsFromObjectId(oid);
+     *     pc.jdoFlags = 1; // == LOAD_REQUIRED
+     *     pc.jdoStateManager = sm;
+     *     return pc;
+     * }
+     */
+    public void addJDONewInstanceOidMethod()
+    {
+        final String methodName = JDO_PC_jdoNewInstance_Object_Name;
+        final String methodSig = JDO_PC_jdoNewInstance_Object_Sig;
+        final int accessFlags = JDO_PC_jdoNewInstance_Object_Mods;
+        final ExceptionsAttribute exceptAttr = null;
+
+        // begin of method body
+        final InsnTarget begin = new InsnTarget();
+        Insn insn = begin;
+
+        // push a newly created an instance of this class
+        final ConstClass thisConstClass = classFile.className();
+        affirm(thisConstClass != null);
+        insn = insn.append(Insn.create(opc_new, thisConstClass));
+        insn = insn.append(Insn.create(opc_dup));
+        insn = insn.append(
+            Insn.create(opc_invokespecial,
+                        pool.addMethodRef(
+                            className,
+                            NameHelper.constructorName(),
+                            NameHelper.constructorSig())));
+        insn = insn.append(Insn.create(opc_astore_3));
+
+        // class on instance pc.jdoCopyKeyFieldsFromObjectId(oid)
+        //^olsen: javac uses the truelly declaring class
+        final String pcKeyOwnerClassName = analyzer.getPCKeyOwnerClassName();
+        affirm(pcKeyOwnerClassName != null);
+        insn = insn.append(Insn.create(opc_aload_3));
+        insn = insn.append(Insn.create(opc_aload_2));
+        insn = insn.append(
+            Insn.create(opc_invokevirtual,
+                        pool.addMethodRef(
+                            pcKeyOwnerClassName,
+                            JDO_PC_jdoCopyKeyFieldsFromObjectId_Name,
+                            JDO_PC_jdoCopyKeyFieldsFromObjectId_Sig)));
+
+        // init jdo flags and assign argument to sm
+        insn = insn.append(Insn.create(opc_aload_3));
+        insn = insn.append(Insn.create(opc_iconst_1));
+        insn = insn.append(
+            Insn.create(opc_putfield,
+                        getjdoFlagsFieldRef()));
+        insn = insn.append(Insn.create(opc_aload_3));
+        insn = insn.append(Insn.create(opc_aload_1));
+        insn = insn.append(
+            Insn.create(opc_putfield,
+                        getjdoStateManagerFieldRef()));
+
+        // end of method body
+        insn = insn.append(Insn.create(opc_aload_3));
+        insn = insn.append(Insn.create(opc_areturn));
+
+        final CodeAttribute codeAttr
+            = new CodeAttribute(getCodeAttributeUtf8(),
+                                2, // maxStack
+                                4, // maxLocals
+                                begin,
+                                new ExceptionTable(),
+                                new AttributeVector());
+        augmenter.addMethod(methodName, methodSig, accessFlags,
+                            codeAttr, exceptAttr);
+    }
+
+    // ----------------------------------------------------------------------
+
+    /**
+     * Adds the code for the begin of the jdoProvideField and
+     * jdoReplaceField methods.
+     */
+    private Insn appendBeginProvideReplaceField(Insn insn)
+    {
+        affirm(insn != null);
+
+        // store the sm field into local var
+        insn = insn.append(Insn.create(opc_aload_0));
+        insn = insn.append(
+            Insn.create(opc_getfield,
+                        getjdoStateManagerFieldRef()));
+        insn = insn.append(Insn.create(opc_astore_2));
+
+        // push (fieldnumber - jdoInheritedFieldCount)
+        insn = insn.append(Insn.create(opc_iload_1));
+        insn = insn.append(
+            Insn.create(opc_getstatic,
+                        pool.addFieldRef(
+                            className,
+                            JDO_PC_jdoInheritedFieldCount_Name,
+                            JDO_PC_jdoInheritedFieldCount_Sig)));
+        insn = insn.append(Insn.create(opc_isub));
+        affirm(insn != null);
+        return insn;
+    }
+    
+    /**
+     * Adds the default-branch code for the jdoProvideField and
+     * jdoReplaceField methods.
+     */
+    private Insn appendEndProvideReplaceField(Insn insn,
+                                              String provideReplaceField_Name,
+                                              String provideReplaceField_Sig)
+    {
+        affirm(insn != null);
+        affirm(provideReplaceField_Name);
+        affirm(provideReplaceField_Sig);
+
+        // throw exception or delegate to PC superclass
+        final boolean isPCRoot = analyzer.isAugmentableAsRoot();
+        if (isPCRoot) {
+            insn = appendThrowJavaException(insn,
+                                            JAVA_IllegalArgumentException_Path,
+                                            "arg1");
+        } else {
+            // call super.jdoProvideField(int)
+            final ConstClass superConstClass = classFile.superName();
+            affirm(superConstClass != null);
+            final String superClassName = superConstClass.asString();
+            affirm(superClassName != null);
+            insn = insn.append(Insn.create(opc_aload_0));
+            insn = insn.append(Insn.create(opc_iload_1));
+            insn = insn.append(
+                Insn.create(opc_invokespecial,
+                            pool.addMethodRef(
+                                superClassName,
+                                provideReplaceField_Name,
+                                provideReplaceField_Sig)));
+            insn = insn.append(Insn.create(opc_return));
+        }        
+
+        affirm(insn != null);
+        return insn;
+    }
+    
+    /**
+     * Adds the code for one case-branch in the jdoProvideField method.
+     */
+    private Insn appendCaseBranchForProvideField(Insn insn,
+                                                 String providedXXXField_Name,
+                                                 String providedXXXField_Sig,
+                                                 ConstFieldRef managedFieldRef)
+    {
+        affirm(insn != null);
+        affirm(providedXXXField_Name != null);
+        affirm(providedXXXField_Sig != null);
+        affirm(managedFieldRef != null);
+
+        // check sm
+        insn = appendCheckVarNonNull(insn, 2,
+                                     JAVA_IllegalStateException_Path,
+                                     "arg0." + JDO_PC_jdoStateManager_Name);
+
+        // push sm and args: this, fieldnumber, and field
+        insn = insn.append(Insn.create(opc_aload_2));
+        insn = insn.append(Insn.create(opc_aload_0));
+        insn = insn.append(Insn.create(opc_iload_1));
+        insn = insn.append(Insn.create(opc_aload_0));
+        insn = insn.append(Insn.create(opc_getfield, managedFieldRef));
+
+        // call providedXXXField
+        insn = insn.append(
+            new InsnInterfaceInvoke(
+                pool.addInterfaceMethodRef(
+                    JDO_StateManager_Path,
+                    providedXXXField_Name,
+                    providedXXXField_Sig),
+                countMethodArgWords(providedXXXField_Sig)));
+
+        // return
+        insn = insn.append(Insn.create(opc_return));
+        
+        affirm(insn != null);
+        return insn;
+    }
+    
+    /**
+     * Adds the switch code for the jdoProvideField method.
+     */
+    private Insn appendSwitchForProvideField(Insn insn,
+                                             SizeHolder sizeHolder)
+    {
+        affirm(insn != null);
+        affirm(sizeHolder != null);
+
+        // generate the switch-statement only if more than zero fields
+        final int managedFieldCount = analyzer.getManagedFieldCount();
+        //if (managedFieldCount == 0) {
+        //    return insn;
+        //}        
+
+        // get types of and field references of the managed fields
+        final String[] managedFieldSigs = analyzer.getAnnotatedFieldSigs();
+        final ConstFieldRef[] managedFieldRefs = getAnnotatedFieldRefs();
+        affirm(managedFieldSigs.length >= managedFieldCount);
+        affirm(managedFieldRefs.length >= managedFieldCount);
+
+        // generate the switch
+        final int lowOp = 0;
+        final InsnTarget defaultOp = new InsnTarget();
+        final InsnTarget[] targetsOp = new InsnTarget[managedFieldCount];
+        for (int i = 0; i < managedFieldCount; i++) {
+            targetsOp[i] = new InsnTarget();
+        }            
+
+        // javac prefers lookup switches for 1-element tables
+        if (managedFieldCount <= 1) {
+            final int[] matchesOp
+                = (managedFieldCount == 0 ? new int[]{} : new int[]{ lowOp });
+            insn = insn.append(
+                new InsnLookupSwitch(defaultOp, matchesOp, targetsOp));
+        } else {
+            insn = insn.append(
+                new InsnTableSwitch(lowOp, defaultOp, targetsOp));
+        }
+        
+        // generate the case-targets for the method calls
+        for (int i = 0; i < managedFieldCount; i++) {
+            // target for accessing field [i]
+            insn = insn.append(targetsOp[i]);
+
+            // get signature and constant field reference for field
+            final String sig = managedFieldSigs[i];
+            final ConstFieldRef ref = managedFieldRefs[i];
+            affirm(sig != null && sig.length() > 0);
+            affirm(ref != null);
+
+            // compute stack demand
+            sizeHolder.size = max(sizeHolder.size,
+                                  Descriptor.countFieldWords(sig));
+
+            // generate the case-branch for a field depending on its type
+            switch (sig.charAt(0)) {
+            case 'Z':
+                insn = appendCaseBranchForProvideField(
+                    insn,
+                    JDO_SM_providedBooleanField_Name,
+                    JDO_SM_providedBooleanField_Sig,
+                    ref);
+                break;
+            case 'C':
+                insn = appendCaseBranchForProvideField(
+                    insn,
+                    JDO_SM_providedCharField_Name,
+                    JDO_SM_providedCharField_Sig,
+                    ref);
+                break;
+            case 'B':
+                insn = appendCaseBranchForProvideField(
+                    insn,
+                    JDO_SM_providedByteField_Name,
+                    JDO_SM_providedByteField_Sig,
+                    ref);
+                break;
+            case 'S':
+                insn = appendCaseBranchForProvideField(
+                    insn,
+                    JDO_SM_providedShortField_Name,
+                    JDO_SM_providedShortField_Sig,
+                    ref);
+                break;
+            case 'I':
+                insn = appendCaseBranchForProvideField(
+                    insn,
+                    JDO_SM_providedIntField_Name,
+                    JDO_SM_providedIntField_Sig,
+                    ref);
+                break;
+            case 'J':
+                insn = appendCaseBranchForProvideField(
+                    insn,
+                    JDO_SM_providedLongField_Name,
+                    JDO_SM_providedLongField_Sig,
+                    ref);
+                break;
+            case 'F':
+                insn = appendCaseBranchForProvideField(
+                    insn,
+                    JDO_SM_providedFloatField_Name,
+                    JDO_SM_providedFloatField_Sig,
+                    ref);
+                break;
+            case 'D':
+                insn = appendCaseBranchForProvideField(
+                    insn,
+                    JDO_SM_providedDoubleField_Name,
+                    JDO_SM_providedDoubleField_Sig,
+                    ref);
+                break;
+            case 'L':
+            case '[':
+                if (sig.equals(JAVA_String_Sig)) {
+                    insn = appendCaseBranchForProvideField(
+                        insn,
+                        JDO_SM_providedStringField_Name,
+                        JDO_SM_providedStringField_Sig,
+                        ref);
+                } else {
+                    insn = appendCaseBranchForProvideField(
+                        insn,
+                        JDO_SM_providedObjectField_Name,
+                        JDO_SM_providedObjectField_Sig,
+                        ref);
+                }
+                break;
+            default:
+                affirm(false, "Illegal field type: " + sig);
+            }
+        }
+        
+        // the default branch target comes next
+        insn = insn.append(defaultOp);        
+
+        affirm(insn != null);
+        return insn;
+    }
+
+    /**
+     * Build the jdoProvideField method for the class.
+     *
+     * public void jdoProvideField(int fieldnumber)
+     * {
+     *     final javax.jdo.StateManager sm = this.jdoStateManager;
+     *     switch(fieldnumber - jdoInheritedFieldCount) {
+     *     case 0:
+     *         sm.providedXXXField(this, fieldnumber, this.yyy);
+     *         return;
+     *     case 1:
+     *         ...
+     *     default:
+     *         <if (isPCRoot) {>
+     *             throw new javax.jdo.JDOFatalInternalException();
+     *         <} else {>
+     *             super.jdoProvideField(fieldnumber);
+     *         <}>
+     *     }
+     * }
+     */
+    public void addJDOProvideFieldMethod()
+    {
+        final String methodName = JDO_PC_jdoProvideField_Name;
+        final String methodSig = JDO_PC_jdoProvideField_Sig;
+        final int accessFlags = JDO_PC_jdoProvideField_Mods;
+        final ExceptionsAttribute exceptAttr = null;
+
+        // begin of method body
+        final InsnTarget begin = new InsnTarget();
+        Insn insn = begin;
+
+        // generate the begin code
+        insn = appendBeginProvideReplaceField(insn);
+        
+        // generate the switch code
+        final SizeHolder sizeHolder = new SizeHolder();
+        insn = appendSwitchForProvideField(insn, sizeHolder);
+        
+        // generate the default-branch code with throw/return
+        insn = appendEndProvideReplaceField(insn,
+                                            JDO_PC_jdoProvideField_Name,
+                                            JDO_PC_jdoProvideField_Sig);
+
+        // end of method body
+        affirm(insn.opcode() == opc_athrow || insn.opcode() == opc_return);
+        
+        affirm(0 <= sizeHolder.size && sizeHolder.size <= 2);
+        //System.out.println("sizeHolder.size = " + sizeHolder.size);
+        final int maxStack = (sizeHolder.size == 0
+                              ? 3 : (sizeHolder.size == 1 ? 4 : 5));
+        final CodeAttribute codeAttr
+            = new CodeAttribute(getCodeAttributeUtf8(),
+                                maxStack, // maxStack
+                                3, // maxLocals
+                                begin,
+                                new ExceptionTable(),

[... 2401 lines stripped ...]