You are viewing a plain text version of this content. The canonical link for it is here.
Posted to jdo-commits@db.apache.org by mb...@apache.org on 2005/05/22 19:55:54 UTC
svn commit: r171351 [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/
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=171351&view=auto
==============================================================================
--- incubator/jdo/trunk/enhancer20/src/java/org/apache/jdo/impl/enhancer/core/Augmenter.java (added)
+++ incubator/jdo/trunk/enhancer20/src/java/org/apache/jdo/impl/enhancer/core/Augmenter.java Sun May 22 10:55:51 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;
+ }
+}