Added: incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/enhancer/core/Annotater.java URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/enhancer/core/Annotater.java?view=auto&rev=158176 ============================================================================== --- incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/enhancer/core/Annotater.java (added) +++ incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/enhancer/core/Annotater.java Fri Mar 18 17:02:29 2005 @@ -0,0 +1,354 @@ +/* + * 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.Iterator; + +import org.apache.jdo.impl.enhancer.classfile.ClassFile; +import org.apache.jdo.impl.enhancer.classfile.ClassMethod; +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.ConstMethodRef; +import org.apache.jdo.impl.enhancer.classfile.ConstNameAndType; +import org.apache.jdo.impl.enhancer.classfile.ConstantPool; +import org.apache.jdo.impl.enhancer.classfile.Descriptor; +import org.apache.jdo.impl.enhancer.classfile.Insn; +import org.apache.jdo.impl.enhancer.classfile.InsnConstOp; +import org.apache.jdo.impl.enhancer.classfile.VMConstants; +import org.apache.jdo.impl.enhancer.meta.EnhancerMetaData; +import org.apache.jdo.impl.enhancer.util.Support; + + + + + +/** + * Handles the augmentation actions for a method. + */ +class Annotater + extends Support + implements VMConstants +{ + /** + * The classfile's enhancement controller. + */ + private final Controller control; + + /** + * The class analyzer for this class. + */ + private final Analyzer analyzer; + + /** + * The classfile to be enhanced. + */ + private final ClassFile classFile; + + /** + * The class name in user ('.' delimited) form. + */ + private final String userClassName; + + /** + * The classfile's constant pool. + */ + private final ConstantPool pool; + + /** + * Repository for the enhancer options. + */ + private final Environment env; + + /** + * Repository for JDO meta-data on classes. + */ + private final EnhancerMetaData meta; + + /** + * Constructor + */ + public Annotater(Controller control, + Analyzer analyzer, + Environment env) + { + affirm(control != null); + affirm(analyzer != null); + affirm(env != null); + + this.control = control; + this.analyzer = analyzer; + this.env = env; + this.meta = env.getEnhancerMetaData(); + this.classFile = control.getClassFile(); + this.userClassName = classFile.userClassName(); + this.pool = classFile.pool(); + + affirm(classFile != null); + affirm(userClassName != null); + affirm(meta != null); + affirm(pool != null); + } + + /** + * Performs necessary annotation actions on the class. + */ + public void annotate() + { + affirm(analyzer.isAnnotateable() && !env.noAnnotate()); + env.message("annotating class " + userClassName); + + boolean annotated = false; + for (final Iterator i = analyzer.getAnnotatableMethods().iterator(); + i.hasNext();) { + final ClassMethod method = (ClassMethod)i.next(); + annotated |= annotated(method); + } + + // notify controller if class changed + if (annotated) { + control.noteUpdate(); + } + +//^olsen: reenable +/* + //@olsen: do special annotation if detected super.clone() + if ((annotate & SuperClone) != 0) { + //final String superName = control.classFile().superName().asString(); + //annotateClone(method, superName); + } +*/ + } + + /** + * Annotate the class method. For now, brute force rules. + */ + private boolean annotated(ClassMethod method) + { + boolean annotated = false; + final CodeAttribute codeAttr = method.codeAttribute(); + if (codeAttr == null) { + return annotated; + } + + env.message( + "annotating: " + userClassName + + "." + method.name().asString() + + Descriptor.userMethodArgs(method.signature().asString())); + + // first instruction is a target + final Insn firstInsn = codeAttr.theCode(); + affirm(firstInsn.opcode() == Insn.opc_target); + Insn insn = firstInsn.next(); + while (insn != null) { + switch(insn.opcode()) { + case opc_getfield: + case opc_putfield: + final Insn newInsn = insnAnnotation(insn); + if (insn != newInsn) { + annotated = true; + } + insn = newInsn; + break; + default: + } + + insn = insn.next(); + } + + return annotated; + } + + /** + * Generate annotations for put/getfield instructions. + */ + private Insn insnAnnotation(final Insn insn) + { + if (false) { + System.out.println("MethodAnnotator.insnAnnotation(): "); + insn.printInsn(System.out); + System.out.println(); + } + + affirm(insn.opcode() == opc_getfield || insn.opcode() == opc_putfield); + final boolean isGet = (insn.opcode() == opc_getfield); + + // get the instruction arguments + final InsnConstOp fieldInsn = (InsnConstOp)insn; + final ConstFieldRef fieldRef = (ConstFieldRef)fieldInsn.value(); + + final ConstNameAndType fieldNameAndType = fieldRef.nameAndType(); + final String fieldName = fieldNameAndType.name().asString(); + final String fieldType = fieldNameAndType.signature().asString(); + + final String qualifyingClassName = fieldRef.className().asString(); + // get the field's declaring class from the model + final String declClassName = + meta.getDeclaringClass(qualifyingClassName, fieldName); + affirm(declClassName != null, "Cannot get declaring class of " + + qualifyingClassName + "." + fieldName); + final ConstClass declClass = pool.addClass(declClassName); + + // check if field is known to be non-managed + if (meta.isKnownNonManagedField(declClassName, fieldName, fieldType)) { + return insn; + } + + // never annotate a jdo field; such may occur in pre-enhanced clone() + if (meta.isPersistenceCapableClass(declClassName) + && (fieldName.equals(JDOConstants.JDO_PC_jdoStateManager_Name) + || fieldName.equals(JDOConstants.JDO_PC_jdoFlags_Name))) { + return insn; + } + + if (false) { + System.out.println(" " + (isGet ? "get" : "put") + "field " + + declClassName + "." + fieldName + + " : " + fieldType); + } + + final String methodName; + final String methodSig; + if (isGet) { + methodName = "jdoGet" + fieldName; + methodSig = "(L" + declClassName + ";)" + fieldType; + } else { + methodName = "jdoSet" + fieldName; + methodSig = "(L" + declClassName + ";" + fieldType + ")V"; + } + + if (false) { + System.out.println(" " + + declClassName + "." + methodName + + " : " + methodSig); + } + + // call the PC's static accessor/mutator + final Insn frag = Insn.create(opc_invokestatic, + pool.addMethodRef(declClassName, + methodName, + methodSig)); + + //insn.prev().insert(Insn.create(opc_nop)); + // replace instruction + final Insn prev = insn.prev(); + insn.remove(); + + // replace current instruction with new fragment + final Insn last = prev.insert(frag); + return last; + } + + private void annotateClone(ClassMethod method, + String superName) + { + //^olsen: extend for full support of inheritance on PC classes + + if (false) { + final String methodName = method.name().asString(); + final String methodSig = method.signature().asString(); + System.out.println("annotateClone()"); + System.out.println(" methodName = " + methodName); + System.out.println(" methodSig = " + methodSig); + System.out.println(" superName = " + superName); + } + + final CodeAttribute codeAttr = method.codeAttribute(); + for (Insn insn = codeAttr.theCode(); + insn != null; + insn = insn.next()) { + + // Found the clone method. See if it is the flavor of clone() + // which does a super.clone() call, and if it is, add + // field initializations for the jdoStateManager and jdoFlags + // fields. + if (insn.opcode() != opc_invokespecial) + continue; + + final InsnConstOp invoke = (InsnConstOp)insn; + final ConstMethodRef methodRef = (ConstMethodRef)invoke.value(); + final ConstNameAndType methodNT = methodRef.nameAndType(); + final String methodName = methodNT.name().asString(); + final String methodSig = methodNT.signature().asString(); + + if (!(methodName.equals("clone") + && methodSig.equals("()Ljava/lang/Object;"))) + continue; + + if (false) { + final ConstClass methodClass = methodRef.className(); + final String methodClassName = methodClass.asString(); + System.out.println(" found invocation of: " + + methodClassName + + "." + methodName + methodSig); + } + + // check whether next instruction already is a downcast to a + // class implementing PersistenceCapable + final String thisClass = classFile.classNameString(); + final Insn checkCastInsn = insn.next(); + final boolean needCheckcast; + if (checkCastInsn.opcode() != opc_checkcast) { + needCheckcast = true; + } else { + ConstClass target = + (ConstClass) ((InsnConstOp)checkCastInsn).value(); + if (target.asString().equals(thisClass)) { + insn = checkCastInsn; + needCheckcast = false; + } else { + needCheckcast = true; + } + } + + // clear jdo fields of clone + { + // duplicate downcasted reference + final Insn newInsn = Insn.create(opc_dup); + if (needCheckcast) { + newInsn.append(Insn.create(opc_checkcast, + pool.addClass(thisClass))); + } + newInsn.append(Insn.create(opc_dup)); + + // clear jdo fields + newInsn.append(Insn.create(opc_aconst_null)); + newInsn.append(Insn.create( + opc_putfield, + pool.addFieldRef( + thisClass, + JDOConstants.JDO_PC_jdoStateManager_Name, + JDOConstants.JDO_PC_jdoStateManager_Sig))); + newInsn.append(Insn.create(opc_iconst_0)); + newInsn.append(Insn.create( + opc_putfield, + pool.addFieldRef( + thisClass, + JDOConstants.JDO_PC_jdoFlags_Name, + JDOConstants.JDO_PC_jdoFlags_Sig))); + + // insert code + insn.insert(newInsn); + + // increase stack + final int annotationStack = 3; + codeAttr.setStackUsed(codeAttr.stackUsed() + annotationStack); + } + } + } +}
Added: incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/enhancer/core/Augmenter.java URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/enhancer/core/Augmenter.java?view=auto&rev=158176 ============================================================================== --- incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/enhancer/core/Augmenter.java (added) +++ incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/enhancer/core/Augmenter.java Fri Mar 18 17:02:29 2005 @@ -0,0 +1,612 @@ +/* + * 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.Enumeration; +import java.util.Iterator; +import java.util.List; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Set; +import java.util.HashSet; +import java.util.Map; +import java.util.HashMap; + +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.ClassMethod; +import org.apache.jdo.impl.enhancer.classfile.CodeAttribute; +import org.apache.jdo.impl.enhancer.classfile.ConstClass; +import org.apache.jdo.impl.enhancer.classfile.ConstantPool; +import org.apache.jdo.impl.enhancer.classfile.Descriptor; +import org.apache.jdo.impl.enhancer.classfile.ExceptionsAttribute; +import org.apache.jdo.impl.enhancer.classfile.Insn; +import org.apache.jdo.impl.enhancer.classfile.InsnTarget; +import org.apache.jdo.impl.enhancer.classfile.LineNumberTableAttribute; +import org.apache.jdo.impl.enhancer.classfile.SyntheticAttribute; +import org.apache.jdo.impl.enhancer.classfile.VMConstants; +import org.apache.jdo.impl.enhancer.meta.EnhancerMetaData; +import org.apache.jdo.impl.enhancer.util.Support; + + + + +/** + * Handles the augmentation actions for a class. + */ +final class Augmenter + extends Support + implements JDOConstants +{ + //@olsen: fix for bug 4467428: + // Debugging under jdk 1.3.1 shows the problem that any breakpoints + // in PC classes are ignored if the added jdo methods do NOT have a + // non-empty line number table attribute, no matter whether the + // 'Synthetic' attribute is given or not. However, this doesn't + // seem to comply with the JVM Spec (2nd edition), which states + // that the synthetic attribute _must_ be specified if no source + // code information is available for the member: + // + // 4.7.6 The Synthetic Attribute + // ... A class member that does not appear in the source code must + // be marked using a Synthetic attribute. ... + // + // 4.7.8 The LineNumberTable Attribute + // The LineNumberTable attribute is an optional variable-length + // attribute in the attributes table of a Code (Â4.7.3) + // attribute. It may be used by debuggers to determine which + // part of the Java virtual machine code array corresponds to a + // given line number in the original source file. ... Furthermore, + // multiple LineNumberTable attributes may together represent a + // given line of a source file; that is, LineNumberTable attributes + // need not be one-to-one with source lines. + // + // Unfortunately, if we do both, adding the synthetic attribute and + // a (dummy) line number table on generated methods, jdk's 1.3.1 javap + // fails to disassemble the classfile with an exception: + // + // sun.tools.java.CompilerError: checkOverride() synthetic + // + // So, to workaround these problems and to allow for both, debugging + // and disassembling with the jdk (1.3.1) tools, we pretend that the + // generated jdo methods have source code equivalents by + // - not adding the synthetic code attribute + // - providing a dummy line number table code attribute + static private final boolean addSyntheticAttr = false; + static private final boolean addLineNumberTableAttr = true; + + /** + * The classfile's enhancement controller. + */ + private final Controller control; + + /** + * The class analyzer for this class. + */ + private final Analyzer analyzer; + + /** + * The classfile to be enhanced. + */ + 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 method builder helper object. + */ + private final Builder builder; + + // public accessors + + /** + * Constructor + */ + public Augmenter(Controller control, + Analyzer analyzer, + Environment env) + { + affirm(control != null); + affirm(analyzer != null); + affirm(env != null); + + this.control = control; + this.analyzer = analyzer; + this.env = env; + this.classFile = control.getClassFile(); + this.className = classFile.classNameString(); + this.userClassName = classFile.userClassName(); + this.pool = classFile.pool(); + this.builder = new Builder(analyzer, this, env); + + affirm(classFile != null); + affirm(className != null); + affirm(userClassName != null); + affirm(pool != null); + affirm(builder != null); + } + + // ---------------------------------------------------------------------- + + //^olsen: check public access modifier + + /** + * Adds the augmentation to the class. + */ + public void augment() + { + affirm(analyzer.isAugmentable() && !env.noAugment()); + env.message("augmenting class " + userClassName); + + if (analyzer.isAugmentableAsRoot()) { + augmentGenericJDOFields(); + augmentGenericJDOMethods(); + } + augmentClassInterface(JDO_PersistenceCapable_Path); + augmentSpecificJDOFields(); + augmentSpecificJDOMethods(); + augmentJDOAccessorMutatorMethods(); + augmentSerializableSupportMethods(); + } + + /** + * Adds the specified interface to the implements clause of the class. + */ + private void augmentClassInterface(String interfaceName) + { + env.message("adding: implements " + + ClassFile.userClassFromVMClass(interfaceName)); + + final ConstClass iface = pool.addClass(interfaceName); + classFile.addInterface(iface); + + // notify controller of class change + control.noteUpdate(); + } + + /** + * Adds the generic JDO fields to the class. + */ + public void augmentGenericJDOFields() + { + //protected transient javax.jdo.StateManager jdoStateManager + addField( + JDO_PC_jdoStateManager_Name, + JDO_PC_jdoStateManager_Sig, + JDO_PC_jdoStateManager_Mods); + + //protected transient byte jdoFlags + addField( + JDO_PC_jdoFlags_Name, + JDO_PC_jdoFlags_Sig, + JDO_PC_jdoFlags_Mods); + } + + /** + * Adds the specific JDO fields to the class. + */ + public void augmentSpecificJDOFields() + { + //private static final int jdoInheritedFieldCount + addField( + JDO_PC_jdoInheritedFieldCount_Name, + JDO_PC_jdoInheritedFieldCount_Sig, + JDO_PC_jdoInheritedFieldCount_Mods); + + //private static final String[] jdoFieldNames + addField( + JDO_PC_jdoFieldNames_Name, + JDO_PC_jdoFieldNames_Sig, + JDO_PC_jdoFieldNames_Mods); + + //private static final Class[] jdoFieldTypes + addField( + JDO_PC_jdoFieldTypes_Name, + JDO_PC_jdoFieldTypes_Sig, + JDO_PC_jdoFieldTypes_Mods); + + //private static final byte[] jdoFieldFlags + addField( + JDO_PC_jdoFieldFlags_Name, + JDO_PC_jdoFieldFlags_Sig, + JDO_PC_jdoFieldFlags_Mods); + + //private static final Class jdoPersistenceCapableSuperclass + addField( + JDO_PC_jdoPersistenceCapableSuperclass_Name, + JDO_PC_jdoPersistenceCapableSuperclass_Sig, + JDO_PC_jdoPersistenceCapableSuperclass_Mods); + } + + /** + * Adds a field to the class. + */ + private void addField(String fieldName, + String fieldSig, + int accessFlags) + { + affirm(fieldName != null); + affirm(fieldSig != null); + + env.message("adding: " + + Descriptor.userFieldSig(fieldSig) + + " " + fieldName); + + //@olsen: fix 4467428, add synthetic attribute for generated fields + final AttributeVector fieldAttrs = new AttributeVector(); + fieldAttrs.addElement( + new SyntheticAttribute( + pool.addUtf8(SyntheticAttribute.expectedAttrName))); + + // create and add the field + final ClassField field + = new ClassField(accessFlags, + pool.addUtf8(fieldName), + pool.addUtf8(fieldSig), + fieldAttrs); + affirm(classFile.findField(fieldName) == null, + "Attempt to add a repeated field."); + classFile.addField(field); + + // notify controller of class change + control.noteUpdate(); + } + + /** + * Adds the generic JDO methods to the class. + */ + public void augmentGenericJDOMethods() + { + builder.addJDOReplaceFlags(); + builder.addJDOIsPersistentMethod(); + builder.addJDOIsTransactionalMethod(); + builder.addJDOIsNewMethod(); + builder.addJDOIsDeletedMethod(); + builder.addJDOIsDirtyMethod(); + builder.addJDOMakeDirtyMethod(); + builder.addJDOPreSerializeMethod(); + builder.addJDOGetPersistenceManagerMethod(); + builder.addJDOGetObjectIdMethod(); + builder.addJDOGetTransactionalObjectIdMethod(); + builder.addJDOReplaceStateManager(); + builder.addJDOProvideFieldsMethod(); + builder.addJDOReplaceFieldsMethod(); + + builder.addSunJDOClassForNameMethod(); + +/* + if (!hasCloneMethod) { + classFile.addMethod( + builder.makeJDOClone( + this, + JAVA_Object_clone_Name)); + } +*/ + } + + /** + * Adds the specific JDO methods to the class. + */ + public void augmentSpecificJDOMethods() + { + // class registration + builder.addJDOGetManagedFieldCountMethod(); + builder.addStaticInitialization(); + + // instantiation methods + builder.addJDONewInstanceMethod(); + builder.addJDONewInstanceOidMethod(); + + // field handling methods + builder.addJDOProvideFieldMethod(); + builder.addJDOReplaceFieldMethod(); + builder.addJDOCopyFieldMethod(); + builder.addJDOCopyFieldsMethod(); + + // key handling methods + if (analyzer.isAugmentableAsRoot() + || analyzer.getKeyClassName() != null) { + builder.addJDONewObjectIdInstanceMethod(); + builder.addJDONewObjectIdInstanceStringMethod(); + builder.addJDOCopyKeyFieldsToObjectIdMethod(); + builder.addJDOCopyKeyFieldsFromObjectIdMethod(); + builder.addJDOCopyKeyFieldsToObjectIdOIFSMethod(); + builder.addJDOCopyKeyFieldsFromObjectIdOIFCMethod(); + } + +/* + builder.addNullMethod(JDO_PC_jdoProvideField_Name, + JDO_PC_jdoProvideField_Sig, + JDO_PC_jdoProvideField_Mods); + builder.addNullMethod(JDO_PC_jdoReplaceField_Name, + JDO_PC_jdoReplaceField_Sig, + JDO_PC_jdoReplaceField_Mods); +*/ + } + + /** + * Adds the JDO accessor+mutator method for a field. + */ + public void augmentJDOAccessorMutatorMethod(String fieldName, + String fieldSig, + int fieldMods, + int fieldFlags, + int index) + { + affirm(fieldName != null); + affirm(fieldSig != null); + affirm((fieldMods & ACCStatic) == 0); + affirm((fieldFlags & CHECK_READ) == 0 + | (fieldFlags & MEDIATE_READ) == 0); + affirm((fieldFlags & CHECK_WRITE) == 0 + | (fieldFlags & MEDIATE_WRITE) == 0); + + // these combinations are not supported by JDO + affirm((fieldFlags & CHECK_READ) == 0 + | (fieldFlags & MEDIATE_WRITE) == 0); + affirm((fieldFlags & CHECK_WRITE) == 0 + | (fieldFlags & MEDIATE_READ) == 0); + + // add accessor + final String aName + = JDONameHelper.getJDO_PC_jdoAccessor_Name(fieldName); + final String aSig + = JDONameHelper.getJDO_PC_jdoAccessor_Sig(className, fieldSig); + final int aMods + = JDONameHelper.getJDO_PC_jdoAccessor_Mods(fieldMods); + if ((fieldFlags & CHECK_READ) != 0) { + builder.addJDOCheckedReadAccessMethod(aName, aSig, aMods, index); + } else if ((fieldFlags & MEDIATE_READ) != 0) { + builder.addJDOMediatedReadAccessMethod(aName, aSig, aMods, index); + } else { + builder.addJDODirectReadAccessMethod(aName, aSig, aMods, index); + } + + // add mutator + final String mName + = JDONameHelper.getJDO_PC_jdoMutator_Name(fieldName); + final String mSig + = JDONameHelper.getJDO_PC_jdoMutator_Sig(className, fieldSig); + final int mMods + = JDONameHelper.getJDO_PC_jdoMutator_Mods(fieldMods); + if ((fieldFlags & CHECK_WRITE) != 0) { + builder.addJDOCheckedWriteAccessMethod(mName, mSig, mMods, index); + } else if ((fieldFlags & MEDIATE_WRITE) != 0) { + builder.addJDOMediatedWriteAccessMethod(mName, mSig, mMods, index); + } else { + builder.addJDODirectWriteAccessMethod(mName, mSig, mMods, index); + } + } + + /** + * Adds the JDO accessor+mutator methods to the class. + */ + public void augmentJDOAccessorMutatorMethods() + { + final int annotatedFieldCount = analyzer.getAnnotatedFieldCount(); + final String[] annotatedFieldNames = analyzer.getAnnotatedFieldNames(); + final String[] annotatedFieldSigs = analyzer.getAnnotatedFieldSigs(); + final int[] annotatedFieldMods = analyzer.getAnnotatedFieldMods(); + final int[] annotatedFieldFlags = analyzer.getAnnotatedFieldFlags(); + affirm(annotatedFieldNames.length == annotatedFieldCount); + affirm(annotatedFieldSigs.length == annotatedFieldCount); + affirm(annotatedFieldMods.length == annotatedFieldCount); + affirm(annotatedFieldFlags.length == annotatedFieldCount); + + for (int i = 0; i < annotatedFieldCount; i++) { + augmentJDOAccessorMutatorMethod(annotatedFieldNames[i], + annotatedFieldSigs[i], + annotatedFieldMods[i], + annotatedFieldFlags[i], i); + } + } + + /** + * + */ + public void augmentSerializableSupportMethods() + { + final EnhancerMetaData meta = env.getEnhancerMetaData(); + final String pcSuperClassName = analyzer.getPCSuperClassName(); + + // Add serializable support, if + // - this class implements Serializable and + // - the pc superclass (if available) does NOT implement Serializable + if (meta.isSerializableClass(className) && + (pcSuperClassName == null || + !meta.isSerializableClass(pcSuperClassName))) { + // add writeObject if this class does not provide method writeObject and + // does not provide method writeReplace + if (!analyzer.hasWriteObjectMethod() && + !analyzer.hasWriteReplaceMethod()) { + builder.addWriteObjectMethod(); + } + else { + if (analyzer.hasWriteObjectMethod()) { + // add call of jdoPreSerialize to writeObject + builder.addJDOPreSerializeCall( + JAVA_Object_writeObject_Name, + JAVA_Object_writeObject_Sig); + } + if (analyzer.hasWriteReplaceMethod()) { + // add call of jdoPreSerialize to writeReplace + builder.addJDOPreSerializeCall( + JAVA_Object_writeReplace_Name, + JAVA_Object_writeReplace_Sig); + } + } + } + } + + /** + * Adds a method to the class. + */ + void addMethod(String methodName, + String methodSig, + int accessFlags, + CodeAttribute codeAttr, + ExceptionsAttribute exceptAttr) + { + affirm(methodName != null); + affirm(methodSig != null); + affirm(codeAttr != null); + + env.message("adding: " + + Descriptor.userMethodResult(methodSig) + + " " + methodName + + Descriptor.userMethodArgs(methodSig)); + + //@olsen: fix 4467428, add dummy, non-empty line number table + if (addLineNumberTableAttr) { + // get first instruction which always is an instruction target + affirm(codeAttr.theCode().opcode() == Insn.opc_target); + final InsnTarget begin = (InsnTarget)codeAttr.theCode(); + + // get attributes of the code attribute + final AttributeVector codeSpecificAttrs = codeAttr.attributes(); + affirm(codeSpecificAttrs != null); + + // add dummy line number attribute with first instruction + codeSpecificAttrs.addElement( + new LineNumberTableAttribute( + pool.addUtf8(LineNumberTableAttribute.expectedAttrName), + new short[]{ 0 }, new InsnTarget[]{ begin })); + } + + // add the method's code and exception attributes + final AttributeVector methodAttrs = new AttributeVector(); + methodAttrs.addElement(codeAttr); + if (exceptAttr != null) { + methodAttrs.addElement(exceptAttr); + } + + //@olsen: fix 4467428, add synthetic attribute for generated methods + if (addSyntheticAttr) { + methodAttrs.addElement( + new SyntheticAttribute( + pool.addUtf8(SyntheticAttribute.expectedAttrName))); + } + + // create and add the method + final ClassMethod method + = new ClassMethod(accessFlags, + pool.addUtf8(methodName), + pool.addUtf8(methodSig), + methodAttrs); + affirm(classFile.findMethod(methodName, methodSig) == null, + "Attempt to add a repeated method."); + classFile.addMethod(method); + + // notify controller of class change + control.noteUpdate(); + } + + /** + * Extends an exisiting method by prepending code. + */ + void prependMethod(String methodName, + String methodSig, + CodeAttribute codeAttr, + ExceptionsAttribute exceptAttr) + { + affirm(methodName != null); + affirm(methodSig != null); + affirm(codeAttr != null); + + env.message("extending: " + + Descriptor.userMethodResult(methodSig) + + " " + methodName + + Descriptor.userMethodArgs(methodSig)); + + // get method + final ClassMethod method = classFile.findMethod(methodName, methodSig); + affirm(method != null, + "Attempt to add code to a non-existing method."); + + // check the found method + affirm(!method.isAbstract(), + "Attempt to add code to an abstract method."); + affirm(!method.isNative(), + "Attempt to add code to a native method."); + final CodeAttribute foundCodeAttr = method.codeAttribute(); + affirm(foundCodeAttr != null); // by JVM spec + + // prepend the new code to the current one + final Insn firstInsn = codeAttr.theCode(); + affirm(firstInsn != null); + final Insn foundFirstInsn = foundCodeAttr.theCode(); + affirm(foundFirstInsn != null); + final Insn lastInsn = firstInsn.append(foundFirstInsn); + affirm(lastInsn != null); + foundCodeAttr.setTheCode(firstInsn); + + // ajust the method's stack and locals demand + foundCodeAttr.setStackUsed(max(foundCodeAttr.stackUsed(), + codeAttr.stackUsed())); + foundCodeAttr.setLocalsUsed(max(foundCodeAttr.localsUsed(), + codeAttr.localsUsed())); + + // add the exception attribute or its exceptions + if (exceptAttr != null) { + affirm((exceptAttr.getExceptions().size() + == new HashSet(exceptAttr.getExceptions()).size()), + "Exception attribute contains duplicate exceptions."); + + final ExceptionsAttribute foundExceptAttr + = method.exceptionsAttribute(); + if (foundExceptAttr == null) { + // add the exception attribute + final AttributeVector methodAttrs = method.attributes(); + affirm(methodAttrs != null); + methodAttrs.addElement(exceptAttr); + } else { + // add those exceptions not already present + final List foundEx = foundExceptAttr.getExceptions(); + final List newEx = exceptAttr.getExceptions(); + newEx.removeAll(foundEx); + foundEx.addAll(newEx); + } + } + + // notify controller of class change + control.noteUpdate(); + } + + static private int max(int i, int j) + { + return (i < j) ? j : i; + } +}