Return-Path: Delivered-To: apmail-db-jdo-commits-archive@www.apache.org Received: (qmail 52054 invoked from network); 22 May 2005 18:22:55 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (209.237.227.199) by minotaur.apache.org with SMTP; 22 May 2005 18:22:55 -0000 Received: (qmail 45895 invoked by uid 500); 22 May 2005 18:22:54 -0000 Mailing-List: contact jdo-commits-help@db.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: jdo-dev@db.apache.org Delivered-To: mailing list jdo-commits@db.apache.org Received: (qmail 45824 invoked by uid 99); 22 May 2005 18:22:54 -0000 X-ASF-Spam-Status: No, hits=-9.8 required=10.0 tests=ALL_TRUSTED,NO_REAL_NAME X-Spam-Check-By: apache.org Received: from minotaur.apache.org (HELO minotaur.apache.org) (209.237.227.194) by apache.org (qpsmtpd/0.28) with SMTP; Sun, 22 May 2005 11:22:53 -0700 Received: (qmail 38169 invoked by uid 65534); 22 May 2005 17:56:12 -0000 Message-ID: <20050522175612.38167.qmail@minotaur.apache.org> Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Subject: svn commit: r171351 [9/16] - in /incubator/jdo/trunk/enhancer20: ./ src/ src/conf/ src/java/ src/java/org/ src/java/org/apache/ src/java/org/apache/jdo/ src/java/org/apache/jdo/enhancer/ src/java/org/apache/jdo/impl/ src/java/org/apache/jdo/impl/enhancer/ src/java/org/apache/jdo/impl/enhancer/classfile/ src/java/org/apache/jdo/impl/enhancer/core/ src/java/org/apache/jdo/impl/enhancer/generator/ src/java/org/apache/jdo/impl/enhancer/meta/ src/java/org/apache/jdo/impl/enhancer/meta/model/ src/java/org/apache/jdo/impl/enhancer/meta/prop/ src/java/org/apache/jdo/impl/enhancer/meta/util/ src/java/org/apache/jdo/impl/enhancer/util/ test/ test/sempdept/ test/sempdept/src/ test/sempdept/src/empdept/ Date: Sun, 22 May 2005 17:55:54 -0000 To: jdo-commits@db.apache.org From: mbo@apache.org X-Mailer: svnmailer-1.0.0-dev X-Virus-Checked: Checked X-Spam-Rating: minotaur.apache.org 1.6.2 0/1000/N Added: incubator/jdo/trunk/enhancer20/src/java/org/apache/jdo/impl/enhancer= /core/Augmenter.java URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/enhancer20/src/java/= org/apache/jdo/impl/enhancer/core/Augmenter.java?rev=3D171351&view=3Dauto =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D --- incubator/jdo/trunk/enhancer20/src/java/org/apache/jdo/impl/enhancer/co= re/Augmenter.java (added) +++ incubator/jdo/trunk/enhancer20/src/java/org/apache/jdo/impl/enhancer/co= re/Augmenter.java Sun May 22 10:55:51 2005 @@ -0,0 +1,612 @@ +/* + * Copyright 2005 The Apache Software Foundation. + *=20 + * 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=20 + *=20 + * http://www.apache.org/licenses/LICENSE-2.0 + *=20 + * Unless required by applicable law or agreed to in writing, software=20 + * distributed under the License is distributed on an "AS IS" BASIS,=20 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied= .=20 + * See the License for the specific language governing permissions and=20 + * 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 (=C2=A74.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 =3D false; + static private final boolean addLineNumberTableAttr =3D 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 !=3D null); + affirm(analyzer !=3D null); + affirm(env !=3D null); + + this.control =3D control; + this.analyzer =3D analyzer; + this.env =3D env; + this.classFile =3D control.getClassFile(); + this.className =3D classFile.classNameString(); + this.userClassName =3D classFile.userClassName(); + this.pool =3D classFile.pool(); + this.builder =3D new Builder(analyzer, this, env); + + affirm(classFile !=3D null); + affirm(className !=3D null); + affirm(userClassName !=3D null); + affirm(pool !=3D null); + affirm(builder !=3D null); + } + + // -------------------------------------------------------------------= --- + + //^olsen: check public access modifier + =20 + /** + * 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 =3D 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); =20 + + //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 !=3D null); + affirm(fieldSig !=3D null); + + env.message("adding: " + + Descriptor.userFieldSig(fieldSig) + + " " + fieldName); + + //@olsen: fix 4467428, add synthetic attribute for generated fields + final AttributeVector fieldAttrs =3D new AttributeVector(); + fieldAttrs.addElement( + new SyntheticAttribute( + pool.addUtf8(SyntheticAttribute.expectedAttrName))); + + // create and add the field + final ClassField field + =3D new ClassField(accessFlags, + pool.addUtf8(fieldName), + pool.addUtf8(fieldSig), + fieldAttrs); + affirm(classFile.findField(fieldName) =3D=3D 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(); =20 + 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(); + =20 + // field handling methods + builder.addJDOProvideFieldMethod(); + builder.addJDOReplaceFieldMethod(); + builder.addJDOCopyFieldMethod(); + builder.addJDOCopyFieldsMethod(); + + // key handling methods + if (analyzer.isAugmentableAsRoot() + || analyzer.getKeyClassName() !=3D 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 !=3D null); + affirm(fieldSig !=3D null); + affirm((fieldMods & ACCStatic) =3D=3D 0); + affirm((fieldFlags & CHECK_READ) =3D=3D 0 + | (fieldFlags & MEDIATE_READ) =3D=3D 0); + affirm((fieldFlags & CHECK_WRITE) =3D=3D 0 + | (fieldFlags & MEDIATE_WRITE) =3D=3D 0); + + // these combinations are not supported by JDO + affirm((fieldFlags & CHECK_READ) =3D=3D 0 + | (fieldFlags & MEDIATE_WRITE) =3D=3D 0); + affirm((fieldFlags & CHECK_WRITE) =3D=3D 0 + | (fieldFlags & MEDIATE_READ) =3D=3D 0); + + // add accessor + final String aName + =3D JDONameHelper.getJDO_PC_jdoAccessor_Name(fieldName); + final String aSig + =3D JDONameHelper.getJDO_PC_jdoAccessor_Sig(className, fieldSi= g); + final int aMods + =3D JDONameHelper.getJDO_PC_jdoAccessor_Mods(fieldMods); + if ((fieldFlags & CHECK_READ) !=3D 0) { + builder.addJDOCheckedReadAccessMethod(aName, aSig, aMods, inde= x); + } else if ((fieldFlags & MEDIATE_READ) !=3D 0) { + builder.addJDOMediatedReadAccessMethod(aName, aSig, aMods, ind= ex); + } else { + builder.addJDODirectReadAccessMethod(aName, aSig, aMods, index= ); + } + + // add mutator + final String mName + =3D JDONameHelper.getJDO_PC_jdoMutator_Name(fieldName); + final String mSig + =3D JDONameHelper.getJDO_PC_jdoMutator_Sig(className, fieldSig= ); + final int mMods + =3D JDONameHelper.getJDO_PC_jdoMutator_Mods(fieldMods); + if ((fieldFlags & CHECK_WRITE) !=3D 0) { + builder.addJDOCheckedWriteAccessMethod(mName, mSig, mMods, ind= ex); + } else if ((fieldFlags & MEDIATE_WRITE) !=3D 0) { + builder.addJDOMediatedWriteAccessMethod(mName, mSig, mMods, in= dex); + } else { + builder.addJDODirectWriteAccessMethod(mName, mSig, mMods, inde= x); + } + } + =20 + /** + * Adds the JDO accessor+mutator methods to the class. + */ + public void augmentJDOAccessorMutatorMethods() + { + final int annotatedFieldCount =3D analyzer.getAnnotatedFieldCount(= ); + final String[] annotatedFieldNames =3D analyzer.getAnnotatedFieldN= ames(); + final String[] annotatedFieldSigs =3D analyzer.getAnnotatedFieldSi= gs(); + final int[] annotatedFieldMods =3D analyzer.getAnnotatedFieldMods(= ); + final int[] annotatedFieldFlags =3D analyzer.getAnnotatedFieldFlag= s(); + affirm(annotatedFieldNames.length =3D=3D annotatedFieldCount); + affirm(annotatedFieldSigs.length =3D=3D annotatedFieldCount); + affirm(annotatedFieldMods.length =3D=3D annotatedFieldCount); + affirm(annotatedFieldFlags.length =3D=3D annotatedFieldCount); + + for (int i =3D 0; i < annotatedFieldCount; i++) { + augmentJDOAccessorMutatorMethod(annotatedFieldNames[i], + annotatedFieldSigs[i], + annotatedFieldMods[i], + annotatedFieldFlags[i], i); + } + } + + /** + * + */ + public void augmentSerializableSupportMethods() + { + final EnhancerMetaData meta =3D env.getEnhancerMetaData(); + final String pcSuperClassName =3D analyzer.getPCSuperClassName(); + =20 + // Add serializable support, if=20 + // - this class implements Serializable and + // - the pc superclass (if available) does NOT implement Serializa= ble + if (meta.isSerializableClass(className) && + (pcSuperClassName =3D=3D null ||=20 + !meta.isSerializableClass(pcSuperClassName))) { + // add writeObject if this class does not provide method write= Object and=20 + // does not provide method writeReplace + if (!analyzer.hasWriteObjectMethod() &&=20 + !analyzer.hasWriteReplaceMethod()) { + builder.addWriteObjectMethod(); + } + else { + if (analyzer.hasWriteObjectMethod()) { + // add call of jdoPreSerialize to writeObject + builder.addJDOPreSerializeCall( + JAVA_Object_writeObject_Name,=20 + JAVA_Object_writeObject_Sig); + } + if (analyzer.hasWriteReplaceMethod()) { + // add call of jdoPreSerialize to writeReplace + builder.addJDOPreSerializeCall( + JAVA_Object_writeReplace_Name,=20 + JAVA_Object_writeReplace_Sig); + } + } + } + } + + /** + * Adds a method to the class. + */ + void addMethod(String methodName, + String methodSig, + int accessFlags, + CodeAttribute codeAttr, + ExceptionsAttribute exceptAttr) + { + affirm(methodName !=3D null); + affirm(methodSig !=3D null); + affirm(codeAttr !=3D 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() =3D=3D Insn.opc_target); + final InsnTarget begin =3D (InsnTarget)codeAttr.theCode(); + + // get attributes of the code attribute + final AttributeVector codeSpecificAttrs =3D codeAttr.attribute= s(); + affirm(codeSpecificAttrs !=3D null); + =20 + // 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 =3D new AttributeVector(); + methodAttrs.addElement(codeAttr); + if (exceptAttr !=3D null) { + methodAttrs.addElement(exceptAttr); + } + =20 + //@olsen: fix 4467428, add synthetic attribute for generated metho= ds + if (addSyntheticAttr) { + methodAttrs.addElement( + new SyntheticAttribute( + pool.addUtf8(SyntheticAttribute.expectedAttrName))); + } + =20 + // create and add the method + final ClassMethod method + =3D new ClassMethod(accessFlags, + pool.addUtf8(methodName), + pool.addUtf8(methodSig), + methodAttrs); + affirm(classFile.findMethod(methodName, methodSig) =3D=3D 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 !=3D null); + affirm(methodSig !=3D null); + affirm(codeAttr !=3D null); + + env.message("extending: " + + Descriptor.userMethodResult(methodSig) + + " " + methodName + + Descriptor.userMethodArgs(methodSig)); + + // get method + final ClassMethod method =3D classFile.findMethod(methodName, meth= odSig); + affirm(method !=3D 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 =3D method.codeAttribute(); + affirm(foundCodeAttr !=3D null); // by JVM spec + + // prepend the new code to the current one + final Insn firstInsn =3D codeAttr.theCode(); + affirm(firstInsn !=3D null); + final Insn foundFirstInsn =3D foundCodeAttr.theCode(); + affirm(foundFirstInsn !=3D null); + final Insn lastInsn =3D firstInsn.append(foundFirstInsn); + affirm(lastInsn !=3D 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 !=3D null) { + affirm((exceptAttr.getExceptions().size() + =3D=3D new HashSet(exceptAttr.getExceptions()).size()), + "Exception attribute contains duplicate exceptions."); + =20 + final ExceptionsAttribute foundExceptAttr + =3D method.exceptionsAttribute(); + if (foundExceptAttr =3D=3D null) { + // add the exception attribute + final AttributeVector methodAttrs =3D method.attributes(); + affirm(methodAttrs !=3D null); + methodAttrs.addElement(exceptAttr); + } else { + // add those exceptions not already present + final List foundEx =3D foundExceptAttr.getExceptions(); + final List newEx =3D exceptAttr.getExceptions(); + newEx.removeAll(foundEx); + foundEx.addAll(newEx); + } + } + =20 + // notify controller of class change + control.noteUpdate(); + } + + static private int max(int i, int j)=20 + { + return (i < j) ? j : i; + } +}