Added: incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/enhancer/core/Builder.java URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/enhancer/core/Builder.java?view=auto&rev=158176 ============================================================================== --- incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/enhancer/core/Builder.java (added) +++ incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/enhancer/core/Builder.java Fri Mar 18 17:02:29 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 ...]