Home » apache-openjpa-1.1.0-source » org.apache.openjpa » enhance » [javadoc | source]
    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one
    3    * or more contributor license agreements.  See the NOTICE file
    4    * distributed with this work for additional information
    5    * regarding copyright ownership.  The ASF licenses this file
    6    * to you under the Apache License, Version 2.0 (the
    7    * "License"); you may not use this file except in compliance
    8    * with the License.  You may obtain a copy of the License at
    9    *
   10    * http://www.apache.org/licenses/LICENSE-2.0
   11    *
   12    * Unless required by applicable law or agreed to in writing,
   13    * software distributed under the License is distributed on an
   14    * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
   15    * KIND, either express or implied.  See the License for the
   16    * specific language governing permissions and limitations
   17    * under the License.    
   18    */
   19   package org.apache.openjpa.enhance;
   20   
   21   import java.io.Externalizable;
   22   import java.io.File;
   23   import java.io.IOException;
   24   import java.io.ObjectInput;
   25   import java.io.ObjectInputStream;
   26   import java.io.ObjectOutput;
   27   import java.io.ObjectOutputStream;
   28   import java.io.ObjectStreamClass;
   29   import java.io.Serializable;
   30   import java.io.ObjectStreamException;
   31   import java.lang.reflect.Field;
   32   import java.lang.reflect.Method;
   33   import java.lang.reflect.Modifier;
   34   import java.security.AccessController;
   35   import java.security.PrivilegedActionException;
   36   import java.util.ArrayList;
   37   import java.util.Arrays;
   38   import java.util.Collection;
   39   import java.util.Date;
   40   import java.util.HashMap;
   41   import java.util.HashSet;
   42   import java.util.Iterator;
   43   import java.util.List;
   44   import java.util.Map;
   45   import java.util.Set;
   46   
   47   import org.apache.commons.lang.StringUtils;
   48   import org.apache.openjpa.conf.OpenJPAConfiguration;
   49   import org.apache.openjpa.conf.OpenJPAConfigurationImpl;
   50   import org.apache.openjpa.lib.conf.Configurations;
   51   import org.apache.openjpa.lib.log.Log;
   52   import org.apache.openjpa.lib.meta.ClassArgParser;
   53   import org.apache.openjpa.lib.util.BytecodeWriter;
   54   import org.apache.openjpa.lib.util.Files;
   55   import org.apache.openjpa.lib.util.J2DoPrivHelper;
   56   import org.apache.openjpa.lib.util.Localizer;
   57   import org.apache.openjpa.lib.util.Options;
   58   import org.apache.openjpa.lib.util.Services;
   59   import org.apache.openjpa.lib.util.Localizer.Message;
   60   import org.apache.openjpa.meta.ClassMetaData;
   61   import org.apache.openjpa.meta.FieldMetaData;
   62   import org.apache.openjpa.meta.JavaTypes;
   63   import org.apache.openjpa.meta.MetaDataRepository;
   64   import org.apache.openjpa.meta.ValueStrategies;
   65   import org.apache.openjpa.util.GeneralException;
   66   import org.apache.openjpa.util.InternalException;
   67   import org.apache.openjpa.util.BigDecimalId;
   68   import org.apache.openjpa.util.BigIntegerId;
   69   import org.apache.openjpa.util.ByteId;
   70   import org.apache.openjpa.util.CharId;
   71   import org.apache.openjpa.util.DateId;
   72   import org.apache.openjpa.util.DoubleId;
   73   import org.apache.openjpa.util.Id;
   74   import org.apache.openjpa.util.IntId;
   75   import org.apache.openjpa.util.FloatId;
   76   import org.apache.openjpa.util.LongId;
   77   import org.apache.openjpa.util.ObjectId;
   78   import org.apache.openjpa.util.ShortId;
   79   import org.apache.openjpa.util.StringId;
   80   import org.apache.openjpa.util.OpenJPAException;
   81   import org.apache.openjpa.util.UserException;
   82   import org.apache.openjpa.util.ImplHelper;
   83   import serp.bytecode.BCClass;
   84   import serp.bytecode.BCField;
   85   import serp.bytecode.BCMethod;
   86   import serp.bytecode.Code;
   87   import serp.bytecode.Constants;
   88   import serp.bytecode.Exceptions;
   89   import serp.bytecode.FieldInstruction;
   90   import serp.bytecode.GetFieldInstruction;
   91   import serp.bytecode.IfInstruction;
   92   import serp.bytecode.Instruction;
   93   import serp.bytecode.JumpInstruction;
   94   import serp.bytecode.LoadInstruction;
   95   import serp.bytecode.MethodInstruction;
   96   import serp.bytecode.Project;
   97   import serp.bytecode.TableSwitchInstruction;
   98   import serp.bytecode.ClassInstruction;
   99   import serp.util.Numbers;
  100   import serp.util.Strings;
  101   
  102   /**
  103    * Bytecode enhancer used to enhance persistent classes from metadata. The
  104    * enhancer must be invoked on all persistence-capable and persistence aware
  105    * classes.
  106    *
  107    * @author Abe White
  108    */
  109   public class PCEnhancer {
  110       // Designates a version for maintaining compatbility when PCEnhancer
  111       // modifies enhancement that can break serialization or other contracts
  112       // Each enhanced class will return the value of this field via
  113       // public int getEnhancementContractVersion()
  114       public static final int ENHANCER_VERSION = 2;
  115   
  116       public static final int ENHANCE_NONE = 0;
  117       public static final int ENHANCE_AWARE = 2 << 0;
  118       public static final int ENHANCE_INTERFACE = 2 << 1;
  119       public static final int ENHANCE_PC = 2 << 2;
  120   
  121       public static final String PRE = "pc";
  122       public static final String ISDETACHEDSTATEDEFINITIVE = PRE 
  123           + "isDetachedStateDefinitive";
  124   
  125       private static final Class PCTYPE = PersistenceCapable.class;
  126       private static final String SM = PRE + "StateManager";
  127       private static final Class SMTYPE = StateManager.class;
  128       private static final String INHERIT = PRE + "InheritedFieldCount";
  129       private static final String CONTEXTNAME = "GenericContext";
  130       private static final Class USEREXCEP = UserException.class;
  131       private static final Class INTERNEXCEP = InternalException.class;
  132       private static final Class HELPERTYPE = PCRegistry.class;
  133       private static final String SUPER = PRE + "PCSuperclass";
  134       private static final Class OIDFSTYPE = FieldSupplier.class;
  135       private static final Class OIDFCTYPE = FieldConsumer.class;
  136   
  137       private static final Localizer _loc = Localizer.forPackage
  138           (PCEnhancer.class);
  139       private static final String REDEFINED_ATTRIBUTE
  140           = PCEnhancer.class.getName() + "#redefined-type";
  141       
  142       private static final AuxiliaryEnhancer[] _auxEnhancers;
  143       static {
  144           Class[] classes = Services.getImplementorClasses(
  145               AuxiliaryEnhancer.class,
  146               (ClassLoader) AccessController.doPrivileged(
  147                   J2DoPrivHelper.getClassLoaderAction(AuxiliaryEnhancer.class)));
  148           List auxEnhancers = new ArrayList(classes.length);
  149           for (int i = 0; i < classes.length; i++) {
  150               try {
  151                   auxEnhancers.add(AccessController.doPrivileged(
  152                       J2DoPrivHelper.newInstanceAction(classes[i])));
  153   		    } catch (Throwable t) {
  154                   // aux enhancer may rely on non-existant spec classes, etc
  155   		    }
  156   		}
  157       	_auxEnhancers = (AuxiliaryEnhancer[]) auxEnhancers.toArray
  158               (new AuxiliaryEnhancer[auxEnhancers.size()]);
  159       }
  160   
  161       private BCClass _pc;
  162       private final BCClass _managedType;
  163       private final MetaDataRepository _repos;
  164       private final ClassMetaData _meta;
  165       private final Log _log;
  166       private Collection _oids = null;
  167       private boolean _defCons = true;
  168       private boolean _redefine = false;
  169       private boolean _subclass = false;
  170       private boolean _fail = false;
  171       private Set _violations = null;
  172       private File _dir = null;
  173       private BytecodeWriter _writer = null;
  174       private Map _backingFields = null; // map of set / get names => field names
  175       private Map _attrsToFields = null; // map of attr names => field names
  176       private Map _fieldsToAttrs = null; // map of field names => attr names
  177       private boolean _isAlreadyRedefined = false;
  178       private boolean _isAlreadySubclassed = false;
  179       private boolean _bcsConfigured = false;
  180   
  181       /**
  182        * Constructor. Supply configuration and type to enhance. This will look
  183        * up the metadata for <code>type</code> from <code>conf</code>'s
  184        * repository.
  185        */
  186       public PCEnhancer(OpenJPAConfiguration conf, Class type) {
  187           this(conf, (BCClass) AccessController.doPrivileged(J2DoPrivHelper
  188               .loadProjectClassAction(new Project(), type)),
  189               (MetaDataRepository) null);
  190       }
  191   
  192       /**
  193        * Constructor. Supply configuration and type to enhance. This will look
  194        * up the metadata for <code>meta</code> by converting back to a class
  195        * and then loading from <code>conf</code>'s repository.
  196        */
  197       public PCEnhancer(OpenJPAConfiguration conf, ClassMetaData meta) {
  198           this(conf, (BCClass) AccessController.doPrivileged(J2DoPrivHelper
  199               .loadProjectClassAction(new Project(), meta.getDescribedType())),
  200               meta.getRepository());
  201       }
  202   
  203       /**
  204        * Constructor. Supply configuration.
  205        *
  206        * @param type the bytecode representation fo the type to
  207        * enhance; this can be created from any stream or file
  208        * @param repos a metadata repository to use for metadata access,
  209        * or null to create a new reporitory; the repository
  210        * from the given configuration isn't used by default
  211        * because the configuration might be an
  212        * implementation-specific subclass whose metadata
  213        * required more than just base metadata files
  214        * @deprecated use {@link #PCEnhancer(OpenJPAConfiguration, BCClass,
  215           MetaDataRepository, ClassLoader)} instead. 
  216        */
  217       public PCEnhancer(OpenJPAConfiguration conf, BCClass type,
  218           MetaDataRepository repos) {
  219           this(conf, type, repos, null);
  220       }
  221   
  222       /**
  223        * Constructor. Supply configuration.
  224        *
  225        * @param type the bytecode representation fo the type to
  226        * enhance; this can be created from any stream or file
  227        * @param repos a metadata repository to use for metadata access,
  228        * or null to create a new reporitory; the repository
  229        * from the given configuration isn't used by default
  230        * because the configuration might be an
  231        * implementation-specific subclass whose metadata
  232        * required more than just base metadata files
  233        * @param loader the environment classloader to use for loading
  234        * classes and resources.
  235        */
  236       public PCEnhancer(OpenJPAConfiguration conf, BCClass type,
  237           MetaDataRepository repos, ClassLoader loader) {
  238           _managedType = type;
  239           _pc = type;
  240   
  241           _log = conf.getLog(OpenJPAConfiguration.LOG_ENHANCE);
  242   
  243           if (repos == null) {
  244               _repos = conf.newMetaDataRepositoryInstance();
  245               _repos.setSourceMode(MetaDataRepository.MODE_META);
  246           } else
  247               _repos = repos;
  248           _meta = _repos.getMetaData(type.getType(), loader, false);
  249       }
  250   
  251       /**
  252        * Constructor. Supply repository. The repository's configuration will
  253        * be used, and the metadata passed in will be used as-is without doing
  254        * any additional lookups. This is useful when running the enhancer
  255        * during metadata load.
  256        *
  257        * @param repos a metadata repository to use for metadata access,
  258        * or null to create a new reporitory; the repository
  259        * from the given configuration isn't used by default
  260        * because the configuration might be an
  261        * implementation-specific subclass whose metadata
  262        * required more than just base metadata files
  263        * @param type the bytecode representation fo the type to
  264        * enhance; this can be created from any stream or file
  265        * @param meta the metadata to use for processing this type.
  266        *
  267        * @since 1.1.0
  268        */
  269       public PCEnhancer(MetaDataRepository repos, BCClass type,
  270           ClassMetaData meta) {
  271           _managedType = type;
  272           _pc = type;
  273   
  274           _log = repos.getConfiguration()
  275               .getLog(OpenJPAConfiguration.LOG_ENHANCE);
  276   
  277           _repos = repos;
  278           _meta = meta;
  279       }
  280   
  281       static String toPCSubclassName(Class cls) {
  282           return Strings.getPackageName(PCEnhancer.class) + "."
  283               + cls.getName().replace('.', '$') + "$pcsubclass";
  284       }
  285   
  286       /**
  287        * Whether or not <code>className</code> is the name for a
  288        * dynamically-created persistence-capable subclass.
  289        *
  290        * @since 1.1.0
  291        */
  292       public static boolean isPCSubclassName(String className) {
  293           return className.startsWith(Strings.getPackageName(PCEnhancer.class))
  294               && className.endsWith("$pcsubclass");
  295       }
  296   
  297       /**
  298        * If <code>className</code> is a dynamically-created persistence-capable
  299        * subclass name, returns the name of the class that it subclasses.
  300        * Otherwise, returns <code>className</code>.
  301        *
  302        * @since 1.1.0
  303        */
  304       public static String toManagedTypeName(String className) {
  305           if (isPCSubclassName(className)) {
  306               className = className.substring(
  307                   Strings.getPackageName(PCEnhancer.class).length() + 1);
  308               className = className.substring(0, className.lastIndexOf("$"));
  309               // this is not correct for nested PCs
  310               className = className.replace('$', '.');
  311           }
  312           
  313           return className;
  314       }
  315   
  316       /**
  317        * Constructor. Supply configuration, type, and metadata.
  318        */
  319       public PCEnhancer(OpenJPAConfiguration conf, BCClass type,
  320           ClassMetaData meta) {
  321           this(conf, type, meta.getRepository());
  322       }
  323   
  324       /**
  325        * Return the bytecode representation of the persistence-capable class
  326        * being manipulated.
  327        */
  328       public BCClass getPCBytecode() {
  329           return _pc;
  330       }
  331   
  332       /**
  333        * Return the bytecode representation of the managed class being
  334        * manipulated. This is usually the same as {@link #getPCBytecode},
  335        * except when running the enhancer to redefine and subclass
  336        * existing persistent types.
  337        */
  338       public BCClass getManagedTypeBytecode() {
  339           return _managedType;
  340       }
  341   
  342       /**
  343        * Return the metadata for the class being manipulated, or null if not
  344        * a persistent type.
  345        */
  346       public ClassMetaData getMetaData() {
  347           return _meta;
  348       }
  349   
  350       /**
  351        * A boolean indicating whether the enhancer should add a no-args
  352        * constructor if one is not already present in the class. OpenJPA
  353        * requires that a no-arg constructor (whether created by the compiler
  354        * or by the user) be present in a PC.
  355        */
  356       public boolean getAddDefaultConstructor() {
  357           return _defCons;
  358       }
  359   
  360       /**
  361        * A boolean indicating whether the enhancer should add a no-args
  362        * constructor if one is not already present in the class. OpenJPA
  363        * requires that a no-arg constructor (whether created by the compiler
  364        * or by the user) be present in a PC.
  365        */
  366       public void setAddDefaultConstructor(boolean addDefaultConstructor) {
  367           _defCons = addDefaultConstructor;
  368       }
  369   
  370       /**
  371        * Whether the enhancer should mutate its arguments, or just run validation
  372        * and optional subclassing logic on them. Usually used in conjunction with
  373        * <code>setCreateSubclass(true)</code>.
  374        *
  375        * @since 1.0.0
  376        */
  377       public boolean getRedefine() {
  378           return _redefine;
  379       }
  380   
  381       /**
  382        * Whether the enhancer should mutate its arguments, or just run validation
  383        * and optional subclassing logic on them. Usually used in conjunction with
  384        * <code>setCreateSubclass(true)</code>.
  385        *
  386        * @since 1.0.0
  387        */
  388       public void setRedefine(boolean redefine) {
  389           _redefine = redefine;
  390       }
  391   
  392       /**
  393        * Whether the type that this instance is enhancing has already been
  394        * redefined.
  395        *
  396        * @since 1.0.0
  397        */
  398       public boolean isAlreadyRedefined() {
  399           return _isAlreadyRedefined;
  400       }
  401   
  402       /**
  403        * Whether the type that this instance is enhancing has already been
  404        * subclassed in this instance's environment classloader.
  405        *
  406        * @since 1.0.0
  407        */
  408       public boolean isAlreadySubclassed() {
  409           return _isAlreadySubclassed;
  410       }
  411   
  412       /**
  413        * Whether the enhancer should make its arguments persistence-capable,
  414        * or generate a persistence-capable subclass.
  415        *
  416        * @since 1.0.0
  417        */
  418       public boolean getCreateSubclass() {
  419           return _subclass;
  420       }
  421   
  422       /**
  423        * Whether the enhancer should make its arguments persistence-capable,
  424        * or generate a persistence-capable subclass.
  425        *
  426        * @since 1.0.0
  427        */
  428       public void setCreateSubclass(boolean subclass) {
  429           _subclass = subclass;
  430       }
  431   
  432       /**
  433        * Whether to fail if the persistent type uses property access and
  434        * bytecode analysis shows that it may be violating OpenJPA's property
  435        * access restrictions.
  436        */
  437       public boolean getEnforcePropertyRestrictions() {
  438           return _fail;
  439       }
  440   
  441       /**
  442        * Whether to fail if the persistent type uses property access and
  443        * bytecode analysis shows that it may be violating OpenJPA's property
  444        * access restrictions.
  445        */
  446       public void setEnforcePropertyRestrictions(boolean fail) {
  447           _fail = fail;
  448       }
  449   
  450       /**
  451        * The base build directory to generate code to. The proper package
  452        * structure will be created beneath this directory. Defaults to
  453        * overwriting the existing class file if null.
  454        */
  455       public File getDirectory() {
  456           return _dir;
  457       }
  458   
  459       /**
  460        * The base build directory to generate code to. The proper package
  461        * structure will be creaed beneath this directory. Defaults to
  462        * overwriting the existing class file if null.
  463        */
  464       public void setDirectory(File dir) {
  465           _dir = dir;
  466       }
  467   
  468       /**
  469        * Return the current {@link BytecodeWriter} to write to or null if none.
  470        */
  471       public BytecodeWriter getBytecodeWriter() {
  472           return _writer;
  473       }
  474   
  475       /**
  476        * Set the {@link BytecodeWriter} to write the bytecode to or null if none.
  477        */
  478       public void setBytecodeWriter(BytecodeWriter writer) {
  479           _writer = writer;
  480       }
  481   
  482       /**
  483        * Perform bytecode enhancements.
  484        *
  485        * @return <code>ENHANCE_*</code> constant
  486        */
  487       public int run() {
  488           if (_log.isTraceEnabled())
  489               _log.trace(_loc.get("enhance-start", _managedType.getType()));
  490   
  491           try {
  492               // if managed interface, skip
  493               if (_pc.isInterface())
  494                   return ENHANCE_INTERFACE;
  495   
  496               // check if already enhanced
  497               Class[] interfaces = _managedType.getDeclaredInterfaceTypes();
  498               for (int i = 0; i < interfaces.length; i++) {
  499                   if (interfaces[i].getName().equals(PCTYPE.getName())) {
  500                       if (_log.isTraceEnabled())
  501                           _log.trace(_loc.get("pc-type", _managedType.getType()));
  502                       return ENHANCE_NONE;
  503                   }
  504               }
  505   
  506               configureBCs();
  507   
  508               // validate properties before replacing field access so that
  509               // we build up a record of backing fields, etc
  510               if (_meta != null
  511                   && _meta.getAccessType() == ClassMetaData.ACCESS_PROPERTY) {
  512                   validateProperties();
  513                   if (getCreateSubclass())
  514                       addAttributeTranslation();
  515               }
  516               replaceAndValidateFieldAccess();
  517               processViolations();
  518   
  519               if (_meta != null) {
  520                   enhanceClass();
  521                   addFields();
  522                   addStaticInitializer();
  523                   addPCMethods();
  524                   addAccessors();
  525                   addAttachDetachCode();
  526                   addSerializationCode();
  527                   addCloningCode();
  528                   runAuxiliaryEnhancers();
  529                   return ENHANCE_PC;
  530               }
  531   
  532               if (_log.isWarnEnabled())
  533                   _log.warn(_loc.get("pers-aware", _managedType.getType()));
  534               return ENHANCE_AWARE;
  535           } catch (OpenJPAException ke) {
  536               throw ke;
  537           } catch (Exception e) {
  538               throw new GeneralException(_loc.get("enhance-error",
  539                   _managedType.getType().getName(), e.getMessage()), e);
  540           }
  541       }
  542   
  543       private void configureBCs() {
  544           if (!_bcsConfigured) {
  545               if (getRedefine()) {
  546                   if (_managedType.getAttribute(REDEFINED_ATTRIBUTE) == null)
  547                       _managedType.addAttribute(REDEFINED_ATTRIBUTE);
  548                   else
  549                       _isAlreadyRedefined = true;
  550               }
  551   
  552               if (getCreateSubclass()) {
  553                   PCSubclassValidator val = new PCSubclassValidator(
  554                       _meta, _managedType, _log, _fail);
  555                   val.assertCanSubclass();
  556   
  557                   _pc = _managedType.getProject().loadClass(
  558                       toPCSubclassName(_managedType.getType()));
  559                   if (_pc.getSuperclassBC() != _managedType) {
  560                       _pc.setSuperclass(_managedType);
  561                       _pc.setAbstract(_managedType.isAbstract());
  562                       _pc.declareInterface(DynamicPersistenceCapable.class);
  563                   } else {
  564                       _isAlreadySubclassed = true;
  565                   }
  566               }
  567   
  568               _bcsConfigured = true;
  569           }
  570       }
  571   
  572       /**
  573        * Write the generated bytecode.
  574        */
  575       public void record()
  576           throws IOException {
  577           if (_managedType != _pc && getRedefine())
  578               record(_managedType);
  579           record(_pc);
  580           if (_oids != null)
  581               for (Iterator itr = _oids.iterator(); itr.hasNext();)
  582                   record((BCClass) itr.next());
  583       }
  584   
  585       /**
  586        * Write the given class.
  587        */
  588       private void record(BCClass bc)
  589           throws IOException {
  590           if (_writer != null)
  591               _writer.write(bc);
  592           else if (_dir == null)
  593               bc.write();
  594           else {
  595               File dir = Files.getPackageFile(_dir, bc.getPackageName(), true);
  596               bc.write(new File(dir, bc.getClassName() + ".class"));
  597           }
  598       }
  599   
  600       /**
  601        * Validate that the methods in this property-access instance are
  602        * written correctly. This method also gathers information on each
  603        * property's backing field.
  604        */
  605       private void validateProperties() {
  606           FieldMetaData[] fmds;
  607           if (getCreateSubclass())
  608               fmds = _meta.getFields();
  609           else
  610               fmds = _meta.getDeclaredFields();
  611           Method meth;
  612           BCMethod getter, setter;
  613           BCField returned, assigned = null;
  614           for (int i = 0; i < fmds.length; i++) {
  615               if (!(fmds[i].getBackingMember() instanceof Method)) {
  616                   addViolation("property-bad-member",
  617                       new Object[]{ fmds[i], fmds[i].getBackingMember() },
  618                       true);
  619                   continue;
  620               }
  621   
  622               meth = (Method) fmds[i].getBackingMember();
  623               // ##### this will fail if we override and don't call super.
  624               BCClass declaringType = _managedType.getProject()
  625                   .loadClass(fmds[i].getDeclaringType());
  626               getter = declaringType.getDeclaredMethod(meth.getName(),
  627                   meth.getParameterTypes());
  628               if (getter == null) {
  629                   addViolation("property-no-getter", new Object[]{ fmds[i] },
  630                       true);
  631                   continue;
  632               }
  633               returned = getReturnedField(getter);
  634               if (returned != null)
  635                   registerBackingFieldInfo(fmds[i], getter, returned);
  636   
  637               setter = declaringType.getDeclaredMethod(getSetterName(fmds[i]),
  638                   new Class[]{ fmds[i].getDeclaredType() });
  639               if (setter == null) {
  640                   if (returned == null) {
  641                       addViolation("property-no-setter",
  642                           new Object[]{ fmds[i] }, true);
  643                       continue;
  644                   } else if (!getRedefine()) {
  645                       // create synthetic setter
  646                       setter = _managedType.declareMethod(getSetterName(fmds[i]),
  647                           void.class, new Class[]{ fmds[i].getDeclaredType() });
  648                       setter.makePrivate();
  649                       Code code = setter.getCode(true);
  650                       code.aload().setThis();
  651                       code.xload().setParam(0);
  652                       code.putfield().setField(returned);
  653                       code.vreturn();
  654                       code.calculateMaxStack();
  655                       code.calculateMaxLocals();
  656                   }
  657               }
  658   
  659               if (setter != null)
  660                   assigned = getAssignedField(setter);
  661   
  662               if (assigned != null) {
  663                   if (setter != null)
  664                       registerBackingFieldInfo(fmds[i], setter, assigned);
  665   
  666                   if (assigned != returned)
  667                       addViolation("property-setter-getter-mismatch", new Object[]
  668                           { fmds[i], assigned.getName(), (returned == null) 
  669                           ? null : returned.getName() }, false);
  670               }
  671           }
  672       }
  673   
  674       private void registerBackingFieldInfo(FieldMetaData fmd, BCMethod method,
  675           BCField field) {
  676           if (_backingFields == null)
  677               _backingFields = new HashMap();
  678           _backingFields.put(method.getName(), field.getName());
  679   
  680           if (_attrsToFields == null)
  681               _attrsToFields = new HashMap();
  682           _attrsToFields.put(fmd.getName(), field.getName());
  683   
  684           if (_fieldsToAttrs == null)
  685               _fieldsToAttrs = new HashMap();
  686           _fieldsToAttrs.put(field.getName(), fmd.getName());
  687       }
  688   
  689       private void addAttributeTranslation() {
  690           _pc.declareInterface(AttributeTranslator.class);
  691           BCMethod method = _pc.declareMethod(PRE + "AttributeIndexToFieldName",
  692               String.class, new Class[] { int.class });
  693           method.makePublic();
  694           Code code = method.getCode(true);
  695   
  696           FieldMetaData[] fmds = _meta.getFields();
  697   
  698           // switch (val)
  699           code.iload().setParam(0);
  700           TableSwitchInstruction tabins = code.tableswitch();
  701           tabins.setLow(0);
  702           tabins.setHigh(fmds.length - 1);
  703   
  704           // case i:
  705           //     return <_attrsToFields.get(fmds[i].getName())>
  706           for (int i = 0; i < fmds.length; i++) {
  707               tabins.addTarget(code.constant().setValue(
  708                   _attrsToFields.get(fmds[i].getName())));
  709               code.areturn();
  710           }
  711   
  712           // default: throw new IllegalArgumentException ()
  713           tabins.setDefaultTarget(throwException
  714               (code, IllegalArgumentException.class));
  715   
  716           code.calculateMaxLocals();
  717           code.calculateMaxStack();
  718       }
  719   
  720       /**
  721        * Return the name of the setter method for the given field.
  722        */
  723       private static String getSetterName(FieldMetaData fmd) {
  724           return "set" + StringUtils.capitalize(fmd.getName());
  725       }
  726   
  727       /**
  728        * Return the field returned by the given method, or null if none.
  729        * Package-protected and static for testing.
  730        */
  731       static BCField getReturnedField(BCMethod meth) {
  732           return findField(meth, ((Code) AccessController.doPrivileged(
  733               J2DoPrivHelper.newCodeAction())).xreturn()
  734               .setType(meth.getReturnType()), false);
  735       }
  736   
  737       /**
  738        * Return the field assigned in the given method, or null if none.
  739        * Package-protected and static for testing.
  740        */
  741       static BCField getAssignedField(BCMethod meth) {
  742           return findField(meth, ((Code) AccessController.doPrivileged(
  743               J2DoPrivHelper.newCodeAction())).putfield(), true);
  744       }
  745   
  746       /**
  747        * Return the field returned / assigned by <code>meth</code>. Returns
  748        * null if non-fields (methods, literals, parameters, variables) are
  749        * returned, or if non-parameters are assigned to fields.
  750        */
  751       private static BCField findField(BCMethod meth, Instruction template,
  752           boolean findAccessed) {
  753           // ignore any static methods. OpenJPA only currently supports
  754           // non-static setters and getters
  755           if (meth.isStatic())
  756               return null;
  757   
  758           Code code = meth.getCode(false);
  759           if (code == null)
  760               return null;
  761           code.beforeFirst();
  762   
  763           BCField field = null, cur;
  764           Instruction templateIns, prevIns, earlierIns;
  765           while (code.searchForward(template)) {
  766               int backupCount = 3;
  767               templateIns = code.previous();
  768               if (!code.hasPrevious())
  769                   return null;
  770               prevIns = code.previous();
  771   
  772               if (prevIns instanceof ClassInstruction
  773                   && code.hasPrevious()) {
  774                   prevIns = code.previous();
  775                   backupCount++;
  776               }
  777   
  778               if (!code.hasPrevious())
  779                   return null;
  780               earlierIns = code.previous();
  781   
  782               // if the opcode two before the template was an aload_0, check
  783               // against the middle instruction based on what type of find
  784               // we're doing
  785               if (!(earlierIns instanceof LoadInstruction)
  786                   || !((LoadInstruction) earlierIns).isThis())
  787                   return null;
  788   
  789               // if the middle instruction was a getfield, then it's the
  790               // field that's being accessed
  791               if (!findAccessed && prevIns instanceof GetFieldInstruction) {
  792                   final FieldInstruction fPrevIns = (FieldInstruction) prevIns;
  793                   cur = (BCField) AccessController.doPrivileged(
  794                       J2DoPrivHelper.getFieldInstructionFieldAction(fPrevIns));
  795                   // if the middle instruction was an xload_1, then the
  796                   // matched instruction is the field that's being set.
  797               } else if (findAccessed && prevIns instanceof LoadInstruction
  798                   && ((LoadInstruction) prevIns).getParam() == 0) {
  799                   final FieldInstruction fTemplateIns =
  800                       (FieldInstruction) templateIns;
  801                   cur = (BCField) AccessController.doPrivileged(J2DoPrivHelper
  802                       .getFieldInstructionFieldAction(fTemplateIns));
  803               } else
  804                   return null;
  805   
  806               if (field != null && cur != field)
  807                   return null;
  808               field = cur;
  809   
  810               // ready for next search iteration
  811               while (backupCount > 0) {
  812                   code.next();
  813                   backupCount--;
  814               }
  815           }
  816           return field;
  817       }
  818   
  819       /**
  820        * Record a violation of the property access restrictions.
  821        */
  822       private void addViolation(String key, Object[] args, boolean fatal) {
  823           if (_violations == null)
  824               _violations = new HashSet();
  825           _violations.add(_loc.get(key, args));
  826           _fail |= fatal;
  827       }
  828   
  829       /**
  830        * Log / throw recorded property access violations.
  831        */
  832       private void processViolations() {
  833           if (_violations == null)
  834               return;
  835   
  836           String sep = J2DoPrivHelper.getLineSeparator();
  837           StringBuffer buf = new StringBuffer();
  838           for (Iterator itr = _violations.iterator(); itr.hasNext();) {
  839               buf.append(itr.next());
  840               if (itr.hasNext())
  841                   buf.append(sep);
  842           }
  843           Message msg = _loc.get("property-violations", buf);
  844   
  845           if (_fail)
  846               throw new UserException(msg);
  847           if (_log.isWarnEnabled())
  848               _log.warn(msg);
  849       }
  850   
  851       /**
  852        * Replaced all direct access to managed fields with the appropriate
  853        * pcGet/pcSet method. Note that this includes access to fields
  854        * owned by PersistenceCapable classes other than this one.
  855        */
  856       private void replaceAndValidateFieldAccess() throws NoSuchMethodException {
  857           // create template putfield/getfield instructions to search for
  858           Code template = (Code) AccessController.doPrivileged(
  859               J2DoPrivHelper.newCodeAction());
  860           Instruction put = template.putfield();
  861           Instruction get = template.getfield();
  862           Instruction stat = template.invokestatic();
  863   
  864           // look through all methods; this is done before any methods are added
  865           // so we don't need to worry about excluding synthetic methods.
  866           BCMethod[] methods = _managedType.getDeclaredMethods();
  867           Code code;
  868           for (int i = 0; i < methods.length; i++) {
  869               code = methods[i].getCode(false);
  870   
  871               // don't modify the methods specified by the auxiliary enhancers
  872               if (code != null && !skipEnhance(methods[i])) {
  873                   replaceAndValidateFieldAccess(code, get, true, stat);
  874                   replaceAndValidateFieldAccess(code, put, false, stat);
  875               }
  876           }
  877       }
  878   
  879       /**
  880        * Replaces all instructions matching the given template in the given
  881        * code block with calls to the appropriate generated getter/setter.
  882        *
  883        * @param code the code block to modify; the code iterator will
  884        * be placed before the first instruction on method start,
  885        * and will be after the last instruction on method completion
  886        * @param ins the template instruction to search for; either a
  887        * getfield or putfield instruction
  888        * @param get boolean indicating if this is a get instruction
  889        * @param stat template invokestatic instruction to replace with
  890        */
  891       private void replaceAndValidateFieldAccess(Code code, Instruction ins,
  892           boolean get, Instruction stat) throws NoSuchMethodException {
  893           code.beforeFirst();
  894   
  895           FieldInstruction fi;
  896           MethodInstruction mi;
  897           ClassMetaData owner;
  898           String name, typeName, methodName;
  899           while (code.searchForward(ins)) {
  900               // back up to the matched instruction
  901               fi = (FieldInstruction) code.previous();
  902               name = fi.getFieldName();
  903               typeName = fi.getFieldTypeName();
  904               owner = getPersistenceCapableOwner(name, fi.getFieldDeclarerType());
  905   
  906               if (owner != null
  907                   && owner.getAccessType() == ClassMetaData.ACCESS_PROPERTY) {
  908                   // if we're directly accessing a field in another class
  909                   // hierarchy that uses property access, something is wrong
  910                   if (owner != _meta && owner.getDeclaredField(name) != null &&
  911                       _meta != null && !owner.getDescribedType()
  912                           .isAssignableFrom(_meta.getDescribedType()))
  913                       throw new UserException(_loc.get("property-field-access",
  914                           new Object[]{ _meta, owner, name,
  915                               code.getMethod().getName() }));
  916   
  917                   // if we're directly accessing a property-backing field outside
  918                   // the property in our own class, notify user
  919                   if (isBackingFieldOfAnotherProperty(name, code))
  920                       addViolation("property-field-access", new Object[]{ _meta,
  921                           owner, name, code.getMethod().getName() }, false);
  922               }
  923   
  924               if (owner == null ||
  925                   owner.getDeclaredField(fromBackingFieldName(name)) == null) {
  926                   // not persistent field?
  927                   code.next();
  928                   continue;
  929               } else if (!getRedefine() && !getCreateSubclass()
  930                   && owner.getAccessType() == ClassMetaData.ACCESS_FIELD) {
  931                   // replace the instruction with a call to the generated access
  932                   // method
  933                   mi = (MethodInstruction) code.set(stat);
  934   
  935                   // invoke the proper access method, whether getter or setter
  936                   String prefix = (get) ? PRE + "Get" : PRE + "Set";
  937                   methodName = prefix + name;
  938                   if (get) {
  939                       mi.setMethod(getType(owner).getName(),
  940                           methodName, typeName, new String[]
  941                           { getType(owner).getName() });
  942                   } else {
  943                       mi.setMethod(getType(owner).getName(),
  944                           methodName, "void", new String[]
  945                           { getType(owner).getName(), typeName });
  946                   }
  947                   code.next();
  948               } else if (getRedefine()) {
  949                   name = fromBackingFieldName(name);
  950                   if (get) {
  951                       addNotifyAccess(code, owner.getField(name));
  952                       code.next();
  953                   } else {
  954                       // insert the set operations after the field mutation, but
  955                       // first load the old value for use in the
  956                       // StateManager.settingXXX method.
  957                       loadManagedInstance(code, false);
  958                       final FieldInstruction fFi = fi;
  959                       code.getfield().setField(
  960                           (BCField) AccessController.doPrivileged(J2DoPrivHelper
  961                               .getFieldInstructionFieldAction(fFi)));
  962                       int val = code.getNextLocalsIndex();
  963                       code.xstore().setLocal(val).setType(fi.getFieldType());
  964   
  965                       // move past the putfield
  966                       code.next();
  967                       addNotifyMutation(code, owner.getField(name), val, -1);
  968                   }
  969               } else {
  970                   code.next();
  971               }
  972               code.calculateMaxLocals();
  973               code.calculateMaxStack();
  974           }
  975       }
  976   
  977       private void addNotifyAccess(Code code, FieldMetaData fmd) {
  978           // PCHelper.accessingField(this, <absolute-index>);
  979           code.aload().setThis();
  980           code.constant().setValue(fmd.getIndex());
  981           code.invokestatic().setMethod(RedefinitionHelper.class,
  982               "accessingField", void.class,
  983               new Class[] { Object.class, int.class });
  984       }
  985   
  986       /**
  987        * This must be called after setting the value in the object.
  988        * 
  989        * @param code
  990        * @param val the position in the local variable table where the
  991        * old value is stored
  992        * @param param the parameter position containing the new value, or
  993        * -1 if the new value is unavailable and should therefore be looked
  994        * up.
  995        * @throws NoSuchMethodException
  996        */
  997       private void addNotifyMutation(Code code, FieldMetaData fmd, int val,
  998           int param)
  999           throws NoSuchMethodException {
 1000           // PCHelper.settingField(this, <absolute-index>, old, new);
 1001           code.aload().setThis();
 1002           code.constant().setValue(fmd.getIndex());
 1003           Class type = fmd.getDeclaredType();
 1004           // we only have special signatures for primitives and Strings
 1005           if (!type.isPrimitive() && type != String.class)
 1006               type = Object.class;
 1007           code.xload().setLocal(val).setType(type);
 1008           if (param == -1) {
 1009               loadManagedInstance(code, false);
 1010               addGetManagedValueCode(code, fmd);
 1011           } else {
 1012               code.xload().setParam(param).setType(type);
 1013           }
 1014           code.invokestatic().setMethod(RedefinitionHelper.class, "settingField",
 1015               void.class, new Class[] {
 1016                   Object.class, int.class, type, type
 1017           });
 1018       }
 1019   
 1020       /**
 1021        * Return true if the given instruction accesses a field that is a backing
 1022        * field of another property in this property-access class.
 1023        */
 1024       private boolean isBackingFieldOfAnotherProperty(String name, Code code) {
 1025           String methName = code.getMethod().getName();
 1026           return !"<init>".equals(methName)
 1027               && _backingFields != null
 1028               && !name.equals(_backingFields.get(methName))
 1029               && _backingFields.containsValue(name);
 1030       }
 1031   
 1032       /**
 1033        * Helper method to return the declaring PersistenceCapable class of
 1034        * the given field.
 1035        *
 1036        * @param fieldName the name of the field
 1037        * @param owner the nominal owner of the field
 1038        * @return the metadata for the PersistenceCapable type that
 1039        * declares the field (and therefore has the static method), or null if none
 1040        */
 1041       private ClassMetaData getPersistenceCapableOwner(String fieldName,
 1042           Class owner) {
 1043           // find the actual ancestor class that declares the field, then
 1044           // check if the class is persistent, and if the field is managed
 1045           Field f = Reflection.findField(owner, fieldName, false);
 1046           if (f == null)
 1047               return null;
 1048   
 1049           // managed interface
 1050           if (_meta != null && _meta.getDescribedType().isInterface())
 1051               return _meta;
 1052   
 1053           return _repos.getMetaData(f.getDeclaringClass(), null, false);
 1054       }
 1055   
 1056       /**
 1057        * Adds all synthetic methods to the bytecode by delegating to
 1058        * the various addXXXMethods () functions in this class. Includes
 1059        * all static field access methods.
 1060        * Note that the 'stock' methods like <code>pcIsTransactional</code>,
 1061        * <code>pcFetchObjectId</code>, etc are defined only in the
 1062        * least-derived PersistenceCapable type.
 1063        */
 1064       private void addPCMethods()
 1065           throws NoSuchMethodException {
 1066           addClearFieldsMethod();
 1067           addNewInstanceMethod(true);
 1068           addNewInstanceMethod(false);
 1069           addManagedFieldCountMethod();
 1070           addReplaceFieldsMethods();
 1071           addProvideFieldsMethods();
 1072           addCopyFieldsMethod();
 1073   
 1074           if (_meta.getPCSuperclass() == null || getCreateSubclass()) {
 1075               addStockMethods();
 1076               addGetVersionMethod();
 1077               addReplaceStateManagerMethod();
 1078   
 1079               if (_meta.getIdentityType() != ClassMetaData.ID_APPLICATION)
 1080                   addNoOpApplicationIdentityMethods();
 1081           }
 1082   
 1083           // add the app id methods to each subclass rather
 1084           // than just the superclass, since it is possible to have
 1085           // a subclass with an app id hierarchy that matches the
 1086           // persistent class inheritance hierarchy
 1087           if (_meta.getIdentityType() == ClassMetaData.ID_APPLICATION
 1088               && (_meta.getPCSuperclass() == null || getCreateSubclass() ||
 1089                   _meta.getObjectIdType() !=
 1090                       _meta.getPCSuperclassMetaData().getObjectIdType())) {
 1091               addCopyKeyFieldsToObjectIdMethod(true);
 1092               addCopyKeyFieldsToObjectIdMethod(false);
 1093               addCopyKeyFieldsFromObjectIdMethod(true);
 1094               addCopyKeyFieldsFromObjectIdMethod(false);
 1095               addNewObjectIdInstanceMethod(true);
 1096               addNewObjectIdInstanceMethod(false);
 1097           }
 1098       }
 1099   
 1100       /**
 1101        * Add a method to clear all persistent fields; we'll call this from
 1102        * the new instance method to ensure that unloaded fields have
 1103        * default values.
 1104        */
 1105       private void addClearFieldsMethod()
 1106           throws NoSuchMethodException {
 1107           // protected void pcClearFields ()
 1108           BCMethod method = _pc.declareMethod(PRE + "ClearFields", void.class,
 1109               null);
 1110           method.makeProtected();
 1111           Code code = method.getCode(true);
 1112   
 1113           // super.pcClearFields ()
 1114           if (_meta.getPCSuperclass() != null && !getCreateSubclass()) {
 1115               code.aload().setThis();
 1116               code.invokespecial().setMethod(getType(_meta.
 1117                   getPCSuperclassMetaData()), PRE + "ClearFields", void.class,
 1118                   null);
 1119           }
 1120   
 1121           FieldMetaData[] fmds = _meta.getDeclaredFields();
 1122           for (int i = 0; i < fmds.length; i++) {
 1123               if (fmds[i].getManagement() != FieldMetaData.MANAGE_PERSISTENT)
 1124                   continue;
 1125   
 1126               loadManagedInstance(code, false);
 1127               switch (fmds[i].getDeclaredTypeCode()) {
 1128                   case JavaTypes.BOOLEAN:
 1129                   case JavaTypes.BYTE:
 1130                   case JavaTypes.CHAR:
 1131                   case JavaTypes.INT:
 1132                   case JavaTypes.SHORT:
 1133                       code.constant().setValue(0);
 1134                       break;
 1135                   case JavaTypes.DOUBLE:
 1136                       code.constant().setValue(0D);
 1137                       break;
 1138                   case JavaTypes.FLOAT:
 1139                       code.constant().setValue(0F);
 1140                       break;
 1141                   case JavaTypes.LONG:
 1142                       code.constant().setValue(0L);
 1143                       break;
 1144                   default:
 1145                       code.constant().setNull();
 1146                       break;
 1147               }
 1148   
 1149               addSetManagedValueCode(code, fmds[i]);
 1150           }
 1151   
 1152           code.vreturn();
 1153           code.calculateMaxStack();
 1154           code.calculateMaxLocals();
 1155       }
 1156   
 1157       /**
 1158        * Adds the <code>pcNewInstance</code> method to the bytecode.
 1159        * These methods are used by the impl helper to create new
 1160        * managed instances efficiently without reflection.
 1161        *
 1162        * @param oid set to true to mimic the method version that takes
 1163        * an oid value as well as a state manager
 1164        */
 1165       private void addNewInstanceMethod(boolean oid) {
 1166           // public PersistenceCapable pcNewInstance (...)
 1167           Class[] args =
 1168               (oid) ? new Class[]{ SMTYPE, Object.class, boolean.class }
 1169                   : new Class[]{ SMTYPE, boolean.class };
 1170           BCMethod method = _pc.declareMethod(PRE + "NewInstance", PCTYPE, args);
 1171           Code code = method.getCode(true);
 1172   
 1173           // if the type is abstract, throw a UserException
 1174           if (_pc.isAbstract()) {
 1175               throwException(code, USEREXCEP);
 1176               code.vreturn();
 1177   
 1178               code.calculateMaxStack();
 1179               code.calculateMaxLocals();
 1180               return;
 1181           }
 1182   
 1183           // XXX pc = new XXX ();
 1184           code.anew().setType(_pc);
 1185           code.dup();
 1186           code.invokespecial().setMethod("<init>", void.class, null);
 1187           int inst = code.getNextLocalsIndex();
 1188           code.astore().setLocal(inst);
 1189   
 1190           // if (clear)
 1191           //   pc.pcClearFields ();
 1192           code.iload().setParam((oid) ? 2 : 1);
 1193           JumpInstruction noclear = code.ifeq();
 1194           code.aload().setLocal(inst);
 1195           code.invokevirtual().setMethod(PRE + "ClearFields", void.class, null);
 1196   
 1197           // pc.pcStateManager = sm;
 1198           noclear.setTarget(code.aload().setLocal(inst));
 1199           code.aload().setParam(0);
 1200           code.putfield().setField(SM, SMTYPE);
 1201   
 1202           // copy key fields from oid
 1203           if (oid) {
 1204               code.aload().setLocal(inst);
 1205               code.aload().setParam(1);
 1206               code.invokevirtual().setMethod(PRE + "CopyKeyFieldsFromObjectId",
 1207                   void.class, new Class[]{ Object.class });
 1208           }
 1209   
 1210           // return pc;
 1211           code.aload().setLocal(inst);
 1212           code.areturn();
 1213   
 1214           code.calculateMaxStack();
 1215           code.calculateMaxLocals();
 1216       }
 1217   
 1218       /**
 1219        * Adds the <code>protected static int pcGetManagedFieldCount ()</code>
 1220        * method to the bytecode, returning the inherited field count added
 1221        * to the number of managed fields in the current PersistenceCapable class.
 1222        */
 1223       private void addManagedFieldCountMethod() {
 1224           // protected static int pcGetManagedFieldCount ()
 1225           BCMethod method = _pc.declareMethod(PRE + "GetManagedFieldCount",
 1226               int.class, null);
 1227           method.setStatic(true);
 1228           method.makeProtected();
 1229           Code code = method.getCode(true);
 1230   
 1231           // return <fields> + pcInheritedFieldCount
 1232           // awhite: the above should work, but I'm seeing a messed up situation
 1233           // all of a sudden where when a subclass calls this method, it somehow
 1234           // happens before <clinit> is ever invoked, and so our
 1235           // pcInheritedFieldCount field isn't initialized!  so instead,
 1236           // return <fields> + <superclass>.pcGetManagedFieldCount ()
 1237           code.constant().setValue(_meta.getDeclaredFields().length);
 1238           if (_meta.getPCSuperclass() != null) {
 1239               Class superClass = getType(_meta.getPCSuperclassMetaData());
 1240               String superName = getCreateSubclass() ?
 1241                   PCEnhancer.toPCSubclassName(superClass) :
 1242                   superClass.getName();
 1243               code.invokestatic().setMethod(superName,
 1244                   PRE + "GetManagedFieldCount", int.class.getName(), null);
 1245               code.iadd();
 1246           }
 1247           code.ireturn();
 1248           code.calculateMaxStack();
 1249       }
 1250   
 1251       /**
 1252        * Adds the {@link PersistenceCapable#pcProvideField} and
 1253        * {@link PersistenceCapable#pcProvideFields} methods to the bytecode.
 1254        */
 1255       private void addProvideFieldsMethods()
 1256           throws NoSuchMethodException {
 1257           // public void pcProvideField (int fieldNumber)
 1258           BCMethod method = _pc.declareMethod(PRE + "ProvideField", void.class,
 1259               new Class[]{ int.class });
 1260           Code code = method.getCode(true);
 1261   
 1262           // adds everything through the switch ()
 1263           int relLocal = beginSwitchMethod(PRE + "ProvideField", code);
 1264   
 1265           // if no fields in this inst, just throw exception
 1266           FieldMetaData[] fmds = getCreateSubclass() ? _meta.getFields()
 1267               : _meta.getDeclaredFields();
 1268           if (fmds.length == 0)
 1269               throwException(code, IllegalArgumentException.class);
 1270           else {
 1271               // switch (val)
 1272               code.iload().setLocal(relLocal);
 1273               TableSwitchInstruction tabins = code.tableswitch();
 1274               tabins.setLow(0);
 1275               tabins.setHigh(fmds.length - 1);
 1276   
 1277               // <field> = pcStateManager.provided<type>Field
 1278               //     (this, fieldNumber);
 1279               for (int i = 0; i < fmds.length; i++) {
 1280                   tabins.addTarget(loadManagedInstance(code, false));
 1281                   code.getfield().setField(SM, SMTYPE);
 1282                   loadManagedInstance(code, false);
 1283                   code.iload().setParam(0);
 1284                   loadManagedInstance(code, false);
 1285                   addGetManagedValueCode(code, fmds[i]);
 1286                   code.invokeinterface().setMethod(getStateManagerMethod
 1287                       (fmds[i].getDeclaredType(), "provided", false, false));
 1288                   code.vreturn();
 1289               }
 1290   
 1291               // default: throw new IllegalArgumentException ()
 1292               tabins.setDefaultTarget(throwException
 1293                   (code, IllegalArgumentException.class));
 1294           }
 1295   
 1296           code.calculateMaxStack();
 1297           code.calculateMaxLocals();
 1298   
 1299           addMultipleFieldsMethodVersion(method);
 1300       }
 1301   
 1302       /**
 1303        * Adds the {@link PersistenceCapable#pcReplaceField} and
 1304        * {@link PersistenceCapable#pcReplaceFields} methods to the bytecode.
 1305        */
 1306       private void addReplaceFieldsMethods()
 1307           throws NoSuchMethodException {
 1308           // public void pcReplaceField (int fieldNumber)
 1309           BCMethod method = _pc.declareMethod(PRE + "ReplaceField", void.class,
 1310               new Class[]{ int.class });
 1311           Code code = method.getCode(true);
 1312   
 1313           // adds everything through the switch ()
 1314           int relLocal = beginSwitchMethod(PRE + "ReplaceField", code);
 1315   
 1316           // if no fields in this inst, just throw exception
 1317           FieldMetaData[] fmds = getCreateSubclass() ? _meta.getFields()
 1318               : _meta.getDeclaredFields();
 1319           if (fmds.length == 0)
 1320               throwException(code, IllegalArgumentException.class);
 1321           else {
 1322               // switch (val)
 1323               code.iload().setLocal(relLocal);
 1324               TableSwitchInstruction tabins = code.tableswitch();
 1325               tabins.setLow(0);
 1326               tabins.setHigh(fmds.length - 1);
 1327   
 1328               // <field> = pcStateManager.replace<type>Field
 1329               //  (this, fieldNumber);
 1330               for (int i = 0; i < fmds.length; i++) {
 1331                   // for the addSetManagedValueCode call below.
 1332                   tabins.addTarget(loadManagedInstance(code, false));
 1333   
 1334                   loadManagedInstance(code, false);
 1335                   code.getfield().setField(SM, SMTYPE);
 1336                   loadManagedInstance(code, false);
 1337                   code.iload().setParam(0);
 1338                   code.invokeinterface().setMethod(getStateManagerMethod
 1339                       (fmds[i].getDeclaredType(), "replace", true, false));
 1340                   if (!fmds[i].getDeclaredType().isPrimitive())
 1341                       code.checkcast().setType(fmds[i].getDeclaredType());
 1342   
 1343                   addSetManagedValueCode(code, fmds[i]);
 1344                   code.vreturn();
 1345               }
 1346   
 1347               // default: throw new IllegalArgumentException ()
 1348               tabins.setDefaultTarget(throwException
 1349                   (code, IllegalArgumentException.class));
 1350           }
 1351   
 1352           code.calculateMaxStack();
 1353           code.calculateMaxLocals();
 1354   
 1355           addMultipleFieldsMethodVersion(method);
 1356       }
 1357   
 1358       /**
 1359        * Adds the {@link PersistenceCapable#pcCopyFields} method to the bytecode.
 1360        */
 1361       private void addCopyFieldsMethod()
 1362           throws NoSuchMethodException {
 1363           // public void pcCopyField (Object pc, int field)
 1364           BCMethod method = _pc.declareMethod(PRE + "CopyField",
 1365               void.class.getName(),
 1366               new String[]{ _managedType.getName(), int.class.getName() });
 1367           method.makeProtected();
 1368           Code code = method.getCode(true);
 1369   
 1370           // adds everything through the switch ()
 1371           int relLocal = beginSwitchMethod(PRE + "CopyField", code);
 1372   
 1373           // if no fields in this inst, just throw exception
 1374           FieldMetaData[] fmds = getCreateSubclass() ? _meta.getFields()
 1375               : _meta.getDeclaredFields();
 1376           if (fmds.length == 0)
 1377               throwException(code, IllegalArgumentException.class);
 1378           else {
 1379               // switch (val)
 1380               code.iload().setLocal(relLocal);
 1381               TableSwitchInstruction tabins = code.tableswitch();
 1382               tabins.setLow(0);
 1383               tabins.setHigh(fmds.length - 1);
 1384   
 1385               for (int i = 0; i < fmds.length; i++) {
 1386                   // <field> = other.<field>;
 1387                   // or set<field> (other.get<field>);
 1388                   tabins.addTarget(loadManagedInstance(code, false));
 1389                   code.aload().setParam(0);
 1390                   addGetManagedValueCode(code, fmds[i], false);
 1391                   addSetManagedValueCode(code, fmds[i]);
 1392   
 1393                   // break;
 1394                   code.vreturn();
 1395               }
 1396   
 1397               // default: throw new IllegalArgumentException ()
 1398               tabins.setDefaultTarget(throwException
 1399                   (code, IllegalArgumentException.class));
 1400           }
 1401   
 1402           code.calculateMaxStack();
 1403           code.calculateMaxLocals();
 1404   
 1405           addMultipleFieldsMethodVersion(method);
 1406       }
 1407   
 1408       /**
 1409        * Helper method to add the code common to the beginning of both the
 1410        * pcReplaceField method and the pcProvideField method. This includes
 1411        * calculating the relative field number of the desired field and calling
 1412        * the superclass if necessary.
 1413        *
 1414        * @return the index in which the local variable holding the relative
 1415        * field number is stored
 1416        */
 1417       private int beginSwitchMethod(String name, Code code) {
 1418           boolean copy = (PRE + "CopyField").equals(name);
 1419           int fieldNumber = (copy) ? 1 : 0;
 1420   
 1421           int relLocal = code.getNextLocalsIndex();
 1422           if (getCreateSubclass()) {
 1423               code.iload().setParam(fieldNumber);
 1424               code.istore().setLocal(relLocal);
 1425               return relLocal;
 1426           }
 1427   
 1428           // int rel = fieldNumber - pcInheritedFieldCount
 1429           code.iload().setParam(fieldNumber);
 1430           code.getstatic().setField(INHERIT, int.class);
 1431           code.isub();
 1432           code.istore().setLocal(relLocal);
 1433           code.iload().setLocal(relLocal);
 1434   
 1435           // super: if (rel < 0) super.pcReplaceField (fieldNumber); return;
 1436           // no super: if (rel < 0) throw new IllegalArgumentException ();
 1437           JumpInstruction ifins = code.ifge();
 1438           if (_meta.getPCSuperclass() != null) {
 1439               loadManagedInstance(code, false);
 1440               String[] args;
 1441               if (copy) {
 1442                   args = new String[]{ getType(_meta.getPCSuperclassMetaData()).
 1443                       getName(), int.class.getName() };
 1444                   code.aload().setParam(0);
 1445               } else
 1446                   args = new String[]{ int.class.getName() };
 1447               code.iload().setParam(fieldNumber);
 1448               code.invokespecial().setMethod(getType(_meta.
 1449                   getPCSuperclassMetaData()).getName(), name, 
 1450                   void.class.getName(), args);
 1451               code.vreturn();
 1452           } else
 1453               throwException(code, IllegalArgumentException.class);
 1454   
 1455           ifins.setTarget(code.nop());
 1456           return relLocal;
 1457       }
 1458   
 1459       /**
 1460        * This helper method, given the pcReplaceField or pcProvideField
 1461        * method, adds the bytecode for the corresponding 'plural' version
 1462        * of the method -- the version that takes an int[] of fields to
 1463        * to access rather than a single field. The multiple fields version
 1464        * simply loops through the provided indexes and delegates to the
 1465        * singular version for each one.
 1466        */
 1467       private void addMultipleFieldsMethodVersion(BCMethod single) {
 1468           boolean copy = (PRE + "CopyField").equals(single.getName());
 1469   
 1470           // public void <method>s (int[] fields)
 1471           Class[] args = (copy) ? new Class[]{ Object.class, int[].class }
 1472               : new Class[]{ int[].class };
 1473           BCMethod method = _pc.declareMethod(single.getName() + "s",
 1474               void.class, args);
 1475           Code code = method.getCode(true);
 1476   
 1477           int fieldNumbers = 0;
 1478           int inst = 0;
 1479           if (copy) {
 1480               fieldNumbers = 1;
 1481   
 1482               if (getCreateSubclass()) {
 1483                   // get the managed instance into the local variable table
 1484                   code.aload().setParam(0);
 1485                   code.invokestatic().setMethod(ImplHelper.class,
 1486                       "getManagedInstance", Object.class,
 1487                       new Class[] { Object.class });
 1488                   code.checkcast().setType(_managedType);
 1489                   inst = code.getNextLocalsIndex();
 1490                   code.astore().setLocal(inst);
 1491   
 1492                   // there might be a difference between the classes of 'this'
 1493                   // vs 'other' in this context; use the PC methods to get the SM
 1494                   code.aload().setParam(0);
 1495                   code.aload().setThis();
 1496                   code.getfield().setField(SM, SMTYPE);
 1497                   code.invokestatic().setMethod(ImplHelper.class,
 1498                       "toPersistenceCapable", PersistenceCapable.class,
 1499                       new Class[] { Object.class, Object.class });
 1500                   code.invokeinterface().setMethod(PersistenceCapable.class,
 1501                       "pcGetStateManager", StateManager.class, null);
 1502               } else {
 1503                   // XXX other = (XXX) pc;
 1504                   code.aload().setParam(0);
 1505                   code.checkcast().setType(_pc);
 1506                   inst = code.getNextLocalsIndex();
 1507                   code.astore().setLocal(inst);
 1508   
 1509                   // access the other's sm field directly
 1510                   code.aload().setLocal(inst);
 1511                   code.getfield().setField(SM, SMTYPE);
 1512               }
 1513   
 1514               // if (other.pcStateManager != pcStateManager)
 1515               //	throw new IllegalArgumentException
 1516   
 1517               loadManagedInstance(code, false);
 1518               code.getfield().setField(SM, SMTYPE);
 1519               JumpInstruction ifins = code.ifacmpeq();
 1520               throwException(code, IllegalArgumentException.class);
 1521               ifins.setTarget(code.nop());
 1522   
 1523               // if (pcStateManager == null)
 1524               //  throw new IllegalStateException
 1525               loadManagedInstance(code, false);
 1526               code.getfield().setField(SM, SMTYPE);
 1527               ifins = code.ifnonnull();
 1528               throwException(code, IllegalStateException.class);
 1529               ifins.setTarget(code.nop());
 1530           }
 1531   
 1532           // for (int i = 0;
 1533           code.constant().setValue(0);
 1534           int idx = code.getNextLocalsIndex();
 1535           code.istore().setLocal(idx);
 1536           JumpInstruction testins = code.go2();
 1537   
 1538           // <method> (fields[i]);
 1539           Instruction bodyins = loadManagedInstance(code, false);
 1540           if (copy)
 1541               code.aload().setLocal(inst);
 1542           code.aload().setParam(fieldNumbers);
 1543           code.iload().setLocal(idx);
 1544           code.iaload();
 1545           code.invokevirtual().setMethod(single);
 1546   
 1547           // i++;
 1548           code.iinc().setIncrement(1).setLocal(idx);
 1549   
 1550           // i < fields.length
 1551           testins.setTarget(code.iload().setLocal(idx));
 1552           code.aload().setParam(fieldNumbers);
 1553           code.arraylength();
 1554           code.ificmplt().setTarget(bodyins);
 1555           code.vreturn();
 1556   
 1557           code.calculateMaxStack();
 1558           code.calculateMaxLocals();
 1559       }
 1560   
 1561       /**
 1562        * Adds the 'stock' methods to the bytecode; these include methods
 1563        * like {@link PersistenceCapable#pcFetchObjectId}
 1564        * and {@link PersistenceCapable#pcIsTransactional}.
 1565        */
 1566       private void addStockMethods()
 1567           throws NoSuchMethodException {
 1568           try {
 1569               // pcGetGenericContext
 1570               translateFromStateManagerMethod(
 1571                   (Method) AccessController.doPrivileged(
 1572                       J2DoPrivHelper.getDeclaredMethodAction(
 1573                           SMTYPE, "get" + CONTEXTNAME, (Class[]) null)), false);
 1574       
 1575               // pcFetchObjectId
 1576               translateFromStateManagerMethod(
 1577                   (Method) AccessController.doPrivileged(
 1578                       J2DoPrivHelper.getDeclaredMethodAction(
 1579                           SMTYPE, "fetchObjectId", (Class[]) null)), false);
 1580       
 1581               // pcIsDeleted
 1582               translateFromStateManagerMethod(
 1583                   (Method) AccessController.doPrivileged(
 1584                       J2DoPrivHelper.getDeclaredMethodAction(
 1585                           SMTYPE, "isDeleted", (Class[]) null)), false);
 1586       
 1587               // pcIsDirty
 1588               translateFromStateManagerMethod(
 1589                   (Method) AccessController.doPrivileged(
 1590                       J2DoPrivHelper.getDeclaredMethodAction(
 1591                           SMTYPE, "isDirty", (Class[]) null)), true);
 1592       
 1593               // pcIsNew
 1594               translateFromStateManagerMethod(
 1595                   (Method) AccessController.doPrivileged(
 1596                       J2DoPrivHelper.getDeclaredMethodAction(
 1597                           SMTYPE, "isNew", (Class[]) null)), false);
 1598       
 1599               // pcIsPersistent
 1600               translateFromStateManagerMethod(
 1601                   (Method) AccessController.doPrivileged(
 1602                       J2DoPrivHelper.getDeclaredMethodAction(
 1603                           SMTYPE, "isPersistent", (Class[]) null)), false);
 1604       
 1605               // pcIsTransactional
 1606               translateFromStateManagerMethod(
 1607                   (Method) AccessController.doPrivileged(
 1608                       J2DoPrivHelper.getDeclaredMethodAction(
 1609                           SMTYPE, "isTransactional", (Class[]) null)), false);
 1610       
 1611               // pcSerializing
 1612               translateFromStateManagerMethod(
 1613                   (Method) AccessController.doPrivileged(
 1614                       J2DoPrivHelper.getDeclaredMethodAction(
 1615                           SMTYPE, "serializing", (Class[]) null)), false);
 1616       
 1617               // pcDirty
 1618               translateFromStateManagerMethod(
 1619                   (Method) AccessController.doPrivileged(
 1620                       J2DoPrivHelper.getDeclaredMethodAction(
 1621                           SMTYPE, "dirty", new Class[]{ String.class })), false);
 1622       
 1623               // pcGetStateManager
 1624               BCMethod meth = _pc.declareMethod(PRE + "GetStateManager",
 1625                   StateManager.class, null);
 1626               Code code = meth.getCode(true);
 1627               loadManagedInstance(code, false);
 1628               code.getfield().setField(SM, StateManager.class);
 1629               code.areturn();
 1630               code.calculateMaxStack();
 1631               code.calculateMaxLocals();
 1632           } catch (PrivilegedActionException pae) {
 1633                throw (NoSuchMethodException) pae.getException();
 1634           }
 1635       }
 1636   
 1637       /**
 1638        * Helper method to add a stock method to the bytecode. Each
 1639        * stock method simply delegates to a corresponding StateManager method.
 1640        * Given the StateManager method, then, this function translates it into
 1641        * the wrapper method that should be added to the bytecode.
 1642        */
 1643       private void translateFromStateManagerMethod(Method m,
 1644           boolean isDirtyCheckMethod) {
 1645           // form the name of the method by prepending 'pc' to the sm method
 1646           String name = PRE + StringUtils.capitalize(m.getName());
 1647           Class[] params = m.getParameterTypes();
 1648           Class returnType = m.getReturnType();
 1649   
 1650           // add the method to the pc
 1651           BCMethod method = _pc.declareMethod(name, returnType, params);
 1652           Code code = method.getCode(true);
 1653   
 1654           // if (pcStateManager == null) return <default>;
 1655           loadManagedInstance(code, false);
 1656           code.getfield().setField(SM, SMTYPE);
 1657           JumpInstruction ifins = code.ifnonnull();
 1658           if (returnType.equals(boolean.class))
 1659               code.constant().setValue(false);
 1660           else if (!returnType.equals(void.class))
 1661               code.constant().setNull();
 1662           code.xreturn().setType(returnType);
 1663   
 1664           // if this is the dirty-check method and we're subclassing but not
 1665           // redefining, hook into PCHelper to do the dirty check
 1666           if (isDirtyCheckMethod && !getRedefine()) {
 1667               // RedefinitionHelper.dirtyCheck(sm);
 1668               ifins.setTarget(loadManagedInstance(code, false));
 1669               code.getfield().setField(SM, SMTYPE);
 1670               code.dup(); // for the return statement below
 1671               code.invokestatic().setMethod(RedefinitionHelper.class, 
 1672                   "dirtyCheck", void.class, new Class[] { SMTYPE });
 1673           } else {
 1674               ifins.setTarget(loadManagedInstance(code, false));
 1675               code.getfield().setField(SM, SMTYPE);
 1676           }
 1677           
 1678           // return pcStateManager.<method> (<args>);
 1679           // managed instance loaded above in if-else block
 1680           for (int i = 0; i < params.length; i++)
 1681               code.xload().setParam(i);
 1682           code.invokeinterface().setMethod(m);
 1683           code.xreturn().setType(returnType);
 1684   
 1685           code.calculateMaxStack();
 1686           code.calculateMaxLocals();
 1687       }
 1688   
 1689       /**
 1690        * Adds the {@link PersistenceCapable#pcGetVersion} method to the bytecode.
 1691        */
 1692       private void addGetVersionMethod()
 1693           throws NoSuchMethodException {
 1694           BCMethod method = _pc.declareMethod(PRE + "GetVersion", Object.class,
 1695               null);
 1696           Code code = method.getCode(true);
 1697   
 1698           // if (pcStateManager == null)
 1699           loadManagedInstance(code, false);
 1700           code.getfield().setField(SM, SMTYPE);
 1701           JumpInstruction ifins = code.ifnonnull();
 1702           FieldMetaData versionField = _meta.getVersionField();
 1703   
 1704           if (versionField == null)
 1705               code.constant().setNull(); // return null;
 1706           else {
 1707               // return <versionField>;
 1708               Class wrapper = toPrimitiveWrapper(versionField);
 1709               if (wrapper != versionField.getDeclaredType()) {
 1710                   code.anew().setType(wrapper);
 1711                   code.dup();
 1712               }
 1713               loadManagedInstance(code, false);
 1714               addGetManagedValueCode(code, versionField);
 1715               if (wrapper != versionField.getDeclaredType())
 1716                   code.invokespecial().setMethod(wrapper, "<init>", void.class,
 1717                       new Class[]{ versionField.getDeclaredType() });
 1718           }
 1719           code.areturn();
 1720   
 1721           // return pcStateManager.getVersion ();
 1722           ifins.setTarget(loadManagedInstance(code, false));
 1723           code.getfield().setField(SM, SMTYPE);
 1724           code.invokeinterface().setMethod(SMTYPE, "getVersion", Object.class,
 1725               null);
 1726           code.areturn();
 1727   
 1728           code.calculateMaxStack();
 1729           code.calculateMaxLocals();
 1730       }
 1731   
 1732       /**
 1733        * Return the version field type as a primitive wrapper, or null if
 1734        * the version field is not primitive.
 1735        */
 1736       private Class toPrimitiveWrapper(FieldMetaData fmd) {
 1737           switch (fmd.getDeclaredTypeCode()) {
 1738               case JavaTypes.BOOLEAN:
 1739                   return Boolean.class;
 1740               case JavaTypes.BYTE:
 1741                   return Byte.class;
 1742               case JavaTypes.CHAR:
 1743                   return Character.class;
 1744               case JavaTypes.DOUBLE:
 1745                   return Double.class;
 1746               case JavaTypes.FLOAT:
 1747                   return Float.class;
 1748               case JavaTypes.INT:
 1749                   return Integer.class;
 1750               case JavaTypes.LONG:
 1751                   return Long.class;
 1752               case JavaTypes.SHORT:
 1753                   return Short.class;
 1754           }
 1755           return fmd.getDeclaredType();
 1756       }
 1757   
 1758       /**
 1759        * Adds the {@link PersistenceCapable#pcReplaceStateManager}
 1760        * method to the bytecode.
 1761        */
 1762       private void addReplaceStateManagerMethod() {
 1763           // public void pcReplaceStateManager (StateManager sm)
 1764           BCMethod method = _pc.declareMethod(PRE + "ReplaceStateManager",
 1765               void.class, new Class[]{ SMTYPE });
 1766           method.setSynchronized(true);
 1767           method.getExceptions(true).addException(SecurityException.class);
 1768           Code code = method.getCode(true);
 1769   
 1770           // if (pcStateManager != null)
 1771           //	pcStateManager = pcStateManager.replaceStateManager(sm);
 1772           loadManagedInstance(code, false);
 1773           code.getfield().setField(SM, SMTYPE);
 1774           JumpInstruction ifins = code.ifnull();
 1775           loadManagedInstance(code, false);
 1776           loadManagedInstance(code, false);
 1777           code.getfield().setField(SM, SMTYPE);
 1778           code.aload().setParam(0);
 1779           code.invokeinterface().setMethod(SMTYPE, "replaceStateManager",
 1780               SMTYPE, new Class[]{ SMTYPE });
 1781           code.putfield().setField(SM, SMTYPE);
 1782           code.vreturn();
 1783   
 1784           // SecurityManager sec = System.getSecurityManager ();
 1785           // if (sec != null)
 1786           //		sec.checkPermission (Permission.SET_STATE_MANAGER);
 1787           ifins.setTarget(code.invokestatic().setMethod(System.class,
 1788               "getSecurityManager", SecurityManager.class, null));
 1789   
 1790           // pcStateManager = sm;
 1791           ifins.setTarget(loadManagedInstance(code, false));
 1792           code.aload().setParam(0);
 1793           code.putfield().setField(SM, SMTYPE);
 1794           code.vreturn();
 1795   
 1796           code.calculateMaxStack();
 1797           code.calculateMaxLocals();
 1798       }
 1799   
 1800       /**
 1801        * Creates the PersistenceCapable methods dealing with application
 1802        * identity and gives them no-op implementations.
 1803        */
 1804       private void addNoOpApplicationIdentityMethods() {
 1805           // public void pcCopyKeyFieldsToObjectId (ObjectIdFieldSupplier fs,
 1806           // 	Object oid)
 1807           BCMethod method = _pc.declareMethod(PRE + "CopyKeyFieldsToObjectId",
 1808               void.class, new Class[]{ OIDFSTYPE, Object.class });
 1809           Code code = method.getCode(true);
 1810           code.vreturn();
 1811           code.calculateMaxLocals();
 1812   
 1813           // public void pcCopyKeyFieldsToObjectId (Object oid)
 1814           method = _pc.declareMethod(PRE + "CopyKeyFieldsToObjectId",
 1815               void.class, new Class[]{ Object.class });
 1816           code = method.getCode(true);
 1817           code.vreturn();
 1818           code.calculateMaxLocals();
 1819   
 1820           // public void pcCopyKeyFieldsFromObjectId (ObjectIdFieldConsumer fc,
 1821           //	Object oid)
 1822           method = _pc.declareMethod(PRE + "CopyKeyFieldsFromObjectId",
 1823               void.class, new Class[]{ OIDFCTYPE, Object.class });
 1824           code = method.getCode(true);
 1825           code.vreturn();
 1826           code.calculateMaxLocals();
 1827   
 1828           // public void pcCopyKeyFieldsFromObjectId (Object oid)
 1829           method = _pc.declareMethod(PRE + "CopyKeyFieldsFromObjectId",
 1830               void.class, new Class[]{ Object.class });
 1831           code = method.getCode(true);
 1832           code.vreturn();
 1833           code.calculateMaxLocals();
 1834   
 1835           // public Object pcNewObjectIdInstance ()
 1836           method = _pc.declareMethod(PRE + "NewObjectIdInstance",
 1837               Object.class, null);
 1838           code = method.getCode(true);
 1839           code.constant().setNull();
 1840           code.areturn();
 1841           code.calculateMaxStack();
 1842           code.calculateMaxLocals();
 1843   
 1844           // public Object pcNewObjectIdInstance (Object obj)
 1845           method = _pc.declareMethod(PRE + "NewObjectIdInstance",
 1846               Object.class, new Class[]{ Object.class });
 1847           code = method.getCode(true);
 1848           code.constant().setNull();
 1849           code.areturn();
 1850           code.calculateMaxStack();
 1851           code.calculateMaxLocals();
 1852       }
 1853   
 1854       /**
 1855        * Adds the <code>pcCopyKeyFieldsToObjectId</code> methods
 1856        * to classes using application identity.
 1857        */
 1858       private void addCopyKeyFieldsToObjectIdMethod(boolean fieldManager)
 1859           throws NoSuchMethodException {
 1860           // public void pcCopyKeyFieldsToObjectId (ObjectIdFieldSupplier fs,
 1861           //	Object oid)
 1862           String[] args = (fieldManager) ?
 1863               new String[]{ OIDFSTYPE.getName(), Object.class.getName() }
 1864               : new String[]{ Object.class.getName() };
 1865           BCMethod method = _pc.declareMethod(PRE + "CopyKeyFieldsToObjectId",
 1866               void.class.getName(), args);
 1867           Code code = method.getCode(true);
 1868   
 1869           // single field identity always throws exception
 1870           if (_meta.isOpenJPAIdentity()) {
 1871               throwException(code, INTERNEXCEP);
 1872               code.vreturn();
 1873   
 1874               code.calculateMaxStack();
 1875               code.calculateMaxLocals();
 1876               return;
 1877           }
 1878   
 1879           // call superclass method
 1880           if (_meta.getPCSuperclass() != null && !getCreateSubclass()) {
 1881               loadManagedInstance(code, false);
 1882               for (int i = 0; i < args.length; i++)
 1883                   code.aload().setParam(i);
 1884               code.invokespecial().setMethod(getType(_meta.
 1885                   getPCSuperclassMetaData()).getName(),
 1886                   PRE + "CopyKeyFieldsToObjectId", void.class.getName(), args);
 1887           }
 1888   
 1889           // Object id = oid;
 1890           if (fieldManager)
 1891               code.aload().setParam(1);
 1892           else
 1893               code.aload().setParam(0);
 1894   
 1895           if (_meta.isObjectIdTypeShared()) {
 1896               // oid = ((ObjectId) id).getId ();
 1897               code.checkcast().setType(ObjectId.class);
 1898               code.invokevirtual().setMethod(ObjectId.class, "getId",
 1899                   Object.class, null);
 1900           }
 1901   
 1902           // <oid type> id = (<oid type>) oid;
 1903           int id = code.getNextLocalsIndex();
 1904           Class oidType = _meta.getObjectIdType();
 1905           code.checkcast().setType(oidType);
 1906           code.astore().setLocal(id);
 1907   
 1908           // int inherited = pcInheritedFieldCount;
 1909           int inherited = 0;
 1910           if (fieldManager) {
 1911               code.getstatic().setField(INHERIT, int.class);
 1912               inherited = code.getNextLocalsIndex();
 1913               code.istore().setLocal(inherited);
 1914           }
 1915   
 1916           // id.<field> = fs.fetch<type>Field (<index>); or...
 1917           // id.<field> = pc.<field>;
 1918           FieldMetaData[] fmds = getCreateSubclass() ? _meta.getFields()
 1919               : _meta.getDeclaredFields();
 1920           Class type;
 1921           String name;
 1922           Field field;
 1923           Method setter;
 1924           boolean reflect;
 1925           for (int i = 0; i < fmds.length; i++) {
 1926               if (!fmds[i].isPrimaryKey())
 1927                   continue;
 1928               code.aload().setLocal(id);
 1929   
 1930               name = fmds[i].getName();
 1931               type = fmds[i].getObjectIdFieldType();
 1932               if (_meta.getAccessType() == ClassMetaData.ACCESS_FIELD) {
 1933                   setter = null;
 1934                   field = Reflection.findField(oidType, name, true);
 1935                   reflect = !Modifier.isPublic(field.getModifiers());
 1936                   if (reflect) {
 1937                       code.classconstant().setClass(oidType);
 1938                       code.constant().setValue(name);
 1939                       code.constant().setValue(true);
 1940                       code.invokestatic().setMethod(Reflection.class, 
 1941                           "findField", Field.class, new Class[] { Class.class,
 1942                           String.class, boolean.class });
 1943                   }
 1944               } else {
 1945                   field = null;
 1946                   setter = Reflection.findSetter(oidType, name, type, true);
 1947                   reflect = !Modifier.isPublic(setter.getModifiers());
 1948                   if (reflect) {
 1949                       code.classconstant().setClass(oidType);
 1950                       code.constant().setValue(name);
 1951                       code.classconstant().setClass(type);
 1952                       code.constant().setValue(true);
 1953                       code.invokestatic().setMethod(Reflection.class, 
 1954                           "findSetter", Method.class, new Class[] { Class.class,
 1955                           String.class, Class.class, boolean.class });
 1956                   }
 1957               }
 1958   
 1959               if (fieldManager) {
 1960                   code.aload().setParam(0);
 1961                   code.constant().setValue(i);
 1962                   code.iload().setLocal(inherited);
 1963                   code.iadd();
 1964                   code.invokeinterface().setMethod
 1965                       (getFieldSupplierMethod(type));
 1966   
 1967                   // if the type of this field meta data is
 1968                   // non-primitive and non-string, be sure to cast
 1969                   // to the appropriate type.
 1970                   if (!reflect && !type.isPrimitive()
 1971                       && !type.getName().equals(String.class.getName()))
 1972                       code.checkcast().setType(type);
 1973               } else {
 1974                   loadManagedInstance(code, false);
 1975                   addGetManagedValueCode(code, fmds[i]);
 1976   
 1977                   // get id/pk from pc instance
 1978                   if (fmds[i].getDeclaredTypeCode() == JavaTypes.PC)
 1979                       addExtractObjectIdFieldValueCode(code, fmds[i]);
 1980               }
 1981   
 1982               if (reflect && field != null) {
 1983                   code.invokestatic().setMethod(Reflection.class, "set", 
 1984                       void.class, new Class[] { Object.class, Field.class,
 1985                       (type.isPrimitive()) ? type : Object.class });
 1986               } else if (reflect) { 
 1987                   code.invokestatic().setMethod(Reflection.class, "set", 
 1988                       void.class, new Class[] { Object.class, Method.class,
 1989                       (type.isPrimitive()) ? type : Object.class });
 1990               } else if (field != null)
 1991                   code.putfield().setField(field);
 1992               else
 1993                   code.invokevirtual().setMethod(setter);
 1994           }
 1995           code.vreturn();
 1996   
 1997           code.calculateMaxStack();
 1998           code.calculateMaxLocals();
 1999       }
 2000   
 2001       /**
 2002        * Add code to extract the id of the given primary key relation field for
 2003        * setting into an objectid instance.
 2004        */
 2005       private void addExtractObjectIdFieldValueCode(Code code, FieldMetaData pk) {
 2006           // if (val != null) 
 2007           //  val = ((PersistenceCapable) val).pcFetchObjectId();
 2008           int pc = code.getNextLocalsIndex();
 2009           code.astore().setLocal(pc);
 2010           code.aload().setLocal(pc);
 2011           JumpInstruction ifnull1 = code.ifnull();
 2012           code.aload().setLocal(pc);
 2013           code.checkcast().setType(PersistenceCapable.class); 
 2014           code.invokeinterface().setMethod(PersistenceCapable.class,
 2015               PRE + "FetchObjectId", Object.class, null);
 2016           int oid = code.getNextLocalsIndex();
 2017           code.astore().setLocal(oid);
 2018           code.aload().setLocal(oid);
 2019           JumpInstruction ifnull2 = code.ifnull(); 
 2020   
 2021           // for datastore / single-field identity:
 2022           // if (val != null)
 2023           //   val = ((OpenJPAId) val).getId();
 2024           ClassMetaData pkmeta = pk.getDeclaredTypeMetaData();
 2025           int pkcode = pk.getObjectIdFieldTypeCode();
 2026           Class pktype = pk.getObjectIdFieldType();
 2027           if (pkmeta.getIdentityType() == ClassMetaData.ID_DATASTORE 
 2028               && pkcode == JavaTypes.LONG) {
 2029               code.aload().setLocal(oid);
 2030               code.checkcast().setType(Id.class);
 2031               code.invokevirtual().setMethod(Id.class, "getId", 
 2032                   long.class, null);
 2033           } else if (pkmeta.getIdentityType() == ClassMetaData.ID_DATASTORE) {
 2034               code.aload().setLocal(oid);
 2035           } else if (pkmeta.isOpenJPAIdentity()) {
 2036               switch (pkcode) {
 2037                   case JavaTypes.BYTE_OBJ:
 2038                       code.anew().setType(Byte.class);
 2039                       code.dup();
 2040                       // no break
 2041                   case JavaTypes.BYTE:
 2042                       code.aload().setLocal(oid);
 2043                       code.checkcast().setType(ByteId.class);
 2044                       code.invokevirtual().setMethod(ByteId.class, "getId",
 2045                           byte.class, null);
 2046                       if (pkcode == JavaTypes.BYTE_OBJ)
 2047                           code.invokespecial().setMethod(Byte.class, "<init>",
 2048                               void.class, new Class[] {byte.class});
 2049                       break;
 2050                   case JavaTypes.CHAR_OBJ:
 2051                       code.anew().setType(Character.class);
 2052                       code.dup();
 2053                       // no break
 2054                   case JavaTypes.CHAR:
 2055                       code.aload().setLocal(oid);
 2056                       code.checkcast().setType(CharId.class);
 2057                       code.invokevirtual().setMethod(CharId.class, "getId",
 2058                           char.class, null);
 2059                       if (pkcode == JavaTypes.CHAR_OBJ)
 2060                           code.invokespecial().setMethod(Character.class, 
 2061                               "<init>", void.class, new Class[] {char.class});
 2062                       break;
 2063                   case JavaTypes.DOUBLE_OBJ:
 2064                       code.anew().setType(Double.class);
 2065                       code.dup();
 2066                       // no break
 2067                   case JavaTypes.DOUBLE:
 2068                       code.aload().setLocal(oid);
 2069                       code.checkcast().setType(DoubleId.class);
 2070                       code.invokevirtual().setMethod(DoubleId.class, "getId",
 2071                           double.class, null);
 2072                       if (pkcode == JavaTypes.DOUBLE_OBJ)
 2073                           code.invokespecial().setMethod(Double.class, "<init>", 
 2074                               void.class, new Class[]{double.class});
 2075                       break;
 2076                   case JavaTypes.FLOAT_OBJ:
 2077                       code.anew().setType(Float.class);
 2078                       code.dup();
 2079                       // no break
 2080                   case JavaTypes.FLOAT:
 2081                       code.aload().setLocal(oid);
 2082                       code.checkcast().setType(FloatId.class);
 2083                       code.invokevirtual().setMethod(FloatId.class, "getId",
 2084                           float.class, null);
 2085                       if (pkcode == JavaTypes.FLOAT_OBJ)
 2086                           code.invokespecial().setMethod(Float.class, "<init>", 
 2087                               void.class, new Class[]{float.class});
 2088                       break;
 2089                   case JavaTypes.INT_OBJ:
 2090                       code.anew().setType(Integer.class);
 2091                       code.dup();
 2092                       // no break
 2093                   case JavaTypes.INT:
 2094                       code.aload().setLocal(oid);
 2095                       code.checkcast().setType(IntId.class);
 2096                       code.invokevirtual().setMethod(IntId.class, "getId",
 2097                           int.class, null);
 2098                       if (pkcode == JavaTypes.INT_OBJ)
 2099                           code.invokespecial().setMethod(Integer.class, "<init>",
 2100                               void.class, new Class[] {int.class});
 2101                       break;
 2102                   case JavaTypes.LONG_OBJ:
 2103                       code.anew().setType(Long.class);
 2104                       code.dup();
 2105                       // no break
 2106                   case JavaTypes.LONG:
 2107                       code.aload().setLocal(oid);
 2108                       code.checkcast().setType(LongId.class);
 2109                       code.invokevirtual().setMethod(LongId.class, "getId",
 2110                           long.class, null);
 2111                       if (pkcode == JavaTypes.LONG_OBJ)
 2112                           code.invokespecial().setMethod(Long.class, "<init>",
 2113                               void.class, new Class[] {long.class});
 2114                       break;
 2115                   case JavaTypes.SHORT_OBJ:
 2116                       code.anew().setType(Short.class);
 2117                       code.dup();
 2118                       // no break
 2119                   case JavaTypes.SHORT:
 2120                       code.aload().setLocal(oid);
 2121                       code.checkcast().setType(ShortId.class);
 2122                       code.invokevirtual().setMethod(ShortId.class, "getId",
 2123                           short.class, null);
 2124                       if (pkcode == JavaTypes.SHORT_OBJ)
 2125                           code.invokespecial().setMethod(Short.class, "<init>", 
 2126                               void.class, new Class[]{short.class});
 2127                       break;
 2128                   case JavaTypes.DATE:
 2129                       code.aload().setLocal(oid);
 2130                       code.checkcast().setType(DateId.class);
 2131                       code.invokevirtual().setMethod(DateId.class, "getId",
 2132                           Date.class, null);
 2133                       break;
 2134                   case JavaTypes.STRING:
 2135                       code.aload().setLocal(oid);
 2136                       code.checkcast().setType(StringId.class);
 2137                       code.invokevirtual().setMethod(StringId.class, "getId",
 2138                           String.class, null);
 2139                       break;
 2140                   case JavaTypes.BIGDECIMAL:
 2141                       code.aload().setLocal(oid);
 2142                       code.checkcast().setType(BigDecimalId.class);
 2143                       code.invokevirtual().setMethod(BigDecimalId.class, "getId",
 2144                           BigDecimalId.class, null);
 2145                       break;
 2146                   case JavaTypes.BIGINTEGER:
 2147                       code.aload().setLocal(oid);
 2148                       code.checkcast().setType(BigIntegerId.class);
 2149                       code.invokevirtual().setMethod(BigIntegerId.class, "getId",
 2150                           BigIntegerId.class, null);
 2151                       break;
 2152                   default:
 2153                       code.aload().setLocal(oid);
 2154                       code.checkcast().setType(ObjectId.class);
 2155                       code.invokevirtual().setMethod(ObjectId.class, "getId",
 2156                           Object.class, null);
 2157               }
 2158           } else if (pkmeta.getObjectIdType() != null) {
 2159               code.aload().setLocal(oid);
 2160               code.checkcast().setType(pktype);
 2161           } else
 2162               code.aload().setLocal(oid);
 2163           JumpInstruction go2 = code.go2();
 2164   
 2165           // if (val == null)
 2166           //   val = <default>;
 2167           Instruction def;
 2168           switch (pkcode) {
 2169               case JavaTypes.BOOLEAN:
 2170                   def = code.constant().setValue(false);
 2171                   break;
 2172               case JavaTypes.BYTE:
 2173                   def = code.constant().setValue((byte) 0);
 2174                   break;
 2175               case JavaTypes.CHAR:
 2176                   def = code.constant().setValue((char) 0);
 2177                   break;
 2178               case JavaTypes.DOUBLE:
 2179                   def = code.constant().setValue(0D);
 2180                   break;
 2181               case JavaTypes.FLOAT:
 2182                   def = code.constant().setValue(0F);
 2183                   break;
 2184               case JavaTypes.INT:
 2185                   def = code.constant().setValue(0);
 2186                   break;
 2187               case JavaTypes.LONG:
 2188                   def = code.constant().setValue(0L);
 2189                   break;
 2190               case JavaTypes.SHORT:
 2191                   def = code.constant().setValue((short) 0);
 2192                   break;
 2193               default:
 2194                   def = code.constant().setNull();
 2195           }
 2196           ifnull1.setTarget(def);
 2197           ifnull2.setTarget(def);
 2198           go2.setTarget(code.nop());
 2199       }
 2200   
 2201       /**
 2202        * Adds the <code>pcCopyKeyFieldsFromObjectId</code> methods
 2203        * to classes using application identity.
 2204        */
 2205       private void addCopyKeyFieldsFromObjectIdMethod(boolean fieldManager)
 2206           throws NoSuchMethodException {
 2207           // public void pcCopyKeyFieldsFromObjectId (ObjectIdFieldConsumer fc,
 2208           //	Object oid)
 2209           String[] args = (fieldManager) 
 2210               ?  new String[]{ OIDFCTYPE.getName(), Object.class.getName() }
 2211               : new String[]{ Object.class.getName() };
 2212           BCMethod method = _pc.declareMethod(PRE + "CopyKeyFieldsFromObjectId",
 2213               void.class.getName(), args);
 2214           Code code = method.getCode(true);
 2215   
 2216           // call superclass method
 2217           if (_meta.getPCSuperclass() != null && !getCreateSubclass()) {
 2218               loadManagedInstance(code, false);
 2219               for (int i = 0; i < args.length; i++)
 2220                   code.aload().setParam(i);
 2221               code.invokespecial().setMethod(getType(_meta.
 2222                   getPCSuperclassMetaData()).getName(),
 2223                   PRE + "CopyKeyFieldsFromObjectId", void.class.getName(), args);
 2224           }
 2225   
 2226           if (fieldManager)
 2227               code.aload().setParam(1);
 2228           else
 2229               code.aload().setParam(0);
 2230   
 2231           if (!_meta.isOpenJPAIdentity() && _meta.isObjectIdTypeShared()) {
 2232               // oid = ((ObjectId) id).getId ();
 2233               code.checkcast().setType(ObjectId.class);
 2234               code.invokevirtual().setMethod(ObjectId.class, "getId",
 2235                   Object.class, null);
 2236           }
 2237   
 2238           // <oid type> cast = (<oid type>) oid;
 2239           int id = code.getNextLocalsIndex();
 2240           Class oidType = _meta.getObjectIdType();
 2241           code.checkcast().setType(oidType);
 2242           code.astore().setLocal(id);
 2243   
 2244           // fs.store<type>Field (<index>, id.<field>); or...
 2245           // this.<field> = id.<field>
 2246           // or for single field identity: id.getId ()
 2247           FieldMetaData[] fmds = getCreateSubclass() ? _meta.getFields()
 2248               : _meta.getDeclaredFields();
 2249           String name;
 2250           Class type;
 2251           Class unwrapped;
 2252           Field field;
 2253           Method getter;
 2254           for (int i = 0; i < fmds.length; i++) {
 2255               if (!fmds[i].isPrimaryKey())
 2256                   continue;
 2257   
 2258               name = fmds[i].getName();
 2259               type = fmds[i].getObjectIdFieldType();
 2260               if (!fieldManager 
 2261                   && fmds[i].getDeclaredTypeCode() == JavaTypes.PC) {
 2262                   // sm.getPCPrimaryKey(oid, i + pcInheritedFieldCount); 
 2263                   loadManagedInstance(code, false);
 2264                   code.dup(); // leave orig on stack to set value into
 2265                   code.getfield().setField(SM, SMTYPE);
 2266                   code.aload().setLocal(id);
 2267                   code.constant().setValue(i);
 2268                   code.getstatic().setField(INHERIT, int.class);
 2269                   code.iadd();
 2270                   code.invokeinterface().setMethod(StateManager.class, 
 2271                       "getPCPrimaryKey", Object.class, 
 2272                       new Class[] { Object.class, int.class });
 2273                   code.checkcast().setType(fmds[i].getDeclaredType());
 2274               } else { 
 2275                   unwrapped = (fmds[i].getDeclaredTypeCode() == JavaTypes.PC) 
 2276                       ? type : unwrapSingleFieldIdentity(fmds[i]);
 2277                   if (fieldManager) {
 2278                       code.aload().setParam(0);
 2279                       code.constant().setValue(i);
 2280                       code.getstatic().setField(INHERIT, int.class);
 2281                       code.iadd();
 2282                   } else
 2283                       loadManagedInstance(code, false);
 2284   
 2285                   if (unwrapped != type) {
 2286                       code.anew().setType(type);
 2287                       code.dup();
 2288                   }
 2289                   code.aload().setLocal(id);
 2290                   if (_meta.isOpenJPAIdentity()) {
 2291                       if (oidType == ObjectId.class) {
 2292                           code.invokevirtual().setMethod(oidType, "getId",
 2293                               Object.class, null);
 2294                           if (!fieldManager && type != Object.class)
 2295                               code.checkcast().setType(fmds[i].getDeclaredType());
 2296                       } else if (oidType == DateId.class) {
 2297                           code.invokevirtual().setMethod(oidType, "getId",
 2298                               Date.class, null);
 2299                           if (!fieldManager && type != Date.class)
 2300                               code.checkcast().setType(fmds[i].getDeclaredType());
 2301                       } else {
 2302                           code.invokevirtual().setMethod(oidType, "getId", 
 2303                               unwrapped, null);
 2304                           if (unwrapped != type)
 2305                               code.invokespecial().setMethod(type, "<init>",
 2306                                   void.class, new Class[]{ unwrapped });
 2307                       }
 2308                   } else if (_meta.getAccessType() == ClassMetaData.ACCESS_FIELD){
 2309                       field = Reflection.findField(oidType, name, true);
 2310                       if (Modifier.isPublic(field.getModifiers()))
 2311                           code.getfield().setField(field);
 2312                       else {
 2313                           // Reflection.getXXX(oid, Reflection.findField(...));
 2314                           code.classconstant().setClass(oidType);
 2315                           code.constant().setValue(name);
 2316                           code.constant().setValue(true);
 2317                           code.invokestatic().setMethod(Reflection.class,
 2318                               "findField", Field.class, new Class[] { 
 2319                               Class.class, String.class, boolean.class });
 2320                           code.invokestatic().setMethod
 2321                               (getReflectionGetterMethod(type, Field.class));
 2322                           if (!type.isPrimitive() && type != Object.class)
 2323                               code.checkcast().setType(type);
 2324                       }
 2325                   } else {
 2326                       getter = Reflection.findGetter(oidType, name, true);
 2327                       if (Modifier.isPublic(getter.getModifiers()))
 2328                           code.invokevirtual().setMethod(getter);
 2329                       else {
 2330                           // Reflection.getXXX(oid, Reflection.findGetter(...));
 2331                           code.classconstant().setClass(oidType);
 2332                           code.constant().setValue(name);
 2333                           code.constant().setValue(true);
 2334                           code.invokestatic().setMethod(Reflection.class,
 2335                               "findGetter", Method.class, new Class[] {
 2336                               Class.class, String.class, boolean.class });
 2337                           code.invokestatic().setMethod
 2338                               (getReflectionGetterMethod(type, Method.class));
 2339                           if (!type.isPrimitive() && type != Object.class)
 2340                               code.checkcast().setType(type);
 2341                       }
 2342                   }
 2343               }
 2344   
 2345               if (fieldManager)
 2346                   code.invokeinterface().setMethod(getFieldConsumerMethod(type));
 2347               else
 2348                   addSetManagedValueCode(code, fmds[i]);
 2349           }
 2350           code.vreturn();
 2351   
 2352           code.calculateMaxStack();
 2353           code.calculateMaxLocals();
 2354       }
 2355   
 2356       /**
 2357        * Return if the class uses the Class/String constructor
 2358        * instead of just String.
 2359        */
 2360       private Boolean usesClassStringIdConstructor() {
 2361           if (_meta.getIdentityType() != ClassMetaData.ID_APPLICATION)
 2362               return Boolean.FALSE;
 2363   
 2364           if (_meta.isOpenJPAIdentity()) {
 2365               if (_meta.getObjectIdType() == ObjectId.class)
 2366                   return null;
 2367               return Boolean.TRUE;
 2368           }
 2369   
 2370           Class oidType = _meta.getObjectIdType();
 2371           try {
 2372               oidType.getConstructor(new Class[]{ Class.class, String.class });
 2373               return Boolean.TRUE;
 2374           } catch (Throwable t) {
 2375           }
 2376           try {
 2377               oidType.getConstructor(new Class[]{ String.class });
 2378               return Boolean.FALSE;
 2379           } catch (Throwable t) {
 2380           }
 2381           return null;
 2382       }
 2383   
 2384       /**
 2385        * If the given field is a wrapper-type single field identity primary key,
 2386        * return its corresponding primitive class. Else return the field type.
 2387        */
 2388       private Class unwrapSingleFieldIdentity(FieldMetaData fmd) {
 2389           if (!fmd.getDefiningMetaData().isOpenJPAIdentity())
 2390               return fmd.getDeclaredType();
 2391   
 2392           switch (fmd.getDeclaredTypeCode()) {
 2393               case JavaTypes.BYTE_OBJ:
 2394                   return byte.class;
 2395               case JavaTypes.CHAR_OBJ:
 2396                   return char.class;
 2397               case JavaTypes.DOUBLE_OBJ:
 2398                   return double.class;
 2399               case JavaTypes.FLOAT_OBJ:
 2400                   return float.class;
 2401               case JavaTypes.INT_OBJ:
 2402                   return int.class;
 2403               case JavaTypes.SHORT_OBJ:
 2404                   return short.class;
 2405               case JavaTypes.LONG_OBJ:
 2406                   return long.class;
 2407               default:
 2408                   return fmd.getDeclaredType();
 2409           }
 2410       }
 2411   
 2412       /**
 2413        * Return the proper getter method of the {@link Reflection} helper for
 2414        * a field or getter method of the given type.
 2415        */
 2416       private Method getReflectionGetterMethod(Class type, Class argType)
 2417           throws NoSuchMethodException {
 2418           String name = "get";
 2419           if (type.isPrimitive())
 2420               name += StringUtils.capitalize(type.getName());
 2421           return Reflection.class.getMethod(name, new Class[] { Object.class, 
 2422               argType }); 
 2423       }
 2424   
 2425       /**
 2426        * Return the proper fetch method of the ObjectIdFieldSupplier for
 2427        * a field of the given type.
 2428        */
 2429       private Method getFieldSupplierMethod(Class type)
 2430           throws NoSuchMethodException {
 2431           return getMethod(OIDFSTYPE, type, "fetch", true, false, false);
 2432       }
 2433   
 2434       /**
 2435        * Return the proper fetch method of the ObjectIdFieldConsumer for
 2436        * a field of the given type.
 2437        */
 2438       private Method getFieldConsumerMethod(Class type)
 2439           throws NoSuchMethodException {
 2440           return getMethod(OIDFCTYPE, type, "store", false, false, false);
 2441       }
 2442   
 2443       /**
 2444        * Adds the pcNewObjectIdInstance method to classes using
 2445        * application identity.
 2446        */
 2447       private void addNewObjectIdInstanceMethod(boolean obj)
 2448           throws NoSuchMethodException {
 2449           // public Object pcNewObjectIdInstance ()
 2450           Class[] args = (obj) ? new Class[]{ Object.class } : null;
 2451           BCMethod method = _pc.declareMethod(PRE + "NewObjectIdInstance",
 2452               Object.class, args);
 2453           Code code = method.getCode(true);
 2454   
 2455           Boolean usesClsString = usesClassStringIdConstructor();
 2456           Class oidType = _meta.getObjectIdType();
 2457           if (obj && usesClsString == null) {
 2458               // throw new IllegalArgumentException (...);
 2459               String msg = _loc.get("str-cons", oidType,
 2460                   _meta.getDescribedType()).getMessage();
 2461               code.anew().setType(IllegalArgumentException.class);
 2462               code.dup();
 2463               code.constant().setValue(msg);
 2464               code.invokespecial().setMethod(IllegalArgumentException.class,
 2465                   "<init>", void.class, new Class[]{ String.class });
 2466               code.athrow();
 2467               code.vreturn();
 2468   
 2469               code.calculateMaxStack();
 2470               code.calculateMaxLocals();
 2471               return;
 2472           }
 2473   
 2474           if (!_meta.isOpenJPAIdentity() && _meta.isObjectIdTypeShared()) {
 2475               // new ObjectId (cls, oid)
 2476               code.anew().setType(ObjectId.class);
 2477               code.dup();
 2478               code.classconstant().setClass(getType(_meta));
 2479           }
 2480   
 2481           // new <oid class> ();
 2482           code.anew().setType(oidType);
 2483           code.dup();
 2484           if (_meta.isOpenJPAIdentity() || (obj && usesClsString == Boolean.TRUE))
 2485               code.classconstant().setClass(getType(_meta));
 2486           if (obj) {
 2487               code.aload().setParam(0);
 2488               code.checkcast().setType(String.class);
 2489               if (usesClsString == Boolean.TRUE)
 2490                   args = new Class[]{ Class.class, String.class };
 2491               else if (usesClsString == Boolean.FALSE)
 2492                   args = new Class[]{ String.class };
 2493           } else if (_meta.isOpenJPAIdentity()) {
 2494               // new <type>Identity (XXX.class, <pk>);
 2495               loadManagedInstance(code, false);
 2496               FieldMetaData pk = _meta.getPrimaryKeyFields()[0];
 2497               addGetManagedValueCode(code, pk);
 2498               if (pk.getDeclaredTypeCode() == JavaTypes.PC)
 2499                   addExtractObjectIdFieldValueCode(code, pk);
 2500               if (_meta.getObjectIdType() == ObjectId.class)
 2501                   args = new Class[]{ Class.class, Object.class };
 2502               else if (_meta.getObjectIdType() == Date.class)
 2503                   args = new Class[]{ Class.class, Date.class };
 2504               else
 2505                   args = new Class[]{ Class.class, pk.getObjectIdFieldType() };
 2506           }
 2507   
 2508           code.invokespecial().setMethod(oidType, "<init>", void.class, args);
 2509           if (!_meta.isOpenJPAIdentity() && _meta.isObjectIdTypeShared())
 2510               code.invokespecial().setMethod(ObjectId.class, "<init>",
 2511                   void.class, new Class[]{ Class.class, Object.class });
 2512           code.areturn();
 2513   
 2514           code.calculateMaxStack();
 2515           code.calculateMaxLocals();
 2516       }
 2517   
 2518       /**
 2519        * When communicating with the StateManager, many methods are used
 2520        * depending on the class of state being passed. This method,
 2521        * given the type of information being passed and the prefix
 2522        * ('provided', 'replace', etc) of the method to
 2523        * call, returns the StateManager method that should be used.
 2524        *
 2525        * @param type the type of state being passed
 2526        * @param prefix the prefix of the method to call; all methods
 2527        * end in '[state type]Field'; only the prefix varies
 2528        * @param get true if receiving information from the
 2529        * StateManager, false if passing it to the SM
 2530        * @param curValue true if the current state value is passed to
 2531        * the StateManager as an extra argument
 2532        */
 2533       private Method getStateManagerMethod(Class type, String prefix,
 2534           boolean get, boolean curValue)
 2535           throws NoSuchMethodException {
 2536           return getMethod(SMTYPE, type, prefix, get, true, curValue);
 2537       }
 2538   
 2539       /**
 2540        * Return the method of the given owner type matching the given criteria.
 2541        *
 2542        * @param type the type of state being passed
 2543        * @param prefix the prefix of the method to call; all methods
 2544        * end in '[state type]Field'; only the prefix varies
 2545        * @param get true if receiving information from the
 2546        * owner, false if passing it to the owner
 2547        * @param haspc true if the pc is passed as an extra argument
 2548        * @param curValue true if the current state value is passed to
 2549        * the owner as an extra argument
 2550        */
 2551       private Method getMethod(Class owner, Class type, String prefix,
 2552           boolean get, boolean haspc, boolean curValue)
 2553           throws NoSuchMethodException {
 2554           // all methods end in [field type]Field, where the field type
 2555           // can be any of the primitve types (but capitalized), 'String',
 2556           // or 'Object'; figure out what type to use
 2557           String typeName = type.getName();
 2558           if (type.isPrimitive())
 2559               typeName = typeName.substring(0, 1).toUpperCase()
 2560                   + typeName.substring(1);
 2561           else if (type.equals(String.class))
 2562               typeName = "String";
 2563           else {
 2564               typeName = "Object";
 2565               type = Object.class;
 2566           }
 2567   
 2568           // the field index is always passed as an arg; the pc instance and
 2569           // the current value may be passed; if setting the new value is
 2570           // also passed
 2571           List plist = new ArrayList(4);
 2572           if (haspc)
 2573               plist.add(PCTYPE);
 2574           plist.add(int.class);
 2575           if (!get || curValue)
 2576               plist.add(type);
 2577           if (!get && curValue) {
 2578               plist.add(type);
 2579               plist.add(int.class);
 2580           }
 2581   
 2582           // use reflection to return the right method
 2583           String name = prefix + typeName + "Field";
 2584           Class[] params = (Class[]) plist.toArray(new Class[plist.size()]);
 2585           
 2586           try {
 2587               return (Method) AccessController.doPrivileged(
 2588                   J2DoPrivHelper.getDeclaredMethodAction(owner, name, params));
 2589           } catch (PrivilegedActionException pae) {
 2590                throw (NoSuchMethodException) pae.getException();
 2591           }
 2592       }
 2593   
 2594       /**
 2595        * Helper method to add the code necessary to throw the given
 2596        * exception type, sans message.
 2597        */
 2598       private Instruction throwException(Code code, Class type) {
 2599           Instruction ins = code.anew().setType(type);
 2600           code.dup();
 2601           code.invokespecial().setMethod(type, "<init>", void.class, null);
 2602           code.athrow();
 2603           return ins;
 2604       }
 2605   
 2606       /**
 2607        * Adds the PersistenceCapable interface to the class being
 2608        * enhanced, and adds a default constructor for use by OpenJPA
 2609        * if it is not already present.
 2610        */
 2611       private void enhanceClass() {
 2612           // make the class implement PersistenceCapable
 2613           _pc.declareInterface(PCTYPE);
 2614   
 2615           // add a version stamp
 2616           addGetEnhancementContractVersionMethod();
 2617   
 2618           // find the default constructor
 2619           BCMethod method = _pc.getDeclaredMethod("<init>", (String[]) null);
 2620   
 2621           // a default constructor is required
 2622           if (method == null) {
 2623               String name = _pc.getName();
 2624               if (!_defCons)
 2625                   throw new UserException(_loc.get("enhance-defaultconst", name));
 2626   
 2627               method = _pc.addDefaultConstructor();
 2628               String access;
 2629               if (_meta.isDetachable()) {
 2630                   // externalizable requires that the constructor
 2631                   // be public, so make the added constructor public
 2632                   method.makePublic();
 2633                   access = "public";
 2634               } else if (_pc.isFinal()) {
 2635                   method.makePrivate();
 2636                   access = "private";
 2637               } else {
 2638                   method.makeProtected();
 2639                   access = "protected";
 2640               }
 2641               if (!(_meta.getDescribedType().isInterface() || getCreateSubclass())
 2642                   && _log.isWarnEnabled())
 2643                   _log.warn(_loc.get("enhance-adddefaultconst", name, access));
 2644           }
 2645       }
 2646   
 2647       /**
 2648        * Adds the following fields to the PersistenceCapable instance:
 2649        * <ul>
 2650        * <li><code>private static int pcInheritedFieldCount</code></li>
 2651        * <li><code>private static Class pcPCSuperclass</code>
 2652        * </li>
 2653        * <li><code>private static String[] pcFieldNames</code></li>
 2654        * <li><code>private static Class[] pcFieldTypes</code></li>
 2655        * <li><code>private static byte[] pcFieldFlags</code></li>
 2656        * <li><code>protected transient StateManager pcStateManager</code>
 2657        * if no PersistenceCapable superclass present)</li>
 2658        * </ul>
 2659        */
 2660       private void addFields() {
 2661           _pc.declareField(INHERIT, int.class).setStatic(true);
 2662           _pc.declareField(PRE + "FieldNames", String[].class).setStatic(true);
 2663           _pc.declareField(PRE + "FieldTypes", Class[].class).setStatic(true);
 2664           _pc.declareField(PRE + "FieldFlags", byte[].class).setStatic(true);
 2665           _pc.declareField(SUPER, Class.class).setStatic(true);
 2666   
 2667           if (_meta.getPCSuperclass() == null || getCreateSubclass()) {
 2668               BCField field = _pc.declareField(SM, SMTYPE);
 2669               field.makeProtected();
 2670               field.setTransient(true);
 2671           }
 2672       }
 2673   
 2674       /**
 2675        * Modifies the class initialization method (creating one if necessary)
 2676        * to initialize the static fields of the PersistenceCapable instance and
 2677        * to register it with the impl helper.
 2678        */
 2679       private void addStaticInitializer() {
 2680           Code code = getOrCreateClassInitCode(true);
 2681           if (_meta.getPCSuperclass() != null) {
 2682               if (getCreateSubclass()) {
 2683                   code.constant().setValue(0);
 2684                   code.putstatic().setField(INHERIT, int.class);
 2685               } else {
 2686                   // pcInheritedFieldCount = <superClass>.pcGetManagedFieldCount()
 2687                   code.invokestatic().setMethod(getType(_meta.
 2688                       getPCSuperclassMetaData()).getName(),
 2689                       PRE + "GetManagedFieldCount", int.class.getName(), null);
 2690                   code.putstatic().setField(INHERIT, int.class);
 2691               }
 2692   
 2693               // pcPCSuperclass = <superClass>;
 2694               // this intentionally calls getDescribedType() directly
 2695               // instead of PCEnhancer.getType()
 2696               code.classconstant().setClass(
 2697                   _meta.getPCSuperclassMetaData().getDescribedType());
 2698               code.putstatic().setField(SUPER, Class.class);
 2699           }
 2700   
 2701           // pcFieldNames = new String[] { "<name1>", "<name2>", ... };
 2702           FieldMetaData[] fmds = _meta.getDeclaredFields();
 2703           code.constant().setValue(fmds.length);
 2704           code.anewarray().setType(String.class);
 2705           for (int i = 0; i < fmds.length; i++) {
 2706               code.dup();
 2707               code.constant().setValue(i);
 2708               code.constant().setValue(fmds[i].getName());
 2709               code.aastore();
 2710           }
 2711           code.putstatic().setField(PRE + "FieldNames", String[].class);
 2712   
 2713           // pcFieldTypes = new Class[] { <type1>.class, <type2>.class, ... };
 2714           code.constant().setValue(fmds.length);
 2715           code.anewarray().setType(Class.class);
 2716           for (int i = 0; i < fmds.length; i++) {
 2717               code.dup();
 2718               code.constant().setValue(i);
 2719               code.classconstant().setClass(fmds[i].getDeclaredType());
 2720               code.aastore();
 2721           }
 2722           code.putstatic().setField(PRE + "FieldTypes", Class[].class);
 2723   
 2724           // pcFieldFlags = new byte[] { <flag1>, <flag2>, ... };
 2725           code.constant().setValue(fmds.length);
 2726           code.newarray().setType(byte.class);
 2727           for (int i = 0; i < fmds.length; i++) {
 2728               code.dup();
 2729               code.constant().setValue(i);
 2730               code.constant().setValue(getFieldFlag(fmds[i]));
 2731               code.bastore();
 2732           }
 2733           code.putstatic().setField(PRE + "FieldFlags", byte[].class);
 2734   
 2735           // PCRegistry.register (cls,
 2736           //	pcFieldNames, pcFieldTypes, pcFieldFlags,
 2737           //  pcPCSuperclass, alias, new XXX ());
 2738           code.classconstant().setClass(_meta.getDescribedType());
 2739           code.getstatic().setField(PRE + "FieldNames", String[].class);
 2740           code.getstatic().setField(PRE + "FieldTypes", Class[].class);
 2741           code.getstatic().setField(PRE + "FieldFlags", byte[].class);
 2742           code.getstatic().setField(SUPER, Class.class);
 2743           
 2744           if (_meta.isMapped())
 2745               code.constant().setValue(_meta.getTypeAlias());
 2746           else
 2747               code.constant().setNull();
 2748   
 2749           if (_pc.isAbstract())
 2750               code.constant().setNull();
 2751           else {
 2752               code.anew().setType(_pc);
 2753               code.dup();
 2754               code.invokespecial().setMethod("<init>", void.class, null);
 2755           }
 2756   
 2757           code.invokestatic().setMethod(HELPERTYPE, "register", void.class,
 2758               new Class[]{ Class.class, String[].class, Class[].class,
 2759                   byte[].class, Class.class, String.class, PCTYPE });
 2760   
 2761           code.vreturn();
 2762           code.calculateMaxStack();
 2763       }
 2764   
 2765       /**
 2766        * Return the flag for the given field.
 2767        */
 2768       private static byte getFieldFlag(FieldMetaData fmd) {
 2769           if (fmd.getManagement() == FieldMetaData.MANAGE_NONE)
 2770               return -1;
 2771   
 2772           byte flags = 0;
 2773           if (fmd.getDeclaredType().isPrimitive()
 2774               || Serializable.class.isAssignableFrom(fmd.getDeclaredType()))
 2775               flags = PersistenceCapable.SERIALIZABLE;
 2776   
 2777           if (fmd.getManagement() == FieldMetaData.MANAGE_TRANSACTIONAL)
 2778               flags |= PersistenceCapable.CHECK_WRITE;
 2779           else if (!fmd.isPrimaryKey() && !fmd.isInDefaultFetchGroup())
 2780               flags |= PersistenceCapable.CHECK_WRITE
 2781                   | PersistenceCapable.CHECK_READ;
 2782           else
 2783               flags |= PersistenceCapable.MEDIATE_WRITE
 2784                   | PersistenceCapable.MEDIATE_READ;
 2785           return flags;
 2786       }
 2787   
 2788       /**
 2789        * Adds the code to properly handle PersistenceCapable serialization
 2790        * to the bytecode. This includes creating and initializing the
 2791        * static <code>serialVersionUID</code> constant if not already defined,
 2792        * as well as creating a custom <code>writeObject</code> method if the
 2793        * class is Serializable and does not define them.
 2794        */
 2795       private void addSerializationCode() {
 2796           if (externalizeDetached()
 2797               || !Serializable.class.isAssignableFrom(_meta.getDescribedType()))
 2798               return;
 2799   
 2800           if (getCreateSubclass()) {
 2801               // ##### what should happen if a type is Externalizable? It looks
 2802               // ##### like Externalizable classes will not be serialized as PCs
 2803               // ##### based on this logic.
 2804               if (!Externalizable.class.isAssignableFrom(
 2805                   _meta.getDescribedType()))
 2806                   addSubclassSerializationCode();
 2807               return;
 2808           }
 2809   
 2810           // if not already present, add a serialVersionUID field; if the instance
 2811           // is detachable and uses detached state without a declared field,
 2812           // can't add a serial version UID because we'll be adding extra fields
 2813           // to the enhanced version
 2814           BCField field = _pc.getDeclaredField("serialVersionUID");
 2815           if (field == null) {
 2816               Long uid = null;
 2817               try {
 2818                   uid = Numbers.valueOf(ObjectStreamClass.lookup
 2819                       (_meta.getDescribedType()).getSerialVersionUID());
 2820               } catch (Throwable t) {
 2821                   // last-chance catch for bug #283 (which can happen
 2822                   // in a variety of ClassLoading environments)
 2823                   if (_log.isTraceEnabled())
 2824                       _log.warn(_loc.get("enhance-uid-access", _meta), t);
 2825                   else
 2826                       _log.warn(_loc.get("enhance-uid-access", _meta));
 2827               }
 2828   
 2829               // if we couldn't access the serialVersionUID, we will have to
 2830               // skip the override of that field and not be serialization
 2831               // compatible with non-enhanced classes
 2832               if (uid != null) {
 2833                   field = _pc.declareField("serialVersionUID", long.class);
 2834                   field.makePrivate();
 2835                   field.setStatic(true);
 2836                   field.setFinal(true);
 2837   
 2838                   Code code = getOrCreateClassInitCode(false);
 2839                   code.beforeFirst();
 2840                   code.constant().setValue(uid.longValue());
 2841                   code.putstatic().setField(field);
 2842   
 2843                   code.calculateMaxStack();
 2844               }
 2845           }
 2846   
 2847           // add write object method
 2848           BCMethod write = _pc.getDeclaredMethod("writeObject",
 2849               new Class[]{ ObjectOutputStream.class });
 2850           boolean full = write == null;
 2851           if (full) {
 2852               // private void writeObject (ObjectOutputStream out)
 2853               write = _pc.declareMethod("writeObject", void.class,
 2854                   new Class[]{ ObjectOutputStream.class });
 2855               write.getExceptions(true).addException(IOException.class);
 2856               write.makePrivate();
 2857           }
 2858           modifyWriteObjectMethod(write, full);
 2859   
 2860           // and read object
 2861           BCMethod read = _pc.getDeclaredMethod("readObject",
 2862               new Class[]{ ObjectInputStream.class });
 2863           full = read == null;
 2864           if (full) {
 2865               // private void readObject (ObjectInputStream in)
 2866               read = _pc.declareMethod("readObject", void.class,
 2867                   new Class[]{ ObjectInputStream.class });
 2868               read.getExceptions(true).addException(IOException.class);
 2869               read.getExceptions(true).addException
 2870                   (ClassNotFoundException.class);
 2871               read.makePrivate();
 2872           }
 2873           modifyReadObjectMethod(read, full);
 2874       }
 2875   
 2876       private void addSubclassSerializationCode() {
 2877           // for generated subclasses, serialization must write an instance of
 2878           // the superclass instead of the subclass, so that the client VM can
 2879           // deserialize successfully.
 2880   
 2881           // private Object writeReplace() throws ObjectStreamException
 2882           BCMethod method = _pc.declareMethod("writeReplace", Object.class, null);
 2883           method.getExceptions(true).addException(ObjectStreamException.class);
 2884           Code code = method.getCode(true);
 2885   
 2886           // Object o = new <managed-type>()
 2887           code.anew().setType(_managedType); // for return
 2888           code.dup(); // for post-<init> work
 2889           code.dup(); // for <init>
 2890           code.invokespecial().setMethod(_managedType.getType(), "<init>",
 2891               void.class, null);
 2892   
 2893           // copy all the fields.
 2894           // ##### limiting to JPA @Transient limitations
 2895           FieldMetaData[] fmds = _meta.getFields();
 2896           for (int i = 0; i < fmds.length; i++) {
 2897               if (fmds[i].isTransient())
 2898                   continue;
 2899               // o.<field> = this.<field> (or reflective analog)
 2900               code.dup(); // for putfield
 2901               code.aload().setThis(); // for getfield
 2902               getfield(code, _managedType, fmds[i].getName());
 2903               putfield(code, _managedType, fmds[i].getName(),
 2904                   fmds[i].getDeclaredType());
 2905           }
 2906   
 2907           code.areturn().setType(Object.class);
 2908   
 2909           code.calculateMaxLocals();
 2910           code.calculateMaxStack();
 2911       }
 2912   
 2913       /**
 2914        * Whether the class being enhanced should externalize to a detached
 2915        * instance rather than serialize.
 2916        */
 2917       private boolean externalizeDetached() {
 2918           return ClassMetaData.SYNTHETIC.equals(_meta.getDetachedState())
 2919               && Serializable.class.isAssignableFrom(_meta.getDescribedType())
 2920               && !_repos.getConfiguration().getDetachStateInstance().
 2921               isDetachedStateTransient();
 2922       }
 2923   
 2924       /**
 2925        * Adds a custom writeObject method that delegates to the
 2926        * {@link ObjectOutputStream#defaultWriteObject} method,
 2927        * but only after calling the internal <code>pcSerializing</code> method.
 2928        */
 2929       private void modifyWriteObjectMethod(BCMethod method, boolean full) {
 2930           Code code = method.getCode(true);
 2931           code.beforeFirst();
 2932   
 2933           // bool clear = pcSerializing ();
 2934           loadManagedInstance(code, false);
 2935           code.invokevirtual().setMethod(PRE + "Serializing",
 2936               boolean.class, null);
 2937           int clear = code.getNextLocalsIndex();
 2938           code.istore().setLocal(clear);
 2939   
 2940           if (full) {
 2941               // out.defaultWriteObject ();
 2942               code.aload().setParam(0);
 2943               code.invokevirtual().setMethod(ObjectOutputStream.class,
 2944                   "defaultWriteObject", void.class, null);
 2945               code.vreturn();
 2946           }
 2947   
 2948           Instruction tmplate = ((Code) AccessController.doPrivileged(
 2949               J2DoPrivHelper.newCodeAction())).vreturn();
 2950           JumpInstruction toret;
 2951           Instruction ret;
 2952           code.beforeFirst();
 2953           while (code.searchForward(tmplate)) {
 2954               ret = code.previous();
 2955               // if (clear) pcSetDetachedState (null);
 2956               code.iload().setLocal(clear);
 2957               toret = code.ifeq();
 2958               loadManagedInstance(code, false);
 2959               code.constant().setNull();
 2960               code.invokevirtual().setMethod(PRE + "SetDetachedState",
 2961                   void.class, new Class[]{ Object.class });
 2962               toret.setTarget(ret);
 2963               code.next(); // jump over return
 2964           }
 2965           code.calculateMaxStack();
 2966           code.calculateMaxLocals();
 2967       }
 2968   
 2969       /**
 2970        * Adds a custom readObject method that delegates to the
 2971        * {@link ObjectInputStream#readObject} method.
 2972        */
 2973       private void modifyReadObjectMethod(BCMethod method, boolean full) {
 2974           Code code = method.getCode(true);
 2975           code.beforeFirst();
 2976   
 2977           // if this instance uses synthetic detached state, note that it has
 2978           // been deserialized
 2979           if (ClassMetaData.SYNTHETIC.equals(_meta.getDetachedState())) {
 2980               loadManagedInstance(code, false);
 2981               code.getstatic().setField(PersistenceCapable.class,
 2982                   "DESERIALIZED", Object.class);
 2983               code.invokevirtual().setMethod(PRE + "SetDetachedState",
 2984                   void.class, new Class[]{ Object.class });
 2985           }
 2986   
 2987           if (full) {
 2988               // in.defaultReadObject ();
 2989               code.aload().setParam(0);
 2990               code.invokevirtual().setMethod(ObjectInputStream.class,
 2991                   "defaultReadObject", void.class, null);
 2992               code.vreturn();
 2993           }
 2994   
 2995           code.calculateMaxStack();
 2996           code.calculateMaxLocals();
 2997       }
 2998   
 2999       /**
 3000        * Creates the pcIsDetached() method to determine if an instance
 3001        * is detached.
 3002        */
 3003       private void addIsDetachedMethod()
 3004           throws NoSuchMethodException {
 3005           // public boolean pcIsDetached()
 3006           BCMethod method = _pc.declareMethod(PRE + "IsDetached",
 3007               Boolean.class, null);
 3008           method.makePublic();
 3009           Code code = method.getCode(true);
 3010           boolean needsDefinitiveMethod = writeIsDetachedMethod(code);
 3011           code.calculateMaxStack();
 3012           code.calculateMaxLocals();
 3013           if (!needsDefinitiveMethod) 
 3014               return;
 3015   
 3016           // private boolean pcIsDetachedStateDefinitive()
 3017           //   return false;
 3018           // auxilliary enhancers may change the return value of this method
 3019           // if their specs consider detached state definitive
 3020           method = _pc.declareMethod(ISDETACHEDSTATEDEFINITIVE, boolean.class,
 3021               null);
 3022           method.makePrivate();
 3023           code = method.getCode(true);
 3024           code.constant().setValue(false);
 3025           code.ireturn();
 3026           code.calculateMaxStack();
 3027           code.calculateMaxLocals();
 3028       }
 3029   
 3030       /**
 3031        * Creates the body of the pcIsDetached() method to determine if an
 3032        * instance is detached.
 3033        *
 3034        * @return true if we need a pcIsDetachedStateDefinitive method, false
 3035        * otherwise
 3036        */
 3037       private boolean writeIsDetachedMethod(Code code)
 3038           throws NoSuchMethodException {
 3039           // not detachable: return Boolean.FALSE
 3040           if (!_meta.isDetachable()) {
 3041               code.getstatic().setField(Boolean.class, "FALSE", Boolean.class);
 3042               code.areturn();
 3043               return false;
 3044           }
 3045   
 3046           // if (sm != null)
 3047           //		return (sm.isDetached ()) ? Boolean.TRUE : Boolean.FALSE;
 3048           loadManagedInstance(code, false);
 3049           code.getfield().setField(SM, SMTYPE);
 3050           JumpInstruction ifins = code.ifnull();
 3051           loadManagedInstance(code, false);
 3052           code.getfield().setField(SM, SMTYPE);
 3053           code.invokeinterface().setMethod(SMTYPE, "isDetached",
 3054               boolean.class, null);
 3055           JumpInstruction iffalse = code.ifeq();
 3056           code.getstatic().setField(Boolean.class, "TRUE", Boolean.class);
 3057           code.areturn();
 3058           iffalse.setTarget(code.getstatic().setField(Boolean.class, "FALSE",
 3059               Boolean.class));
 3060           code.areturn();
 3061   
 3062           // if we use detached state:
 3063           // if (pcGetDetachedState () != null
 3064           //     && pcGetDetachedState != DESERIALIZED)
 3065           //     return Boolean.TRUE;
 3066           Boolean state = _meta.usesDetachedState();
 3067           JumpInstruction notdeser = null;
 3068           Instruction target;
 3069           if (state != Boolean.FALSE) {
 3070               ifins.setTarget(loadManagedInstance(code, false));
 3071               code.invokevirtual().setMethod(PRE + "GetDetachedState",
 3072                   Object.class, null);
 3073               ifins = code.ifnull();
 3074               loadManagedInstance(code, false);
 3075               code.invokevirtual().setMethod(PRE + "GetDetachedState",
 3076                   Object.class, null);
 3077               code.getstatic().setField(PersistenceCapable.class,
 3078                   "DESERIALIZED", Object.class);
 3079               notdeser = code.ifacmpeq();
 3080               code.getstatic().setField(Boolean.class, "TRUE", Boolean.class);
 3081               code.areturn();
 3082   
 3083               if (state == Boolean.TRUE) {
 3084                   // if we have to use detached state:
 3085                   // return Boolean.FALSE;
 3086                   target = code.getstatic().setField(Boolean.class, "FALSE",
 3087                       Boolean.class);
 3088                   ifins.setTarget(target);
 3089                   notdeser.setTarget(target);
 3090                   code.areturn();
 3091                   return false;
 3092               }
 3093           }
 3094   
 3095           // create artificial target to simplify
 3096           target = code.nop();
 3097           ifins.setTarget(target);
 3098           if (notdeser != null)
 3099               notdeser.setTarget(target);
 3100   
 3101           // allow users with version or auto-assigned pk fields to manually 
 3102           // construct a "detached" instance, so check these before taking into 
 3103           // account non-existent detached state
 3104   
 3105           // consider detached if version is non-default
 3106           FieldMetaData version = _meta.getVersionField();
 3107           if (state != Boolean.TRUE && version != null) {
 3108               // if (<version> != <default>)
 3109               //		return true;
 3110               loadManagedInstance(code, false);
 3111               addGetManagedValueCode(code, version);
 3112               ifins = ifDefaultValue(code, version);
 3113               code.getstatic().setField(Boolean.class, "TRUE", Boolean.class);
 3114               code.areturn();
 3115               ifins.setTarget(code.getstatic().setField(Boolean.class, "FALSE",
 3116                   Boolean.class));
 3117               code.areturn();
 3118               return false;
 3119           }
 3120   
 3121           // consider detached if auto-genned primary keys are non-default
 3122           ifins = null;
 3123           JumpInstruction ifins2 = null;
 3124           boolean hasAutoAssignedPK = false;
 3125           if (state != Boolean.TRUE
 3126               && _meta.getIdentityType() == ClassMetaData.ID_APPLICATION) {
 3127               // for each pk field:
 3128               // if (<pk> != <default> [&& !"".equals (<pk>)])
 3129               //		return Boolean.TRUE;
 3130               FieldMetaData[] pks = _meta.getPrimaryKeyFields();
 3131               for (int i = 0; i < pks.length; i++) {
 3132                   if (pks[i].getValueStrategy() == ValueStrategies.NONE)
 3133                       continue;
 3134   
 3135                   target = loadManagedInstance(code, false);
 3136                   if (ifins != null)
 3137                       ifins.setTarget(target);
 3138                   if (ifins2 != null)
 3139                       ifins2.setTarget(target);
 3140                   ifins2 = null;
 3141   
 3142                   addGetManagedValueCode(code, pks[i]);
 3143                   ifins = ifDefaultValue(code, pks[i]);
 3144                   if (pks[i].getDeclaredTypeCode() == JavaTypes.STRING) {
 3145                       code.constant().setValue("");
 3146                       loadManagedInstance(code, false);
 3147                       addGetManagedValueCode(code, pks[i]);
 3148                       code.invokevirtual().setMethod(String.class, "equals",
 3149                           boolean.class, new Class[]{ Object.class });
 3150                       ifins2 = code.ifne();
 3151                   }
 3152                   code.getstatic().setField(Boolean.class, "TRUE",
 3153                       Boolean.class);
 3154                   code.areturn();
 3155               }
 3156           }
 3157   
 3158           // create artificial target to simplify
 3159           target = code.nop();
 3160           if (ifins != null)
 3161               ifins.setTarget(target);
 3162           if (ifins2 != null)
 3163               ifins2.setTarget(target);
 3164   
 3165           // if has auto-assigned pk and we get to this point, must have default
 3166           // value, so must be new instance
 3167           if (hasAutoAssignedPK) {
 3168               code.getstatic().setField(Boolean.class, "FALSE", Boolean.class);
 3169               code.areturn();
 3170               return false;
 3171           }
 3172   
 3173           // if detached state is not definitive, just give up now and return
 3174           // null so that the runtime will perform a DB lookup to determine
 3175           // whether we're detached or new
 3176           code.aload().setThis();
 3177           code.invokespecial().setMethod(ISDETACHEDSTATEDEFINITIVE, boolean.class,
 3178               null);
 3179           ifins = code.ifne();
 3180           code.constant().setNull();
 3181           code.areturn();
 3182           ifins.setTarget(code.nop());
 3183   
 3184           // no detached state: if instance uses detached state and it's not
 3185           // synthetic or the instance is not serializable or the state isn't
 3186           // transient, must not be detached
 3187           if (state == null
 3188               && (!ClassMetaData.SYNTHETIC.equals(_meta.getDetachedState())
 3189               || !Serializable.class.isAssignableFrom(_meta.getDescribedType())
 3190               || !_repos.getConfiguration().getDetachStateInstance().
 3191               isDetachedStateTransient())) {
 3192               // return Boolean.FALSE
 3193               code.getstatic().setField(Boolean.class, "FALSE", Boolean.class);
 3194               code.areturn();
 3195               return true;
 3196           }
 3197   
 3198           // no detached state: if instance uses detached state (and must be
 3199           // synthetic and transient in serializable instance at this point),
 3200           // not detached if state not set to DESERIALIZED
 3201           if (state == null) {
 3202               // if (pcGetDetachedState () == null) // instead of DESERIALIZED
 3203               //     return Boolean.FALSE;
 3204               loadManagedInstance(code, false);
 3205               code.invokevirtual().setMethod(PRE + "GetDetachedState",
 3206                   Object.class, null);
 3207               ifins = code.ifnonnull();
 3208               code.getstatic().setField(Boolean.class, "FALSE", Boolean.class);
 3209               code.areturn();
 3210               ifins.setTarget(code.nop());
 3211           }
 3212   
 3213           // give up; we just don't know
 3214           code.constant().setNull();
 3215           code.areturn();
 3216           return true;
 3217       }
 3218   
 3219       /**
 3220        * Compare the given field to its Java default, returning the
 3221        * comparison instruction. The field value will already be on the stack.
 3222        */
 3223       private static JumpInstruction ifDefaultValue(Code code,
 3224           FieldMetaData fmd) {
 3225           switch (fmd.getDeclaredTypeCode()) {
 3226               case JavaTypes.BOOLEAN:
 3227               case JavaTypes.BYTE:
 3228               case JavaTypes.CHAR:
 3229               case JavaTypes.INT:
 3230               case JavaTypes.SHORT:
 3231                   return code.ifeq();
 3232               case JavaTypes.DOUBLE:
 3233                   code.constant().setValue(0D);
 3234                   code.dcmpl();
 3235                   return code.ifeq();
 3236               case JavaTypes.FLOAT:
 3237                   code.constant().setValue(0F);
 3238                   code.fcmpl();
 3239                   return code.ifeq();
 3240               case JavaTypes.LONG:
 3241                   code.constant().setValue(0L);
 3242                   code.lcmp();
 3243                   return code.ifeq();
 3244               default:
 3245                   return code.ifnull();
 3246           }
 3247       }
 3248   
 3249       /**
 3250        * Helper method to get the code for the class initializer method,
 3251        * creating the method if it does not already exist.
 3252        */
 3253       private Code getOrCreateClassInitCode(boolean replaceLast) {
 3254           BCMethod clinit = _pc.getDeclaredMethod("<clinit>");
 3255           Code code;
 3256           if (clinit != null) {
 3257               code = clinit.getCode(true);
 3258               if (replaceLast) {
 3259                   Code template = (Code) AccessController.doPrivileged(
 3260                       J2DoPrivHelper.newCodeAction());
 3261                   code.searchForward(template.vreturn());
 3262                   code.previous();
 3263                   code.set(template.nop());
 3264                   code.next();
 3265               }
 3266               return code;
 3267           }
 3268   
 3269           // add static initializer method if non exists
 3270           clinit = _pc.declareMethod("<clinit>", void.class, null);
 3271           clinit.makePackage();
 3272           clinit.setStatic(true);
 3273           clinit.setFinal(true);
 3274   
 3275           code = clinit.getCode(true);
 3276           if (!replaceLast) {
 3277               code.vreturn();
 3278               code.previous();
 3279           }
 3280           return code;
 3281       }
 3282   
 3283       /**
 3284        * Adds bytecode modifying the cloning behavior of the class being
 3285        * enhanced to correctly replace the <code>pcStateManager</code> 
 3286        * instance fields of any clone created with their default values. 
 3287        * Also, if this class is the base PC type and does not declared 
 3288        * a clone method, one will be added. Also, if _pc is a synthetic
 3289        * subclass, create the clone() method that clears the state manager
 3290        * that may have been initialized in a super's clone() method.
 3291        */
 3292       private void addCloningCode() {
 3293           if (_meta.getPCSuperclass() != null && !getCreateSubclass())
 3294               return;
 3295   
 3296           // add the clone method if necessary
 3297           BCMethod clone = _pc.getDeclaredMethod("clone", 
 3298               (String[]) null);
 3299           String superName = _managedType.getSuperclassName();
 3300           Code code = null;
 3301           if (clone == null) {
 3302               // add clone support for base classes
 3303               // which also implement cloneable
 3304               boolean isCloneable = Cloneable.class.isAssignableFrom(
 3305                   _managedType.getType());
 3306               boolean extendsObject =
 3307                   superName.equals(Object.class.getName());
 3308               if (!isCloneable || (!extendsObject && !getCreateSubclass()))
 3309                   return;
 3310   
 3311               if (!getCreateSubclass())
 3312                   if (_log.isTraceEnabled())
 3313                       _log.trace(
 3314                           _loc.get("enhance-cloneable", _managedType.getName()));
 3315   
 3316               // add clone method
 3317               // protected Object clone () throws CloneNotSupportedException
 3318               clone = _pc.declareMethod("clone", Object.class, null);
 3319               if (!setVisibilityToSuperMethod(clone))
 3320                   clone.makeProtected();
 3321               clone.getExceptions(true).addException
 3322                   (CloneNotSupportedException.class);
 3323               code = clone.getCode(true);
 3324   
 3325               // return super.clone ();
 3326               loadManagedInstance(code, false);
 3327               code.invokespecial().setMethod(superName, "clone",
 3328                   Object.class.getName(), null);
 3329               code.areturn();
 3330           } else {
 3331               // get the clone method code
 3332               code = clone.getCode(false);
 3333               if (code == null)
 3334                   return;
 3335           }
 3336   
 3337           // create template super.clone () instruction to match against
 3338           Instruction template = ((Code) AccessController.doPrivileged(
 3339               J2DoPrivHelper.newCodeAction())).invokespecial()
 3340               .setMethod(superName, "clone", Object.class.getName(), null);
 3341   
 3342           // find calls to the template instruction; on match
 3343           // clone will be on stack
 3344           code.beforeFirst();
 3345           if (code.searchForward(template)) {
 3346               // ((<type>) clone).pcStateManager = null;
 3347               code.dup();
 3348               code.checkcast().setType(_pc);
 3349               code.constant().setNull();
 3350               code.putfield().setField(SM, SMTYPE);
 3351   
 3352               // if modified, increase stack
 3353               code.calculateMaxStack();
 3354               code.calculateMaxLocals();
 3355           }
 3356       }
 3357   
 3358       /**
 3359        * Gets the auxiliary enhancers registered as {@link Services services}.
 3360        */
 3361       public AuxiliaryEnhancer[] getAuxiliaryEnhancers() {
 3362   		return _auxEnhancers;
 3363       }
 3364       
 3365       /**
 3366        * Allow any registered auxiliary code generators to run.
 3367        */
 3368       private void runAuxiliaryEnhancers() {
 3369       	for (int i = 0; i < _auxEnhancers.length; i++)
 3370       		_auxEnhancers[i].run(_pc, _meta);
 3371       }
 3372       
 3373       /**
 3374        * Affirms if the given method be skipped.
 3375        * 
 3376        * @param method method to be skipped or not
 3377        * @return true if any of the auxiliary enhancers skips the given method,
 3378        * or if the method is a constructor
 3379        */
 3380       private boolean skipEnhance(BCMethod method) {
 3381           if ("<init>".equals(method.getName()))
 3382               return true;
 3383           
 3384           for (int i = 0; i < _auxEnhancers.length; i++)
 3385       		if (_auxEnhancers[i].skipEnhance(method))
 3386       			return true;
 3387           
 3388           return false;
 3389       }
 3390   
 3391       /**
 3392        * Adds synthetic field access methods that will replace all direct
 3393        * field accesses.
 3394        */
 3395       private void addAccessors()
 3396           throws NoSuchMethodException {
 3397           FieldMetaData[] fmds = getCreateSubclass() ? _meta.getFields()
 3398               : _meta.getDeclaredFields();
 3399           for (int i = 0; i < fmds.length; i++) {
 3400               if (getCreateSubclass()) {
 3401                   if (!getRedefine()
 3402                       && _meta.getAccessType() != ClassMetaData.ACCESS_FIELD) {
 3403                       addSubclassSetMethod(fmds[i]);
 3404                       addSubclassGetMethod(fmds[i]);
 3405                   }
 3406               } else {
 3407                   addGetMethod(i, fmds[i]);
 3408                   addSetMethod(i, fmds[i]);
 3409               }
 3410           }
 3411       }
 3412   
 3413       /**
 3414        * Adds a non-static setter that delegates to the super methods, and
 3415        * performs any necessary field tracking.
 3416        */
 3417       private void addSubclassSetMethod(FieldMetaData fmd)
 3418           throws NoSuchMethodException {
 3419           Class propType = fmd.getDeclaredType();
 3420           String setterName = getSetterName(fmd);
 3421           BCMethod setter = _pc.declareMethod(setterName, void.class,
 3422               new Class[] { propType });
 3423           setVisibilityToSuperMethod(setter);
 3424           Code code = setter.getCode(true);
 3425   
 3426           // not necessary if we're already tracking access via redefinition
 3427           if (!getRedefine()) {
 3428               // get the orig value onto stack
 3429               code.aload().setThis();
 3430               addGetManagedValueCode(code, fmd);
 3431               int val = code.getNextLocalsIndex();
 3432               code.xstore().setLocal(val).setType(fmd.getDeclaredType());
 3433               addNotifyMutation(code, fmd, val, 0);
 3434           }
 3435   
 3436           // ##### test case: B extends A. Methods defined in A. What
 3437           // ##### happens?
 3438           // super.setXXX(...)
 3439           code.aload().setThis();
 3440           code.xload().setParam(0).setType(propType);
 3441           code.invokespecial().setMethod(_managedType.getType(),
 3442               setterName, void.class, new Class[] { propType });
 3443   
 3444           code.vreturn();
 3445           code.calculateMaxLocals();
 3446           code.calculateMaxStack();
 3447       }
 3448   
 3449       private boolean setVisibilityToSuperMethod(BCMethod method) {
 3450           BCMethod[] methods = _managedType.getMethods(method.getName(),
 3451               method.getParamTypes());
 3452           if (methods.length == 0)
 3453               throw new UserException(_loc.get("no-accessor",
 3454                   _managedType.getName(), method.getName()));
 3455           BCMethod superMeth = methods[0];
 3456           if (superMeth.isPrivate()) {
 3457               method.makePrivate();
 3458               return true;
 3459           } else if (superMeth.isPackage()) {
 3460               method.makePackage();
 3461               return true;
 3462           } else if (superMeth.isProtected()) {
 3463               method.makeProtected();
 3464               return true;
 3465           } else if (superMeth.isPublic()) {
 3466               method.makePublic();
 3467               return true;
 3468           }
 3469           return false;
 3470       }
 3471   
 3472       /**
 3473        * Adds a non-static getter that delegates to the super methods, and
 3474        * performs any necessary field tracking.
 3475        */
 3476       private void addSubclassGetMethod(FieldMetaData fmd) {
 3477           String methName = "get" + StringUtils.capitalize(fmd.getName());
 3478           if (_managedType.getMethods(methName, new Class[0]).length == 0)
 3479               methName = "is" + StringUtils.capitalize(fmd.getName());
 3480           BCMethod getter = _pc.declareMethod(methName, fmd.getDeclaredType(),
 3481               null);
 3482           setVisibilityToSuperMethod(getter);
 3483           getter.makePublic();
 3484           Code code = getter.getCode(true);
 3485   
 3486           // if we're not already tracking field access via reflection, then we
 3487           // must make the getter hook in lazy loading before accessing the super
 3488           // method.
 3489           if (!getRedefine())
 3490               addNotifyAccess(code, fmd);
 3491   
 3492           code.aload().setThis();
 3493           code.invokespecial().setMethod(_managedType.getType(), methName,
 3494               fmd.getDeclaredType(), null);
 3495           code.xreturn().setType(fmd.getDeclaredType());
 3496           code.calculateMaxLocals();
 3497           code.calculateMaxStack();
 3498       }
 3499   
 3500       /**
 3501        * Adds a static getter method for the given field.
 3502        * The generated method interacts with the instance state and the
 3503        * StateManager to get the value of the field.
 3504        *
 3505        * @param index the relative number of the field
 3506        * @param fmd metadata about the field to get
 3507        */
 3508       private void addGetMethod(int index, FieldMetaData fmd)
 3509           throws NoSuchMethodException {
 3510           BCMethod method = createGetMethod(fmd);
 3511           Code code = method.getCode(true);
 3512   
 3513           // if reads are not checked, just return the value
 3514           byte fieldFlag = getFieldFlag(fmd);
 3515           if ((fieldFlag & PersistenceCapable.CHECK_READ) == 0
 3516               && (fieldFlag & PersistenceCapable.MEDIATE_READ) == 0) {
 3517               loadManagedInstance(code, true);
 3518               addGetManagedValueCode(code, fmd);
 3519               code.xreturn().setType(fmd.getDeclaredType());
 3520   
 3521               code.calculateMaxStack();
 3522               code.calculateMaxLocals();
 3523               return;
 3524           }
 3525   
 3526           // if (inst.pcStateManager == null) return inst.<field>;
 3527           loadManagedInstance(code, true);
 3528           code.getfield().setField(SM, SMTYPE);
 3529           JumpInstruction ifins = code.ifnonnull();
 3530           loadManagedInstance(code, true);
 3531           addGetManagedValueCode(code, fmd);
 3532           code.xreturn().setType(fmd.getDeclaredType());
 3533   
 3534           // int field = pcInheritedFieldCount + <fieldindex>;
 3535           int fieldLocal = code.getNextLocalsIndex();
 3536           ifins.setTarget(code.getstatic().setField(INHERIT, int.class));
 3537           code.constant().setValue(index);
 3538           code.iadd();
 3539           code.istore().setLocal(fieldLocal);
 3540   
 3541           // inst.pcStateManager.accessingField (field);
 3542           // return inst.<field>;
 3543           loadManagedInstance(code, true);
 3544           code.getfield().setField(SM, SMTYPE);
 3545           code.iload().setLocal(fieldLocal);
 3546           code.invokeinterface().setMethod(SMTYPE, "accessingField", void.class,
 3547               new Class[]{ int.class });
 3548           loadManagedInstance(code, true);
 3549           addGetManagedValueCode(code, fmd);
 3550           code.xreturn().setType(fmd.getDeclaredType());
 3551   
 3552           code.calculateMaxStack();
 3553           code.calculateMaxLocals();
 3554       }
 3555   
 3556       /**
 3557        * Adds a static setter method for the given field.
 3558        * The generated method interacts with the instance state and the
 3559        * StateManager to set the value of the field.
 3560        *
 3561        * @param index the relative number of the field
 3562        * @param fmd metadata about the field to set
 3563        */
 3564       private void addSetMethod(int index, FieldMetaData fmd)
 3565           throws NoSuchMethodException {
 3566           BCMethod method = createSetMethod(fmd);
 3567           Code code = method.getCode(true);
 3568   
 3569           // PCEnhancer uses static methods; PCSubclasser does not.
 3570           int firstParamOffset = getAccessorParameterOffset();
 3571   
 3572           // if (inst.pcStateManager == null) inst.<field> = value;
 3573           loadManagedInstance(code, true);
 3574           code.getfield().setField(SM, SMTYPE);
 3575           JumpInstruction ifins = code.ifnonnull();
 3576           loadManagedInstance(code, true);
 3577           code.xload().setParam(firstParamOffset);
 3578           addSetManagedValueCode(code, fmd);
 3579           code.vreturn();
 3580   
 3581           // inst.pcStateManager.setting<fieldType>Field (inst,
 3582           //     pcInheritedFieldCount + <index>, inst.<field>, value, 0);
 3583           ifins.setTarget(loadManagedInstance(code, true));
 3584           code.getfield().setField(SM, SMTYPE);
 3585           loadManagedInstance(code, true);
 3586           code.getstatic().setField(INHERIT, int.class);
 3587           code.constant().setValue(index);
 3588           code.iadd();
 3589           loadManagedInstance(code, true);
 3590           addGetManagedValueCode(code, fmd);
 3591           code.xload().setParam(firstParamOffset);
 3592           code.constant().setValue(0);
 3593           code.invokeinterface().setMethod(getStateManagerMethod
 3594               (fmd.getDeclaredType(), "setting", false, true));
 3595           code.vreturn();
 3596   
 3597           code.calculateMaxStack();
 3598           code.calculateMaxLocals();
 3599       }
 3600   
 3601       /**
 3602        * Determines which attach / detach methods to use.
 3603        */
 3604       private void addAttachDetachCode()
 3605           throws NoSuchMethodException {
 3606           // see if any superclasses are detachable
 3607           boolean parentDetachable = false;
 3608           for (ClassMetaData parent = _meta.getPCSuperclassMetaData();
 3609               parent != null; parent = parent.getPCSuperclassMetaData()) {
 3610               if (parent.isDetachable()) {
 3611                   parentDetachable = true;
 3612                   break;
 3613               }
 3614           }
 3615   
 3616           // if parent not detachable, we need to add the detach state fields and
 3617           // accessor methods
 3618           if (_meta.getPCSuperclass() == null || getCreateSubclass()
 3619               || parentDetachable != _meta.isDetachable()) {
 3620               addIsDetachedMethod();
 3621               addDetachedStateMethods(_meta.usesDetachedState()
 3622                   != Boolean.FALSE);
 3623           }
 3624   
 3625           // if we detach on serialize, we also need to implement the
 3626           // externalizable interface to write just the state for the fields
 3627           // being detached
 3628           if (externalizeDetached()) {
 3629               try {
 3630                   addDetachExternalize(parentDetachable,
 3631                       _meta.usesDetachedState() != Boolean.FALSE);
 3632               } catch (NoSuchMethodException nsme) {
 3633                   throw new GeneralException(nsme);
 3634               }
 3635           }
 3636       }
 3637   
 3638       /**
 3639        * Add the fields to hold detached state and their accessor methods.
 3640        *
 3641        * @param impl whether to fully implement detach state functionality
 3642        */
 3643       private void addDetachedStateMethods(boolean impl) {
 3644           Field detachField = _meta.getDetachedStateField();
 3645           String name = null;
 3646           String declarer = null;
 3647           if (impl && detachField == null) {
 3648               name = PRE + "DetachedState";
 3649               declarer = _pc.getName();
 3650               BCField field = _pc.declareField(name, Object.class);
 3651               field.makePrivate();
 3652               field.setTransient(true);
 3653           } else if (impl) {
 3654               name = detachField.getName();
 3655               declarer = detachField.getDeclaringClass().getName();
 3656           }
 3657   
 3658           // public Object pcGetDetachedState ()
 3659           BCMethod method = _pc.declareMethod(PRE + "GetDetachedState",
 3660               Object.class, null);
 3661           method.setStatic(false);
 3662           method.makePublic();
 3663           int access = method.getAccessFlags();
 3664   
 3665           Code code = method.getCode(true);
 3666           if (impl) {
 3667               // return pcDetachedState;
 3668               loadManagedInstance(code, false);
 3669               getfield(code, _managedType.getProject().loadClass(declarer),
 3670                   name);
 3671           } else
 3672               code.constant().setNull();
 3673           code.areturn();
 3674           code.calculateMaxLocals();
 3675           code.calculateMaxStack();
 3676   
 3677           // public void pcSetDetachedState (Object state)
 3678           method = _pc.declareMethod(PRE + "SetDetachedState",
 3679               void.class, new Class []{ Object.class });
 3680           method.setAccessFlags(access);
 3681           code = method.getCode(true);
 3682           if (impl) {
 3683               // pcDetachedState = state;
 3684               loadManagedInstance(code, false);
 3685               code.aload().setParam(0);
 3686               putfield(code, _managedType.getProject().loadClass(declarer),
 3687                   name, Object.class);
 3688           }
 3689           code.vreturn();
 3690           code.calculateMaxStack();
 3691           code.calculateMaxLocals();
 3692       }
 3693   
 3694       /**
 3695        * Adds to <code>code</code> the instructions to get field
 3696        * <code>attrName</code> declared in type <code>declarer</code>
 3697        * onto the top of the stack.
 3698        *
 3699        * The instance to access must already be on the top of the
 3700        * stack when this is invoked.
 3701        */
 3702       private void getfield(Code code, BCClass declarer, String attrName) {
 3703           if (declarer == null)
 3704               declarer = _managedType;
 3705   
 3706           // first, see if we can convert the attribute name to a field name
 3707           String fieldName = toBackingFieldName(attrName);
 3708   
 3709           // next, find the field in the managed type hierarchy
 3710           BCField field = null;
 3711           outer: for (BCClass bc = _pc; bc != null; bc = bc.getSuperclassBC()) {
 3712               BCField[] fields = (BCField[]) AccessController
 3713                   .doPrivileged(J2DoPrivHelper.getBCClassFieldsAction(bc,
 3714                       fieldName));
 3715               for (int i = 0; i < fields.length; i++) {
 3716                   field = fields[i];
 3717                   // if we reach a field declared in this type, then this is the
 3718                   // most-masking field, and is the one that we want.
 3719                   if (fields[i].getDeclarer() == declarer) {
 3720                       break outer;
 3721                   }
 3722               }
 3723           }
 3724   
 3725           if (getCreateSubclass() && code.getMethod().getDeclarer() == _pc
 3726               && (field == null || !field.isPublic())) {
 3727               // we're creating the subclass, not redefining the user type.
 3728   
 3729               // Reflection.getXXX(this, Reflection.findField(...));
 3730               code.classconstant().setClass(declarer);
 3731               code.constant().setValue(fieldName);
 3732               code.constant().setValue(true);
 3733               code.invokestatic().setMethod(Reflection.class,
 3734                   "findField", Field.class, new Class[] {
 3735                   Class.class, String.class, boolean.class });
 3736               Class type = _meta.getField(attrName).getDeclaredType();
 3737               try {
 3738                   code.invokestatic().setMethod(
 3739                       getReflectionGetterMethod(type, Field.class));
 3740               } catch (NoSuchMethodException e) {
 3741                   // should never happen
 3742                   throw new InternalException(e);
 3743               }
 3744               if (!type.isPrimitive() && type != Object.class)
 3745                   code.checkcast().setType(type);
 3746           } else {
 3747               code.getfield().setField(declarer.getName(), fieldName,
 3748                   field.getType().getName());
 3749           }
 3750       }
 3751   
 3752       /**
 3753        * Adds to <code>code</code> the instructions to set field
 3754        * <code>attrName</code> declared in type <code>declarer</code>
 3755        * to the value of type <code>fieldType</code> on the top of the stack.
 3756        *
 3757        * When this method is invoked, the value to load must
 3758        * already be on the top of the stack in <code>code</code>,
 3759        * and the instance to load into must be second.
 3760        */
 3761       private void putfield(Code code, BCClass declarer, String attrName,
 3762           Class fieldType) {
 3763           if (declarer == null)
 3764               declarer = _managedType;
 3765   
 3766           String fieldName = toBackingFieldName(attrName);
 3767   
 3768           if (getRedefine() || getCreateSubclass()) {
 3769               // Reflection.set(this, Reflection.findField(...), value);
 3770               code.classconstant().setClass(declarer);
 3771               code.constant().setValue(fieldName);
 3772               code.constant().setValue(true);
 3773               code.invokestatic().setMethod(Reflection.class,
 3774                   "findField", Field.class, new Class[] {
 3775                   Class.class, String.class, boolean.class });
 3776               code.invokestatic().setMethod(Reflection.class, "set",
 3777                   void.class,
 3778                   new Class[] {
 3779                       Object.class,
 3780                       fieldType.isPrimitive() ? fieldType : Object.class, 
 3781                       Field.class });
 3782           } else {
 3783               code.putfield()
 3784                   .setField(declarer.getName(), fieldName, fieldType.getName());
 3785           }
 3786       }
 3787   
 3788       /**
 3789        * If using property access, see if there is a different backing field
 3790        * name for the persistent attribute <code>name</code>.
 3791        */
 3792       private String toBackingFieldName(String name) {
 3793           if (_meta.getAccessType() == ClassMetaData.ACCESS_PROPERTY
 3794               && _attrsToFields != null && _attrsToFields.containsKey(name))
 3795               name = (String) _attrsToFields.get(name);
 3796           return name;
 3797       }
 3798   
 3799       /**
 3800        * If using property access, see if there is a different persistent
 3801        * attribute name for the backing field <code>name</code>.
 3802        */
 3803       private String fromBackingFieldName(String name) {
 3804           // meta is null when doing persistence-aware enhancement
 3805           if (_meta != null
 3806               && _meta.getAccessType() == ClassMetaData.ACCESS_PROPERTY
 3807               && _fieldsToAttrs != null && _fieldsToAttrs.containsKey(name))
 3808               return (String) _fieldsToAttrs.get(name);
 3809           else
 3810               return name;
 3811       }
 3812   
 3813       /**
 3814        * Implement the externalizable interface to detach on serialize.
 3815        */
 3816       private void addDetachExternalize(boolean parentDetachable,
 3817           boolean detachedState)
 3818           throws NoSuchMethodException {
 3819           // ensure that the declared default constructor is public 
 3820           // for externalization
 3821           BCMethod meth = _pc.getDeclaredMethod("<init>", (String[]) null);
 3822           if (!meth.isPublic()) {
 3823               if (_log.isWarnEnabled())
 3824                   _log.warn(_loc.get("enhance-defcons-extern", 
 3825                     _meta.getDescribedType()));
 3826               meth.makePublic();
 3827           }
 3828           // declare externalizable interface
 3829           if (!Externalizable.class.isAssignableFrom(_meta.getDescribedType()))
 3830               _pc.declareInterface(Externalizable.class);
 3831   
 3832           // make sure the user doesn't already have custom externalization or
 3833           // serialization methods
 3834           Class[] input = new Class[]{ ObjectInputStream.class };
 3835           Class[] output = new Class[]{ ObjectOutputStream.class };
 3836           if (_managedType.getDeclaredMethod("readObject", input) != null
 3837               || _managedType.getDeclaredMethod("writeObject", output) != null)
 3838               throw new UserException(_loc.get("detach-custom-ser", _meta));
 3839           input[0] = ObjectInput.class;
 3840           output[0] = ObjectOutput.class;
 3841           if (_managedType.getDeclaredMethod("readExternal", input) != null
 3842               || _managedType.getDeclaredMethod("writeExternal", output) != null)
 3843               throw new UserException(_loc.get("detach-custom-extern", _meta));
 3844   
 3845           // create list of all unmanaged serializable fields
 3846           BCField[] fields = _managedType.getDeclaredFields();
 3847           Collection unmgd = new ArrayList(fields.length);
 3848           for (int i = 0; i < fields.length; i++) {
 3849               if (!fields[i].isTransient() && !fields[i].isStatic()
 3850                   && !fields[i].isFinal()
 3851                   && !fields[i].getName().startsWith(PRE)
 3852                   && _meta.getDeclaredField(fields[i].getName()) == null)
 3853                   unmgd.add(fields[i]);
 3854           }
 3855   
 3856           addReadExternal(parentDetachable, detachedState);
 3857           addReadUnmanaged(unmgd, parentDetachable);
 3858           addWriteExternal(parentDetachable, detachedState);
 3859           addWriteUnmanaged(unmgd, parentDetachable);
 3860       }
 3861   
 3862       /**
 3863        * Add custom readExternal method.
 3864        */
 3865       private void addReadExternal(boolean parentDetachable,
 3866           boolean detachedState)
 3867           throws NoSuchMethodException {
 3868           Class[] inargs = new Class[]{ ObjectInput.class };
 3869           BCMethod meth = _pc.declareMethod("readExternal", void.class, inargs);
 3870           Exceptions exceps = meth.getExceptions(true);
 3871           exceps.addException(IOException.class);
 3872           exceps.addException(ClassNotFoundException.class);
 3873           Code code = meth.getCode(true);
 3874   
 3875           // super.readExternal (in);
 3876           Class sup = _meta.getDescribedType().getSuperclass();
 3877           if (!parentDetachable && Externalizable.class.isAssignableFrom(sup)) {
 3878               loadManagedInstance(code, false);
 3879               code.aload().setParam(0);
 3880               code.invokespecial().setMethod(sup, "readExternal",
 3881                   void.class, inargs);
 3882           }
 3883   
 3884           // readUnmanaged (in);
 3885           loadManagedInstance(code, false);
 3886           code.aload().setParam(0);
 3887           code.invokevirtual().setMethod(getType(_meta),
 3888               PRE + "ReadUnmanaged", void.class, inargs);
 3889   
 3890           if (detachedState) {
 3891               // pcSetDetachedState (in.readObject ());
 3892               loadManagedInstance(code, false);
 3893               code.aload().setParam(0);
 3894               code.invokeinterface().setMethod(ObjectInput.class, "readObject",
 3895                   Object.class, null);
 3896               code.invokevirtual().setMethod(PRE + "SetDetachedState",
 3897                   void.class, new Class[]{ Object.class });
 3898   
 3899               // pcReplaceStateManager ((StateManager) in.readObject ());
 3900               loadManagedInstance(code, false);
 3901               code.aload().setParam(0);
 3902               code.invokeinterface().setMethod(ObjectInput.class, "readObject",
 3903                   Object.class, null);
 3904               code.checkcast().setType(StateManager.class);
 3905               code.invokevirtual().setMethod(PRE + "ReplaceStateManager",
 3906                   void.class, new Class[]{ StateManager.class });
 3907           }
 3908   
 3909           // read managed fields
 3910           FieldMetaData[] fmds = _meta.getFields();
 3911           for (int i = 0; i < fmds.length; i++)
 3912               if (!fmds[i].isTransient())
 3913                   readExternal(code, fmds[i].getName(),
 3914                       fmds[i].getDeclaredType(), fmds[i]);
 3915   
 3916           code.vreturn();
 3917           code.calculateMaxStack();
 3918           code.calculateMaxLocals();
 3919       }
 3920   
 3921       /**
 3922        * Read unmanaged fields from the stream (pcReadUnmanaged).
 3923        */
 3924       private void addReadUnmanaged(Collection unmgd, boolean parentDetachable)
 3925           throws NoSuchMethodException {
 3926           Class[] inargs = new Class[]{ ObjectInput.class };
 3927           BCMethod meth = _pc.declareMethod(PRE + "ReadUnmanaged", void.class,
 3928               inargs);
 3929           meth.makeProtected();
 3930           Exceptions exceps = meth.getExceptions(true);
 3931           exceps.addException(IOException.class);
 3932           exceps.addException(ClassNotFoundException.class);
 3933           Code code = meth.getCode(true);
 3934   
 3935           // super.readUnmanaged (in);
 3936           if (parentDetachable) {
 3937               loadManagedInstance(code, false);
 3938               code.aload().setParam(0);
 3939               code.invokespecial().setMethod(getType(_meta.
 3940                   getPCSuperclassMetaData()), PRE + "ReadUnmanaged", void.class, 
 3941                   inargs);
 3942           }
 3943   
 3944           // read declared unmanaged serializable fields
 3945           BCField field;
 3946           for (Iterator itr = unmgd.iterator(); itr.hasNext();) {
 3947               field = (BCField) itr.next();
 3948               readExternal(code, field.getName(), field.getType(), null);
 3949           }
 3950           code.vreturn();
 3951           code.calculateMaxStack();
 3952           code.calculateMaxLocals();
 3953       }
 3954   
 3955       /**
 3956        * Helper method to read a field from an externalization input stream.
 3957        */
 3958       private void readExternal(Code code, String fieldName, Class type,
 3959           FieldMetaData fmd)
 3960           throws NoSuchMethodException {
 3961           String methName;
 3962           if (type.isPrimitive()) {
 3963               methName = type.getName();
 3964               methName = Character.toUpperCase(methName.charAt(0))
 3965                   + methName.substring(1);
 3966               methName = "read" + methName;
 3967           } else
 3968               methName = "readObject";
 3969   
 3970           // <field> = in.read<type> ();
 3971           loadManagedInstance(code, false);
 3972           code.aload().setParam(0);
 3973           Class ret = (type.isPrimitive()) ? type : Object.class;
 3974           code.invokeinterface().setMethod(ObjectInput.class, methName,
 3975               ret, null);
 3976           if (!type.isPrimitive() && type != Object.class)
 3977               code.checkcast().setType(type);
 3978           if (fmd == null)
 3979               putfield(code, null, fieldName, type);
 3980           else {
 3981               addSetManagedValueCode(code, fmd);
 3982               switch (fmd.getDeclaredTypeCode()) {
 3983                   case JavaTypes.DATE:
 3984                   case JavaTypes.ARRAY:
 3985                   case JavaTypes.COLLECTION:
 3986                   case JavaTypes.MAP:
 3987                   case JavaTypes.OBJECT:
 3988                   case JavaTypes.CALENDAR:
 3989                       // if (sm != null)
 3990                       //   sm.proxyDetachedDeserialized (<index>);
 3991                       loadManagedInstance(code, false);
 3992                       code.getfield().setField(SM, SMTYPE);
 3993                       IfInstruction ifins = code.ifnull();
 3994                       loadManagedInstance(code, false);
 3995                       code.getfield().setField(SM, SMTYPE);
 3996                       code.constant().setValue(fmd.getIndex());
 3997                       code.invokeinterface().setMethod(SMTYPE,
 3998                           "proxyDetachedDeserialized", void.class,
 3999                           new Class[]{ int.class });
 4000                       ifins.setTarget(code.nop());
 4001               }
 4002           }
 4003       }
 4004   
 4005       /**
 4006        * Add custom writeExternal method.
 4007        */
 4008       private void addWriteExternal(boolean parentDetachable,
 4009           boolean detachedState)
 4010           throws NoSuchMethodException {
 4011           Class[] outargs = new Class[]{ ObjectOutput.class };
 4012           BCMethod meth = _pc.declareMethod("writeExternal", void.class, outargs);
 4013           Exceptions exceps = meth.getExceptions(true);
 4014           exceps.addException(IOException.class);
 4015           Code code = meth.getCode(true);
 4016   
 4017           // super.writeExternal (out);
 4018           Class sup = getType(_meta).getSuperclass();
 4019           if (!parentDetachable && Externalizable.class.isAssignableFrom(sup)) {
 4020               loadManagedInstance(code, false);
 4021               code.aload().setParam(0);
 4022               code.invokespecial().setMethod(sup, "writeExternal",
 4023                   void.class, outargs);
 4024           }
 4025   
 4026           // writeUnmanaged (out);
 4027           loadManagedInstance(code, false);
 4028           code.aload().setParam(0);
 4029           code.invokevirtual().setMethod(getType(_meta),
 4030               PRE + "WriteUnmanaged", void.class, outargs);
 4031   
 4032           JumpInstruction go2 = null;
 4033           if (detachedState) {
 4034               // if (sm != null)
 4035               //   if (sm.writeDetached (out))
 4036               //      return;
 4037               loadManagedInstance(code, false);
 4038               code.getfield().setField(SM, SMTYPE);
 4039               IfInstruction ifnull = code.ifnull();
 4040               loadManagedInstance(code, false);
 4041               code.getfield().setField(SM, SMTYPE);
 4042               code.aload().setParam(0);
 4043               code.invokeinterface().setMethod(SMTYPE, "writeDetached",
 4044                   boolean.class, outargs);
 4045               go2 = code.ifeq();
 4046               code.vreturn();
 4047   
 4048               // else
 4049               //   out.writeObject (pcGetDetachedState ());
 4050               Class[] objargs = new Class[]{ Object.class };
 4051               ifnull.setTarget(code.aload().setParam(0));
 4052               loadManagedInstance(code, false);
 4053               code.invokevirtual().setMethod(PRE + "GetDetachedState",
 4054                   Object.class, null);
 4055               code.invokeinterface().setMethod(ObjectOutput.class,
 4056                   "writeObject", void.class, objargs);
 4057               //    out.writeObject (null) // StateManager
 4058               code.aload().setParam(0);
 4059               code.constant().setValue((Object) null);
 4060               code.invokeinterface().setMethod(ObjectOutput.class,
 4061                   "writeObject", void.class, objargs);
 4062           }
 4063           if (go2 != null)
 4064               go2.setTarget(code.nop());
 4065   
 4066           // write managed fields
 4067           FieldMetaData[] fmds = _meta.getFields();
 4068           for (int i = 0; i < fmds.length; i++)
 4069               if (!fmds[i].isTransient())
 4070                   writeExternal(code, fmds[i].getName(),
 4071                       fmds[i].getDeclaredType(), fmds[i]);
 4072   
 4073           // return
 4074           code.vreturn();
 4075           code.calculateMaxStack();
 4076           code.calculateMaxLocals();
 4077       }
 4078   
 4079       /**
 4080        * Write unmanaged fields to the stream (pcWriteUnmanaged).
 4081        */
 4082       private void addWriteUnmanaged(Collection unmgd, boolean parentDetachable)
 4083           throws NoSuchMethodException {
 4084           Class[] outargs = new Class[]{ ObjectOutput.class };
 4085           BCMethod meth = _pc.declareMethod(PRE + "WriteUnmanaged", void.class,
 4086               outargs);
 4087           meth.makeProtected();
 4088           Exceptions exceps = meth.getExceptions(true);
 4089           exceps.addException(IOException.class);
 4090           Code code = meth.getCode(true);
 4091   
 4092           // super.writeUnmanaged (out);
 4093           if (parentDetachable) {
 4094               loadManagedInstance(code, false);
 4095               code.aload().setParam(0);
 4096               code.invokespecial().setMethod(getType(_meta.
 4097                   getPCSuperclassMetaData()), PRE + "WriteUnmanaged", void.class, 
 4098                   outargs);
 4099           }
 4100   
 4101           // write declared unmanaged serializable fields
 4102           BCField field;
 4103           for (Iterator itr = unmgd.iterator(); itr.hasNext();) {
 4104               field = (BCField) itr.next();
 4105               writeExternal(code, field.getName(), field.getType(), null);
 4106           }
 4107           code.vreturn();
 4108           code.calculateMaxStack();
 4109           code.calculateMaxLocals();
 4110       }
 4111   
 4112       /**
 4113        * Helper method to write a field to an externalization output stream.
 4114        */
 4115       private void writeExternal(Code code, String fieldName, Class type,
 4116           FieldMetaData fmd)
 4117           throws NoSuchMethodException {
 4118           String methName;
 4119           if (type.isPrimitive()) {
 4120               methName = type.getName();
 4121               methName = Character.toUpperCase(methName.charAt(0))
 4122                   + methName.substring(1);
 4123               methName = "write" + methName;
 4124           } else
 4125               methName = "writeObject";
 4126   
 4127           // out.write<type> (<field>);
 4128           code.aload().setParam(0);
 4129           loadManagedInstance(code, false);
 4130           if (fmd == null)
 4131               getfield(code, null, fieldName);
 4132           else
 4133               addGetManagedValueCode(code, fmd);
 4134           Class[] args = new Class[]{ type };
 4135           if (type == byte.class || type == char.class || type == short.class)
 4136               args[0] = int.class;
 4137           else if (!type.isPrimitive())
 4138               args[0] = Object.class;
 4139           code.invokeinterface().setMethod(ObjectOutput.class, methName,
 4140               void.class, args);
 4141       }
 4142   
 4143       private void addGetManagedValueCode(Code code, FieldMetaData fmd)
 4144           throws NoSuchMethodException {
 4145           addGetManagedValueCode(code, fmd, true);
 4146       }
 4147   
 4148       /**
 4149        * Load the field value specified by <code>fmd</code> onto the stack.
 4150        * Before this method is called, the object that the data should be loaded
 4151        * from will be on the top of the stack.
 4152        *
 4153        * @param fromSameClass if <code>true</code>, then <code>fmd</code> is
 4154        * being loaded from an instance of the same class as the current execution
 4155        * context. If <code>false</code>, then the instance on the top of the stack
 4156        * might be a superclass of the current execution context's 'this' instance.
 4157        */
 4158       private void addGetManagedValueCode(Code code, FieldMetaData fmd,
 4159           boolean fromSameClass)
 4160           throws NoSuchMethodException {
 4161           // if redefining, then we must always reflect (or access the field
 4162           // directly if accessible), since the redefined methods will always
 4163           // trigger method calls to StateManager, even from internal direct-
 4164           // access usage. We could work around this by not redefining, and
 4165           // just do a subclass approach instead. But this is not a good option,
 4166           // since it would sacrifice lazy loading and efficient dirty tracking.
 4167   
 4168           if (getRedefine()
 4169               || _meta.getAccessType() == ClassMetaData.ACCESS_FIELD) {
 4170               getfield(code, null, fmd.getName());
 4171           } else if (getCreateSubclass()) {
 4172               // property access, and we're not redefining. If we're operating
 4173               // on an instance that is definitely the same type as 'this', then
 4174               // call superclass method to bypass tracking. Otherwise, reflect
 4175               // to both bypass tracking and avoid class verification errors.
 4176               if (fromSameClass) {
 4177                   Method meth = (Method) fmd.getBackingMember();
 4178                   code.invokespecial().setMethod(meth);
 4179               } else {
 4180                   getfield(code, null, fmd.getName());
 4181               }
 4182           } else {
 4183               // regular enhancement + property access
 4184               Method meth = (Method) fmd.getBackingMember();
 4185               code.invokevirtual().setMethod(PRE + meth.getName(),
 4186                   meth.getReturnType(), meth.getParameterTypes());
 4187           }
 4188       }
 4189   
 4190       /**
 4191        * Store the value at the top of the stack into the field value specified
 4192        * by <code>fmd</code>. Before this method is called, the data to load will
 4193        * be on the top of the stack and the object that the data should be loaded
 4194        * into will be second in the stack.
 4195        */
 4196       private void addSetManagedValueCode(Code code, FieldMetaData fmd)
 4197           throws NoSuchMethodException {
 4198           // if redefining, then we must always reflect (or access the field
 4199           // directly if accessible), since the redefined methods will always
 4200           // trigger method calls to StateManager, even from internal direct-
 4201           // access usage. We could work around this by not redefining, and
 4202           // just do a subclass approach instead. But this is not a good option,
 4203           // since it would sacrifice lazy loading and efficient dirty tracking.
 4204   
 4205           if (getRedefine()
 4206               || _meta.getAccessType() == ClassMetaData.ACCESS_FIELD) {
 4207               putfield(code, null, fmd.getName(), fmd.getDeclaredType());
 4208           } else if (getCreateSubclass()) {
 4209               // property access, and we're not redefining. invoke the
 4210               // superclass method to bypass tracking.
 4211               code.invokespecial().setMethod(_managedType.getType(),
 4212                   getSetterName(fmd), void.class,
 4213                   new Class[] { fmd.getDeclaredType() });
 4214           } else {
 4215               // regular enhancement + property access
 4216               code.invokevirtual().setMethod(PRE + getSetterName(fmd),
 4217                   void.class, new Class[] { fmd.getDeclaredType() });
 4218           }
 4219       }
 4220   
 4221       /**
 4222        * Return the offset that the first meaningful accessor parameter is at.
 4223        */
 4224       private int getAccessorParameterOffset() {
 4225           return (_meta.getAccessType() == ClassMetaData.ACCESS_FIELD) ? 1 : 0;
 4226       }
 4227   
 4228       /**
 4229        * Add the {@link Instruction}s to load the instance to modify onto the
 4230        * stack, and return it. If <code>forStatic</code> is set, then
 4231        * <code>code</code> is in an accessor method or another static method;
 4232        * otherwise, it is in one of the PC-specified methods.
 4233        *
 4234        * @return the first instruction added to <code>code</code>.
 4235        */
 4236       private Instruction loadManagedInstance(Code code, boolean forStatic) {
 4237           if (_meta.getAccessType() == ClassMetaData.ACCESS_FIELD && forStatic)
 4238               return code.aload().setParam(0);
 4239           return code.aload().setThis();
 4240       }
 4241   
 4242       /**
 4243        * Create the generated getter {@link BCMethod} for <code>fmd</code>. The
 4244        * calling environment will then populate this method's code block.
 4245        */
 4246       private BCMethod createGetMethod(FieldMetaData fmd) {
 4247           BCMethod getter;
 4248           if (_meta.getAccessType() == ClassMetaData.ACCESS_FIELD) {
 4249               // static <fieldtype> pcGet<field> (XXX inst)
 4250               BCField field = _pc.getDeclaredField(fmd.getName());
 4251               getter = _pc.declareMethod(PRE + "Get" + fmd.getName(), fmd.
 4252                   getDeclaredType().getName(), new String[]{ _pc.getName() });
 4253               getter.setAccessFlags(field.getAccessFlags()
 4254                   & ~Constants.ACCESS_TRANSIENT & ~Constants.ACCESS_VOLATILE);
 4255               getter.setStatic(true);
 4256               getter.setFinal(true);
 4257               return getter;
 4258           }
 4259   
 4260           // property access:
 4261           // copy the user's getter method to a new name; we can't just reset
 4262           // the name, because that will also reset all calls to the method
 4263           Method meth = (Method) fmd.getBackingMember();
 4264           getter = _pc.getDeclaredMethod(meth.getName(),
 4265               meth.getParameterTypes());
 4266           BCMethod newgetter = _pc.declareMethod(PRE + meth.getName(),
 4267               meth.getReturnType(), meth.getParameterTypes());
 4268           newgetter.setAccessFlags(getter.getAccessFlags());
 4269           newgetter.makeProtected(); 
 4270           transferCodeAttributes(getter, newgetter);
 4271           return getter;
 4272       }
 4273   
 4274       /**
 4275        * Create the generated setter {@link BCMethod} for <code>fmd</code>. The
 4276        * calling environment will then populate this method's code block.
 4277        */
 4278       private BCMethod createSetMethod(FieldMetaData fmd) {
 4279           BCMethod setter;
 4280           if (_meta.getAccessType() == ClassMetaData.ACCESS_FIELD) {
 4281               // static void pcSet<field> (XXX inst, <fieldtype> value)
 4282               BCField field = _pc.getDeclaredField(fmd.getName());
 4283               setter = _pc.declareMethod(PRE + "Set" + fmd.getName(), void.class,
 4284                   new Class[]{ getType(_meta), fmd.getDeclaredType() });
 4285               setter.setAccessFlags(field.getAccessFlags()
 4286                   & ~Constants.ACCESS_TRANSIENT & ~Constants.ACCESS_VOLATILE);
 4287               setter.setStatic(true);
 4288               setter.setFinal(true);
 4289               return setter;
 4290           }
 4291   
 4292           // property access:
 4293           // copy the user's getter method to a new name; we can't just reset
 4294           // the name, because that will also reset all calls to the method
 4295           setter = _pc.getDeclaredMethod(getSetterName(fmd),
 4296               new Class[]{ fmd.getDeclaredType() });
 4297           BCMethod newsetter = _pc.declareMethod(PRE + setter.getName(),
 4298               setter.getReturnName(), setter.getParamNames());
 4299           newsetter.setAccessFlags(setter.getAccessFlags());
 4300           newsetter.makeProtected();
 4301           transferCodeAttributes(setter, newsetter);
 4302           return setter;
 4303       }
 4304   
 4305       private void addGetEnhancementContractVersionMethod() {
 4306           // public int getEnhancementContractVersion()
 4307           BCMethod method = _pc.declareMethod(PRE + 
 4308                   "GetEnhancementContractVersion", int.class, null);
 4309           method.makePublic();
 4310           Code code = method.getCode(true);
 4311           code.constant().setValue(ENHANCER_VERSION);
 4312           code.ireturn();
 4313           code.calculateMaxStack();
 4314           code.calculateMaxLocals();
 4315       }
 4316   
 4317       /**
 4318        * Return the concrete type for the given class, i.e. impl for managed
 4319        * interfaces
 4320        */
 4321       public Class getType(ClassMetaData meta) {
 4322           if (meta.getInterfaceImpl() != null)
 4323               return meta.getInterfaceImpl();
 4324           return meta.getDescribedType();
 4325       }
 4326   
 4327       /**
 4328        * Move code-related attributes from one method to another.
 4329        */
 4330       private static void transferCodeAttributes(BCMethod from, BCMethod to) {
 4331           Code code = from.getCode(false);
 4332           if (code != null) {
 4333               to.addAttribute(code);
 4334               from.removeCode();
 4335           }
 4336   
 4337           Exceptions exceps = from.getExceptions(false);
 4338           if (exceps != null)
 4339               to.addAttribute(exceps);
 4340       }
 4341   
 4342       /**
 4343        * Usage: java org.apache.openjpa.enhance.PCEnhancer [option]*
 4344        * &lt;class name | .java file | .class file | .jdo file&gt;+
 4345        *  Where the following options are recognized.
 4346        * <ul>
 4347        * <li><i>-properties/-p &lt;properties file&gt;</i>: The path to a OpenJPA
 4348        * properties file containing information as outlined in
 4349        * {@link Configuration}; optional.</li>
 4350        * <li><i>-&lt;property name&gt; &lt;property value&gt;</i>: All bean
 4351        * properties of the standard OpenJPA {@link OpenJPAConfiguration} can be
 4352        * set by using their names and supplying a value; for example:
 4353        * <li><i>-directory/-d &lt;build directory&gt;</i>: The path to the base
 4354        * directory where enhanced classes are stored. By default, the
 4355        * enhancer overwrites the original .class file with the enhanced
 4356        * version. Use this option to store the generated .class file in
 4357        * another directory. The package structure will be created beneath
 4358        * the given directory.</li>
 4359        * <li><i>-addDefaultConstructor/-adc [true/t | false/f]</i>: Whether to
 4360        * add a default constructor to persistent classes missing one, as
 4361        * opposed to throwing an exception. Defaults to true.</li>
 4362        * <li><i>-tmpClassLoader/-tcl [true/t | false/f]</i>: Whether to
 4363        * load the pre-enhanced classes using a temporary class loader.
 4364        * Defaults to true. Set this to false when attempting to debug
 4365        * class loading errors.</li>
 4366        * <li><i>-enforcePropertyRestrictions/-epr [true/t | false/f]</i>:
 4367        * Whether to throw an exception if a PROPERTY access entity appears
 4368        * to be violating standard property restrictions. Defaults to false.</li>
 4369        * </ul>
 4370        *  Each additional argument can be either the full class name of the
 4371        * type to enhance, the path to the .java file for the type, the path to
 4372        * the .class file for the type, or the path to a .jdo file listing one
 4373        * or more types to enhance.
 4374        * If the type being enhanced has metadata, it will be enhanced as a
 4375        * persistence capable class. If not, it will be considered a persistence
 4376        * aware class, and all access to fields of persistence capable classes
 4377        * will be replaced by the appropriate	get/set method. If the type
 4378        * explicitly declares the persistence-capable interface, it will
 4379        * not be enhanced. Thus, it is safe to invoke the enhancer on classes
 4380        * that are already enhanced.
 4381        */
 4382       public static void main(String[] args) {
 4383           Options opts = new Options();
 4384           args = opts.setFromCmdLine(args);
 4385           if (!run(args, opts))
 4386               System.err.println(_loc.get("enhance-usage"));
 4387       }
 4388   
 4389       /**
 4390        * Run the tool. Returns false if invalid options given. Runs against all
 4391        * the persistence units defined in the resource to parse.
 4392        */
 4393       public static boolean run(final String[] args, Options opts) {
 4394           return Configurations.runAgainstAllAnchors(opts,
 4395               new Configurations.Runnable() {
 4396               public boolean run(Options opts) throws IOException {
 4397                   OpenJPAConfiguration conf = new OpenJPAConfigurationImpl();
 4398                   try {
 4399                       return PCEnhancer.run(conf, args, opts);
 4400                   } finally {
 4401                       conf.close();
 4402                   }
 4403               }
 4404           });
 4405       }
 4406   
 4407       /**
 4408        * Run the tool. Returns false if invalid options given.
 4409        */
 4410       public static boolean run(OpenJPAConfiguration conf, String[] args,
 4411           Options opts)
 4412           throws IOException {
 4413           Flags flags = new Flags();
 4414           flags.directory = Files.getFile(opts.removeProperty("directory", "d",
 4415               null), null);
 4416           flags.addDefaultConstructor = opts.removeBooleanProperty
 4417               ("addDefaultConstructor", "adc", flags.addDefaultConstructor);
 4418           flags.tmpClassLoader = opts.removeBooleanProperty
 4419               ("tmpClassLoader", "tcl", flags.tmpClassLoader);
 4420           flags.enforcePropertyRestrictions = opts.removeBooleanProperty
 4421               ("enforcePropertyRestrictions", "epr",
 4422                   flags.enforcePropertyRestrictions);
 4423   
 4424           // for unit testing
 4425           BytecodeWriter writer = (BytecodeWriter) opts.get(
 4426               PCEnhancer.class.getName() + "#bytecodeWriter");
 4427   
 4428           Configurations.populateConfiguration(conf, opts);
 4429           return run(conf, args, flags, null, writer, null);
 4430       }
 4431   
 4432       /**
 4433        * Enhance the given classes.
 4434        */
 4435       public static boolean run(OpenJPAConfiguration conf, String[] args,
 4436           Flags flags, MetaDataRepository repos, BytecodeWriter writer,
 4437           ClassLoader loader)
 4438           throws IOException {
 4439           if (loader == null)
 4440               loader = conf.getClassResolverInstance().
 4441                   getClassLoader(PCEnhancer.class, null);
 4442           if (flags.tmpClassLoader)
 4443               loader = (ClassLoader) AccessController.doPrivileged(J2DoPrivHelper
 4444                   .newTemporaryClassLoaderAction(loader));
 4445   
 4446           if (repos == null) {
 4447               repos = conf.newMetaDataRepositoryInstance();
 4448               repos.setSourceMode(MetaDataRepository.MODE_META);
 4449           }
 4450   
 4451           Log log = conf.getLog(OpenJPAConfiguration.LOG_TOOL);
 4452           Collection classes;
 4453           if (args == null || args.length == 0) {
 4454               log.info(_loc.get("running-all-classes"));
 4455               classes = repos.getPersistentTypeNames(true, loader);
 4456               if (classes == null) {
 4457               	log.warn(_loc.get("no-class-to-enhance"));
 4458               	return false;
 4459               }
 4460           } else {
 4461               ClassArgParser cap = conf.getMetaDataRepositoryInstance().
 4462                   getMetaDataFactory().newClassArgParser();
 4463               cap.setClassLoader(loader);
 4464               classes = new HashSet();
 4465               for (int i = 0; i < args.length; i++)
 4466                   classes.addAll(Arrays.asList(cap.parseTypes(args[i])));
 4467           }
 4468   
 4469           Project project = new Project();
 4470           BCClass bc;
 4471           PCEnhancer enhancer;
 4472           int status;
 4473           for (Iterator itr = classes.iterator(); itr.hasNext();) {
 4474               Object o = itr.next();
 4475               if (log.isTraceEnabled())
 4476                   log.trace(_loc.get("enhance-running", o));
 4477   
 4478               if (o instanceof String)
 4479                   bc = project.loadClass((String) o, loader);
 4480               else
 4481                   bc = project.loadClass((Class) o);
 4482               enhancer = new PCEnhancer(conf, bc, repos, loader);
 4483               if (writer != null)
 4484                   enhancer.setBytecodeWriter(writer);
 4485               enhancer.setDirectory(flags.directory);
 4486               enhancer.setAddDefaultConstructor(flags.addDefaultConstructor);
 4487               status = enhancer.run();
 4488               if (status == ENHANCE_NONE) {
 4489                   if (log.isTraceEnabled())
 4490                       log.trace(_loc.get("enhance-norun"));
 4491               } else if (status == ENHANCE_INTERFACE) {
 4492                   if (log.isTraceEnabled())
 4493                       log.trace(_loc.get("enhance-interface"));
 4494               } else if (status == ENHANCE_AWARE) {
 4495                   if (log.isTraceEnabled())
 4496                       log.trace(_loc.get("enhance-aware"));
 4497                   enhancer.record();
 4498               } else
 4499                   enhancer.record();
 4500               project.clear();
 4501           }
 4502           return true;
 4503       }
 4504   
 4505       /**
 4506        * Run flags.
 4507        */
 4508       public static class Flags {
 4509   
 4510           public File directory = null;
 4511           public boolean addDefaultConstructor = true;
 4512           public boolean tmpClassLoader = true;
 4513   		public boolean enforcePropertyRestrictions = false;
 4514   	}
 4515   
 4516   	/**
 4517    	 * Plugin interface for additional enhancement.
 4518   	 */
 4519   	public static interface AuxiliaryEnhancer
 4520   	{
 4521   		public void run (BCClass bc, ClassMetaData meta);
 4522   		public boolean skipEnhance(BCMethod m);
 4523   	}
 4524   }

Save This Page
Home » apache-openjpa-1.1.0-source » org.apache.openjpa » enhance » [javadoc | source]