You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openjpa.apache.org by st...@apache.org on 2016/09/13 17:29:02 UTC
svn commit: r1760580 [4/5] - in /openjpa/branches/fb-3.0-asm:
openjpa-examples/openbooks/
openjpa-kernel/src/main/java/org/apache/openjpa/ant/
openjpa-kernel/src/main/java/org/apache/openjpa/conf/
openjpa-kernel/src/main/java/org/apache/openjpa/enhance...
Added: openjpa/branches/fb-3.0-asm/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCEnhancerSerp.java
URL: http://svn.apache.org/viewvc/openjpa/branches/fb-3.0-asm/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCEnhancerSerp.java?rev=1760580&view=auto
==============================================================================
--- openjpa/branches/fb-3.0-asm/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCEnhancerSerp.java (added)
+++ openjpa/branches/fb-3.0-asm/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCEnhancerSerp.java Tue Sep 13 17:29:01 2016
@@ -0,0 +1,4950 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.openjpa.enhance;
+
+import java.io.Externalizable;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInput;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutput;
+import java.io.ObjectOutputStream;
+import java.io.ObjectStreamClass;
+import java.io.Serializable;
+import java.io.ObjectStreamException;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+import org.apache.openjpa.lib.util.StringUtil;
+import org.apache.openjpa.conf.OpenJPAConfiguration;
+import org.apache.openjpa.lib.log.Log;
+import org.apache.openjpa.lib.meta.ClassArgParser;
+import org.apache.openjpa.lib.util.BytecodeWriter;
+import org.apache.openjpa.lib.util.ClassUtil;
+import org.apache.openjpa.lib.util.Files;
+import org.apache.openjpa.lib.util.J2DoPrivHelper;
+import org.apache.openjpa.lib.util.Localizer;
+import org.apache.openjpa.lib.util.Services;
+import org.apache.openjpa.lib.util.Localizer.Message;
+import org.apache.openjpa.lib.util.svn.SVNUtils;
+import org.apache.openjpa.meta.AccessCode;
+import org.apache.openjpa.meta.ClassMetaData;
+import org.apache.openjpa.meta.FieldMetaData;
+import org.apache.openjpa.meta.JavaTypes;
+import org.apache.openjpa.meta.MetaDataRepository;
+import org.apache.openjpa.meta.ValueStrategies;
+import org.apache.openjpa.util.ApplicationIds;
+import org.apache.openjpa.util.GeneralException;
+import org.apache.openjpa.util.InternalException;
+import org.apache.openjpa.util.BigDecimalId;
+import org.apache.openjpa.util.BigIntegerId;
+import org.apache.openjpa.util.ByteId;
+import org.apache.openjpa.util.CharId;
+import org.apache.openjpa.util.DateId;
+import org.apache.openjpa.util.DoubleId;
+import org.apache.openjpa.util.Id;
+import org.apache.openjpa.util.IntId;
+import org.apache.openjpa.util.FloatId;
+import org.apache.openjpa.util.LongId;
+import org.apache.openjpa.util.ObjectId;
+import org.apache.openjpa.util.ShortId;
+import org.apache.openjpa.util.StringId;
+import org.apache.openjpa.util.OpenJPAException;
+import org.apache.openjpa.util.UserException;
+import org.apache.openjpa.util.ImplHelper;
+import serp.bytecode.BCClass;
+import serp.bytecode.BCField;
+import serp.bytecode.BCMethod;
+import serp.bytecode.Code;
+import serp.bytecode.Constants;
+import serp.bytecode.Exceptions;
+import serp.bytecode.FieldInstruction;
+import serp.bytecode.GetFieldInstruction;
+import serp.bytecode.IfInstruction;
+import serp.bytecode.Instruction;
+import serp.bytecode.JumpInstruction;
+import serp.bytecode.LoadInstruction;
+import serp.bytecode.LookupSwitchInstruction;
+import serp.bytecode.MethodInstruction;
+import serp.bytecode.Project;
+import serp.bytecode.PutFieldInstruction;
+import serp.bytecode.TableSwitchInstruction;
+import serp.bytecode.ClassInstruction;
+
+/**
+ * Bytecode enhancer used to enhance persistent classes from metadata. The
+ * enhancer must be invoked on all persistence-capable and persistence aware
+ * classes.
+ *
+ * @author Abe White
+ */
+public class PCEnhancerSerp {
+ // Designates a version for maintaining compatbility when PCEnhancer
+ // modifies enhancement that can break serialization or other contracts
+ // Each enhanced class will return the value of this field via
+ // public int getEnhancementContractVersion()
+ public static final int ENHANCER_VERSION;
+
+ boolean _addVersionInitFlag = true;
+
+ public static final String PRE = "pc";
+ public static final String ISDETACHEDSTATEDEFINITIVE = PRE
+ + "isDetachedStateDefinitive";
+
+ private static final Class PCTYPE = PersistenceCapable.class;
+ private static final String SM = PRE + "StateManager";
+ private static final Class SMTYPE = StateManager.class;
+ private static final String INHERIT = PRE + "InheritedFieldCount";
+ private static final String CONTEXTNAME = "GenericContext";
+ private static final Class USEREXCEP = UserException.class;
+ private static final Class INTERNEXCEP = InternalException.class;
+ private static final Class HELPERTYPE = PCRegistry.class;
+ private static final String SUPER = PRE + "PCSuperclass";
+ private static final Class OIDFSTYPE = FieldSupplier.class;
+ private static final Class OIDFCTYPE = FieldConsumer.class;
+
+ private static final String VERSION_INIT_STR = PRE + "VersionInit";
+
+ private static final Localizer _loc = Localizer.forPackage(PCEnhancer.class);
+ private static final String REDEFINED_ATTRIBUTE = PCEnhancer.class.getName() + "#redefined-type";
+
+ private static final AuxiliarySerpEnhancer[] _auxEnhancers;
+ static {
+ Class[] classes = Services.getImplementorClasses(
+ AuxiliarySerpEnhancer.class,
+ AccessController.doPrivileged(
+ J2DoPrivHelper.getClassLoaderAction(AuxiliarySerpEnhancer.class)));
+ List auxEnhancers = new ArrayList(classes.length);
+ for (int i = 0; i < classes.length; i++) {
+ try {
+ auxEnhancers.add(AccessController.doPrivileged(
+ J2DoPrivHelper.newInstanceAction(classes[i])));
+ } catch (Throwable t) {
+ // aux enhancer may rely on non-existant spec classes, etc
+ }
+ }
+ _auxEnhancers = (AuxiliarySerpEnhancer[]) auxEnhancers.toArray
+ (new AuxiliarySerpEnhancer[auxEnhancers.size()]);
+
+ int rev = 0;
+ Properties revisionProps = new Properties();
+ try {
+ InputStream in = PCEnhancerSerp.class.getResourceAsStream("/META-INF/org.apache.openjpa.revision.properties");
+ if (in != null) {
+ try {
+ revisionProps.load(in);
+ } finally {
+ in.close();
+ }
+ }
+ String prop = revisionProps.getProperty("openjpa.enhancer.revision");
+ rev = SVNUtils.svnInfoToInteger(prop);
+ } catch (Exception e) {
+ }
+ if (rev > 0) {
+ ENHANCER_VERSION = rev;
+ } else {
+ // Something bad happened and we couldn't load from the properties file. We need to default to using the
+ // value of 2 because that is the value that was the value as of rev.511998.
+ // As of OpenJPA-3.0.0 we switched to '3' as we are now using ASM to generate the bytecode
+ ENHANCER_VERSION = 3;
+ }
+ }
+
+ private BCClass _pc;
+ private final BCClass _managedType;
+ private final MetaDataRepository _repos;
+ private final ClassMetaData _meta;
+ private final Log _log;
+ private Collection _oids = null;
+ private boolean _defCons = true;
+ private boolean _redefine = false;
+ private boolean _subclass = false;
+ private boolean _fail = false;
+ private Set _violations = null;
+ private File _dir = null;
+ private BytecodeWriter _writer = null;
+ private Map _backingFields = null; // map of set / get names => field names
+ private Map _attrsToFields = null; // map of attr names => field names
+ private Map _fieldsToAttrs = null; // map of field names => attr names
+ private boolean _isAlreadyRedefined = false;
+ private boolean _isAlreadySubclassed = false;
+ private boolean _bcsConfigured = false;
+
+ private boolean _optimizeIdCopy = false; // whether to attempt optimizing id copy
+
+ /**
+ * Constructor. Supply configuration and type to enhance. This will look
+ * up the metadata for <code>type</code> from <code>conf</code>'s
+ * repository.
+ */
+ public PCEnhancerSerp(OpenJPAConfiguration conf, Class type) {
+ this(conf, AccessController.doPrivileged(J2DoPrivHelper
+ .loadProjectClassAction(new Project(), type)),
+ (MetaDataRepository) null);
+ }
+
+ /**
+ * Constructor. Supply configuration.
+ *
+ * @param type the bytecode representation fo the type to
+ * enhance; this can be created from any stream or file
+ * @param repos a metadata repository to use for metadata access,
+ * or null to create a new reporitory; the repository
+ * from the given configuration isn't used by default
+ * because the configuration might be an
+ * implementation-specific subclass whose metadata
+ * required more than just base metadata files
+ * @deprecated use {@link #PCEnhancerSerp(OpenJPAConfiguration, BCClass,
+ MetaDataRepository, ClassLoader)} instead.
+ */
+ public PCEnhancerSerp(OpenJPAConfiguration conf, BCClass type, MetaDataRepository repos) {
+ this(conf, type, repos, null);
+ }
+
+ /**
+ * Constructor. Supply configuration.
+ *
+ * @param type the bytecode representation fo the type to
+ * enhance; this can be created from any stream or file
+ * @param repos a metadata repository to use for metadata access,
+ * or null to create a new reporitory; the repository
+ * from the given configuration isn't used by default
+ * because the configuration might be an
+ * implementation-specific subclass whose metadata
+ * required more than just base metadata files
+ * @param loader the environment classloader to use for loading
+ * classes and resources.
+ */
+ public PCEnhancerSerp(OpenJPAConfiguration conf, BCClass type, MetaDataRepository repos, ClassLoader loader) {
+ _managedType = type;
+ _pc = type;
+
+ _log = conf.getLog(OpenJPAConfiguration.LOG_ENHANCE);
+
+ if (repos == null) {
+ _repos = conf.newMetaDataRepositoryInstance();
+ _repos.setSourceMode(MetaDataRepository.MODE_META);
+ } else
+ _repos = repos;
+ _meta = _repos.getMetaData(type.getType(), loader, false);
+
+ configureOptimizeIdCopy();
+ }
+
+ /**
+ * Constructor. Supply repository. The repository's configuration will
+ * be used, and the metadata passed in will be used as-is without doing
+ * any additional lookups. This is useful when running the enhancer
+ * during metadata load.
+ *
+ * @param repos a metadata repository to use for metadata access,
+ * or null to create a new reporitory; the repository
+ * from the given configuration isn't used by default
+ * because the configuration might be an
+ * implementation-specific subclass whose metadata
+ * required more than just base metadata files
+ * @param type the bytecode representation fo the type to
+ * enhance; this can be created from any stream or file
+ * @param meta the metadata to use for processing this type.
+ *
+ * @since 1.1.0
+ */
+ public PCEnhancerSerp(MetaDataRepository repos, BCClass type,
+ ClassMetaData meta) {
+ _managedType = type;
+ _pc = type;
+
+ _log = repos.getConfiguration()
+ .getLog(OpenJPAConfiguration.LOG_ENHANCE);
+
+ _repos = repos;
+ _meta = meta;
+ }
+
+ static String toPCSubclassName(Class cls) {
+ return ClassUtil.getPackageName(PCEnhancerSerp.class) + "."
+ + cls.getName().replace('.', '$') + "$pcsubclass";
+ }
+
+ /**
+ * Whether or not <code>className</code> is the name for a
+ * dynamically-created persistence-capable subclass.
+ *
+ * @since 1.1.0
+ */
+ public static boolean isPCSubclassName(String className) {
+ return className.startsWith(ClassUtil.getPackageName(PCEnhancerSerp.class))
+ && className.endsWith("$pcsubclass");
+ }
+
+ /**
+ * If <code>className</code> is a dynamically-created persistence-capable
+ * subclass name, returns the name of the class that it subclasses.
+ * Otherwise, returns <code>className</code>.
+ *
+ * @since 1.1.0
+ */
+ public static String toManagedTypeName(String className) {
+ if (isPCSubclassName(className)) {
+ className = className.substring(
+ ClassUtil.getPackageName(PCEnhancerSerp.class).length() + 1);
+ className = className.substring(0, className.lastIndexOf("$"));
+ // this is not correct for nested PCs
+ className = className.replace('$', '.');
+ }
+
+ return className;
+ }
+
+ /**
+ * Constructor. Supply configuration, type, and metadata.
+ */
+ public PCEnhancerSerp(OpenJPAConfiguration conf, BCClass type,
+ ClassMetaData meta) {
+ this(conf, type, meta.getRepository());
+ }
+
+ /**
+ * Return the bytecode representation of the persistence-capable class
+ * being manipulated.
+ */
+ public BCClass getPCBytecode() {
+ return _pc;
+ }
+
+ /**
+ * Return the bytecode representation of the managed class being
+ * manipulated. This is usually the same as {@link #getPCBytecode},
+ * except when running the enhancer to redefine and subclass
+ * existing persistent types.
+ */
+ public BCClass getManagedTypeBytecode() {
+ return _managedType;
+ }
+
+ /**
+ * Return the metadata for the class being manipulated, or null if not
+ * a persistent type.
+ */
+ public ClassMetaData getMetaData() {
+ return _meta;
+ }
+
+ /**
+ * A boolean indicating whether the enhancer should add a no-args
+ * constructor if one is not already present in the class. OpenJPA
+ * requires that a no-arg constructor (whether created by the compiler
+ * or by the user) be present in a PC.
+ */
+ public boolean getAddDefaultConstructor() {
+ return _defCons;
+ }
+
+ /**
+ * A boolean indicating whether the enhancer should add a no-args
+ * constructor if one is not already present in the class. OpenJPA
+ * requires that a no-arg constructor (whether created by the compiler
+ * or by the user) be present in a PC.
+ */
+ public void setAddDefaultConstructor(boolean addDefaultConstructor) {
+ _defCons = addDefaultConstructor;
+ }
+
+ /**
+ * Whether the enhancer should mutate its arguments, or just run validation
+ * and optional subclassing logic on them. Usually used in conjunction with
+ * <code>setCreateSubclass(true)</code>.
+ *
+ * @since 1.0.0
+ */
+ public boolean getRedefine() {
+ return _redefine;
+ }
+
+ /**
+ * Whether the enhancer should mutate its arguments, or just run validation
+ * and optional subclassing logic on them. Usually used in conjunction with
+ * <code>setCreateSubclass(true)</code>.
+ *
+ * @since 1.0.0
+ */
+ public void setRedefine(boolean redefine) {
+ _redefine = redefine;
+ }
+
+ /**
+ * Whether the type that this instance is enhancing has already been
+ * redefined.
+ *
+ * @since 1.0.0
+ */
+ public boolean isAlreadyRedefined() {
+ return _isAlreadyRedefined;
+ }
+
+ /**
+ * Whether the type that this instance is enhancing has already been
+ * subclassed in this instance's environment classloader.
+ *
+ * @since 1.0.0
+ */
+ public boolean isAlreadySubclassed() {
+ return _isAlreadySubclassed;
+ }
+
+ /**
+ * Whether the enhancer should make its arguments persistence-capable,
+ * or generate a persistence-capable subclass.
+ *
+ * @since 1.0.0
+ */
+ public boolean getCreateSubclass() {
+ return _subclass;
+ }
+
+ /**
+ * Whether the enhancer should make its arguments persistence-capable,
+ * or generate a persistence-capable subclass.
+ *
+ * @since 1.0.0
+ */
+ public void setCreateSubclass(boolean subclass) {
+ _subclass = subclass;
+ _addVersionInitFlag = false;
+ }
+
+ /**
+ * Whether to fail if the persistent type uses property access and
+ * bytecode analysis shows that it may be violating OpenJPA's property
+ * access restrictions.
+ */
+ public boolean getEnforcePropertyRestrictions() {
+ return _fail;
+ }
+
+ /**
+ * Whether to fail if the persistent type uses property access and
+ * bytecode analysis shows that it may be violating OpenJPA's property
+ * access restrictions.
+ */
+ public void setEnforcePropertyRestrictions(boolean fail) {
+ _fail = fail;
+ }
+
+ /**
+ * The base build directory to generate code to. The proper package
+ * structure will be created beneath this directory. Defaults to
+ * overwriting the existing class file if null.
+ */
+ public File getDirectory() {
+ return _dir;
+ }
+
+ /**
+ * The base build directory to generate code to. The proper package
+ * structure will be creaed beneath this directory. Defaults to
+ * overwriting the existing class file if null.
+ */
+ public void setDirectory(File dir) {
+ _dir = dir;
+ }
+
+ /**
+ * Set the {@link BytecodeWriter} to write the bytecode to or null if none.
+ */
+ public void setBytecodeWriter(BytecodeWriter writer) {
+ _writer = writer;
+ }
+
+ /**
+ * Perform bytecode enhancements.
+ *
+ * @return <code>ENHANCE_*</code> constant
+ */
+ public int run() {
+ Class<?> type = _managedType.getType();
+ try {
+ // if managed interface, skip
+ if (_pc.isInterface())
+ return PCEnhancer.ENHANCE_INTERFACE;
+
+ // check if already enhanced
+ ClassLoader loader = AccessController.doPrivileged(J2DoPrivHelper.getClassLoaderAction(type));
+ for (String iface : _managedType.getDeclaredInterfaceNames()) {
+ if (iface.equals(PCTYPE.getName())) {
+ if (_log.isTraceEnabled()) {
+ _log.trace(_loc.get("pc-type", type, loader));
+ }
+ return PCEnhancer.ENHANCE_NONE;
+ }
+ }
+ if (_log.isTraceEnabled()) {
+ _log.trace(_loc.get("enhance-start", type, loader));
+ }
+
+
+ configureBCs();
+
+ // validate properties before replacing field access so that
+ // we build up a record of backing fields, etc
+ if (isPropertyAccess(_meta)) {
+ validateProperties();
+ if (getCreateSubclass())
+ addAttributeTranslation();
+ }
+ replaceAndValidateFieldAccess();
+ processViolations();
+
+ if (_meta != null) {
+ enhanceClass();
+ addFields();
+ addStaticInitializer();
+ addPCMethods();
+ addAccessors();
+ addAttachDetachCode();
+ addSerializationCode();
+ addCloningCode();
+ runAuxiliaryEnhancers();
+ return PCEnhancer.ENHANCE_PC;
+ }
+ return PCEnhancer.ENHANCE_AWARE;
+ } catch (OpenJPAException ke) {
+ throw ke;
+ } catch (Exception e) {
+ throw new GeneralException(_loc.get("enhance-error",
+ type.getName(), e.getMessage()), e);
+ }
+ }
+
+ private void configureBCs() {
+ if (!_bcsConfigured) {
+ if (getRedefine()) {
+ if (_managedType.getAttribute(REDEFINED_ATTRIBUTE) == null)
+ _managedType.addAttribute(REDEFINED_ATTRIBUTE);
+ else
+ _isAlreadyRedefined = true;
+ }
+
+ if (getCreateSubclass()) {
+ PCSubclassValidator val = new PCSubclassValidator(
+ _meta, _managedType, _log, _fail);
+ val.assertCanSubclass();
+
+ _pc = _managedType.getProject().loadClass(
+ toPCSubclassName(_managedType.getType()));
+ if (_pc.getSuperclassBC() != _managedType) {
+ _pc.setSuperclass(_managedType);
+ _pc.setAbstract(_managedType.isAbstract());
+ _pc.declareInterface(DynamicPersistenceCapable.class);
+ } else {
+ _isAlreadySubclassed = true;
+ }
+ }
+
+ _bcsConfigured = true;
+ }
+ }
+
+ /**
+ * Write the generated bytecode.
+ */
+ public void record()
+ throws IOException {
+ if (_managedType != _pc && getRedefine())
+ record(_managedType);
+ record(_pc);
+ if (_oids != null)
+ for (Iterator itr = _oids.iterator(); itr.hasNext();)
+ record((BCClass) itr.next());
+ }
+
+ /**
+ * Write the given class.
+ */
+ private void record(BCClass bc)
+ throws IOException {
+ if (_writer != null)
+ _writer.write(bc);
+ else if (_dir == null)
+ AsmAdaptor.write(bc);
+ else {
+ File dir = Files.getPackageFile(_dir, bc.getPackageName(), true);
+ AsmAdaptor.write(bc, new File(dir, bc.getClassName() + ".class"));
+ }
+ }
+
+ /**
+ * Validate that the methods that use a property-access instance are
+ * written correctly. This method also gathers information on each
+ * property's backing field.
+ */
+ private void validateProperties() {
+ FieldMetaData[] fmds;
+ if (getCreateSubclass())
+ fmds = _meta.getFields();
+ else
+ fmds = _meta.getDeclaredFields();
+ Method meth;
+ BCMethod getter, setter;
+ BCField returned, assigned = null;
+ for (int i = 0; i < fmds.length; i++) {
+
+ if (!(fmds[i].getBackingMember() instanceof Method) ) {
+ // If not mixed access is not defined, flag the field members,
+ // otherwise do not process them because they are valid
+ // persistent attributes.
+ if (!_meta.isMixedAccess()) {
+ addViolation("property-bad-member",
+ new Object[]{ fmds[i], fmds[i].getBackingMember() },
+ true);
+ }
+ continue;
+ }
+
+ meth = (Method) fmds[i].getBackingMember();
+ // ##### this will fail if we override and don't call super.
+ BCClass declaringType = _managedType.getProject()
+ .loadClass(fmds[i].getDeclaringType());
+ getter = declaringType.getDeclaredMethod(meth.getName(),
+ meth.getParameterTypes());
+ if (getter == null) {
+ addViolation("property-no-getter", new Object[]{ fmds[i] },
+ true);
+ continue;
+ }
+ returned = getReturnedField(getter);
+ if (returned != null)
+ registerBackingFieldInfo(fmds[i], getter, returned);
+
+ setter = declaringType.getDeclaredMethod(getSetterName(fmds[i]),
+ new Class[]{ fmds[i].getDeclaredType() });
+ if (setter == null) {
+ if (returned == null) {
+ addViolation("property-no-setter",
+ new Object[]{ fmds[i] }, true);
+ continue;
+ } else if (!getRedefine()) {
+ // create synthetic setter
+ setter = _managedType.declareMethod(getSetterName(fmds[i]),
+ void.class, new Class[]{ fmds[i].getDeclaredType() });
+ setter.makePrivate();
+ Code code = setter.getCode(true);
+ code.aload().setThis();
+ code.xload().setParam(0);
+ code.putfield().setField(returned);
+ code.vreturn();
+ code.calculateMaxStack();
+ code.calculateMaxLocals();
+ }
+ }
+
+ if (setter != null)
+ assigned = getAssignedField(setter);
+
+ if (assigned != null) {
+ if (setter != null)
+ registerBackingFieldInfo(fmds[i], setter, assigned);
+
+ if (assigned != returned)
+ addViolation("property-setter-getter-mismatch", new Object[]
+ { fmds[i], assigned.getName(), (returned == null)
+ ? null : returned.getName() }, false);
+ }
+ }
+ }
+
+ private void registerBackingFieldInfo(FieldMetaData fmd, BCMethod method,
+ BCField field) {
+ if (_backingFields == null)
+ _backingFields = new HashMap();
+ _backingFields.put(method.getName(), field.getName());
+
+ if (_attrsToFields == null)
+ _attrsToFields = new HashMap();
+ _attrsToFields.put(fmd.getName(), field.getName());
+
+ if (_fieldsToAttrs == null)
+ _fieldsToAttrs = new HashMap();
+ _fieldsToAttrs.put(field.getName(), fmd.getName());
+ }
+
+ private void addAttributeTranslation() {
+
+ // Get all field metadata
+ ArrayList<Integer> propFmds = new ArrayList<Integer>();
+ FieldMetaData[] fmds = _meta.getFields();
+
+ if (_meta.isMixedAccess()) {
+ // Stores indexes of property access fields to be used in
+ //
+ propFmds = new ArrayList<Integer>();
+
+ // Determine which fields have property access and save their
+ // indexes
+ for (int i = 0; i < fmds.length; i++) {
+ if (isPropertyAccess(fmds[i]))
+ propFmds.add(i);
+ }
+
+ // if no fields have property access do not do attribute translation
+ if (propFmds.size() == 0)
+ return;
+ }
+
+ _pc.declareInterface(AttributeTranslator.class);
+ BCMethod method = _pc.declareMethod(PRE + "AttributeIndexToFieldName",
+ String.class, new Class[] { int.class });
+ method.makePublic();
+ Code code = method.getCode(true);
+
+ // switch (val)
+ code.iload().setParam(0);
+ if (!_meta.isMixedAccess()) {
+ // if not mixed access use a table switch on all property-based fmd.
+ // a table switch is more efficient with +1 incremental operations
+ TableSwitchInstruction tabins = code.tableswitch();
+
+ tabins.setLow(0);
+ tabins.setHigh(fmds.length - 1);
+
+ // case i:
+ // return <_attrsToFields.get(fmds[i].getName())>
+ for (int i = 0; i < fmds.length; i++) {
+ tabins.addTarget(code.constant().setValue(
+ _attrsToFields.get(fmds[i].getName())));
+ code.areturn();
+ }
+ // default: throw new IllegalArgumentException ()
+ tabins.setDefaultTarget(throwException
+ (code, IllegalArgumentException.class));
+ }
+ else {
+ // In mixed access mode, property indexes are not +1 incremental
+ // a lookup switch must be used to do indexed lookup.
+ LookupSwitchInstruction lookupins = code.lookupswitch();
+
+ for (Integer i : propFmds) {
+ lookupins.addCase(i,
+ code.constant().setValue(
+ _attrsToFields.get(fmds[i].getName())));
+ code.areturn();
+ }
+ // default: throw new IllegalArgumentException ()
+ lookupins.setDefaultTarget(throwException
+ (code, IllegalArgumentException.class));
+ }
+
+ code.calculateMaxLocals();
+ code.calculateMaxStack();
+ }
+
+ /**
+ * Return the name of the setter method for the given field.
+ */
+ private static String getSetterName(FieldMetaData fmd) {
+ return fmd.getSetterName();
+ }
+
+ /**
+ * Return the field returned by the given method, or null if none.
+ * Package-protected and static for testing.
+ */
+ static BCField getReturnedField(BCMethod meth) {
+ return findField(meth, (AccessController.doPrivileged(
+ J2DoPrivHelper.newCodeAction())).xreturn()
+ .setType(meth.getReturnType()), false);
+ }
+
+ /**
+ * Return the field assigned in the given method, or null if none.
+ * Package-protected and static for testing.
+ */
+ static BCField getAssignedField(BCMethod meth) {
+ return findField(meth, (AccessController.doPrivileged(
+ J2DoPrivHelper.newCodeAction())).putfield(), true);
+ }
+
+ /**
+ * Return the field returned / assigned by <code>meth</code>. Returns
+ * null if non-fields (methods, literals, parameters, variables) are
+ * returned, or if non-parameters are assigned to fields.
+ */
+ private static BCField findField(BCMethod meth, Instruction template,
+ boolean findAccessed) {
+ // ignore any static methods. OpenJPA only currently supports
+ // non-static setters and getters
+ if (meth.isStatic())
+ return null;
+
+ Code code = meth.getCode(false);
+ if (code == null)
+ return null;
+ code.beforeFirst();
+
+ BCField field = null, cur;
+ Instruction templateIns, prevIns, earlierIns;
+ while (code.searchForward(template)) {
+ int backupCount = 3;
+ templateIns = code.previous();
+ if (!code.hasPrevious())
+ return null;
+ prevIns = code.previous();
+
+ if (prevIns instanceof ClassInstruction
+ && code.hasPrevious()) {
+ prevIns = code.previous();
+ backupCount++;
+ }
+
+ if (!code.hasPrevious())
+ return null;
+ earlierIns = code.previous();
+
+ // if the opcode two before the template was an aload_0, check
+ // against the middle instruction based on what type of find
+ // we're doing
+ if (!(earlierIns instanceof LoadInstruction)
+ || !((LoadInstruction) earlierIns).isThis())
+ return null;
+
+ // if the middle instruction was a getfield, then it's the
+ // field that's being accessed
+ if (!findAccessed && prevIns instanceof GetFieldInstruction) {
+ final FieldInstruction fPrevIns = (FieldInstruction) prevIns;
+ cur = AccessController.doPrivileged(
+ J2DoPrivHelper.getFieldInstructionFieldAction(fPrevIns));
+ // if the middle instruction was an xload_1, then the
+ // matched instruction is the field that's being set.
+ } else if (findAccessed && prevIns instanceof LoadInstruction
+ && ((LoadInstruction) prevIns).getParam() == 0) {
+ final FieldInstruction fTemplateIns =
+ (FieldInstruction) templateIns;
+ cur = AccessController.doPrivileged(J2DoPrivHelper
+ .getFieldInstructionFieldAction(fTemplateIns));
+ } else
+ return null;
+
+ if (field != null && cur != field)
+ return null;
+ field = cur;
+
+ // ready for next search iteration
+ while (backupCount > 0) {
+ code.next();
+ backupCount--;
+ }
+ }
+ return field;
+ }
+
+ /**
+ * Record a violation of the property access restrictions.
+ */
+ private void addViolation(String key, Object[] args, boolean fatal) {
+ if (_violations == null)
+ _violations = new HashSet();
+ _violations.add(_loc.get(key, args));
+ _fail |= fatal;
+ }
+
+ /**
+ * Log / throw recorded property access violations.
+ */
+ private void processViolations() {
+ if (_violations == null)
+ return;
+
+ String sep = J2DoPrivHelper.getLineSeparator();
+ StringBuilder buf = new StringBuilder();
+ for (Iterator itr = _violations.iterator(); itr.hasNext();) {
+ buf.append(itr.next());
+ if (itr.hasNext())
+ buf.append(sep);
+ }
+ Message msg = _loc.get("property-violations", buf);
+
+ if (_fail)
+ throw new UserException(msg);
+ if (_log.isWarnEnabled())
+ _log.warn(msg);
+ }
+
+ /**
+ * Replaced all direct access to managed fields with the appropriate
+ * pcGet/pcSet method. Note that this includes access to fields
+ * owned by PersistenceCapable classes other than this one.
+ */
+ private void replaceAndValidateFieldAccess() throws NoSuchMethodException {
+ // create template putfield/getfield instructions to search for
+ Code template = AccessController.doPrivileged(
+ J2DoPrivHelper.newCodeAction());
+ Instruction put = template.putfield();
+ Instruction get = template.getfield();
+ Instruction stat = template.invokestatic();
+
+ // look through all methods; this is done before any methods are added
+ // so we don't need to worry about excluding synthetic methods.
+ BCMethod[] methods = _managedType.getDeclaredMethods();
+ Code code;
+ for (int i = 0; i < methods.length; i++) {
+ code = methods[i].getCode(false);
+
+ // don't modify the methods specified by the auxiliary enhancers
+ if (code != null && !skipEnhance(methods[i])) {
+ replaceAndValidateFieldAccess(code, get, true, stat);
+ replaceAndValidateFieldAccess(code, put, false, stat);
+ }
+ }
+ }
+
+ /**
+ * Replaces all instructions matching the given template in the given
+ * code block with calls to the appropriate generated getter/setter.
+ *
+ * @param code the code block to modify; the code iterator will
+ * be placed before the first instruction on method start,
+ * and will be after the last instruction on method completion
+ * @param ins the template instruction to search for; either a
+ * getfield or putfield instruction
+ * @param get boolean indicating if this is a get instruction
+ * @param stat template invokestatic instruction to replace with
+ */
+ private void replaceAndValidateFieldAccess(Code code, Instruction ins,
+ boolean get, Instruction stat) throws NoSuchMethodException {
+ code.beforeFirst();
+
+ FieldInstruction fi;
+ MethodInstruction mi;
+ ClassMetaData owner;
+ String name, typeName, methodName;
+ while (code.searchForward(ins)) {
+ // back up to the matched instruction
+ fi = (FieldInstruction) code.previous();
+ name = fi.getFieldName();
+ typeName = fi.getFieldTypeName();
+ owner = getPersistenceCapableOwner(name, fi.getFieldDeclarerType());
+ FieldMetaData fmd = owner == null ? null : owner.getField(name);
+ if (isPropertyAccess(fmd)) {
+ // if we're directly accessing a field in another class
+ // hierarchy that uses property access, something is wrong
+ if (owner != _meta && owner.getDeclaredField(name) != null &&
+ _meta != null && !owner.getDescribedType()
+ .isAssignableFrom(_meta.getDescribedType()))
+ throw new UserException(_loc.get("property-field-access",
+ new Object[]{ _meta, owner, name,
+ code.getMethod().getName() }));
+
+ // if we're directly accessing a property-backing field outside
+ // the property in our own class, notify user
+ if (isBackingFieldOfAnotherProperty(name, code))
+ addViolation("property-field-access", new Object[]{ _meta,
+ owner, name, code.getMethod().getName() }, false);
+ }
+
+ if (owner == null ||
+ owner.getDeclaredField(fromBackingFieldName(name)) == null) {
+ // not persistent field?
+ code.next();
+ continue;
+ } else if (!getRedefine() && !getCreateSubclass()
+ && isFieldAccess(fmd)) {
+ // replace the instruction with a call to the generated access
+ // method
+ mi = (MethodInstruction) code.set(stat);
+
+ // invoke the proper access method, whether getter or setter
+ String prefix = (get) ? PRE + "Get" : PRE + "Set";
+ methodName = prefix + name;
+ if (get) {
+ mi.setMethod(getType(owner).getName(),
+ methodName, typeName, new String[]
+ { getType(owner).getName() });
+ } else {
+ mi.setMethod(getType(owner).getName(),
+ methodName, "void", new String[]
+ { getType(owner).getName(), typeName });
+ }
+ code.next();
+ } else if (getRedefine()) {
+ name = fromBackingFieldName(name);
+ if (get) {
+ addNotifyAccess(code, owner.getField(name));
+ code.next();
+ } else {
+ // insert the set operations after the field mutation, but
+ // first load the old value for use in the
+ // StateManager.settingXXX method.
+ loadManagedInstance(code, false);
+ final FieldInstruction fFi = fi;
+ code.getfield().setField(
+ AccessController.doPrivileged(J2DoPrivHelper
+ .getFieldInstructionFieldAction(fFi)));
+ int val = code.getNextLocalsIndex();
+ code.xstore().setLocal(val).setType(fi.getFieldType());
+
+ // move past the putfield
+ code.next();
+ addNotifyMutation(code, owner.getField(name), val, -1);
+ }
+ } else {
+ code.next();
+ }
+ code.calculateMaxLocals();
+ code.calculateMaxStack();
+ }
+ }
+
+ private void addNotifyAccess(Code code, FieldMetaData fmd) {
+ // PCHelper.accessingField(this, <absolute-index>);
+ code.aload().setThis();
+ code.constant().setValue(fmd.getIndex());
+ code.invokestatic().setMethod(RedefinitionHelper.class,
+ "accessingField", void.class,
+ new Class[] { Object.class, int.class });
+ }
+
+ /**
+ * This must be called after setting the value in the object.
+ *
+ * @param val the position in the local variable table where the
+ * old value is stored
+ * @param param the parameter position containing the new value, or
+ * -1 if the new value is unavailable and should therefore be looked
+ * up.
+ * @throws NoSuchMethodException
+ */
+ private void addNotifyMutation(Code code, FieldMetaData fmd, int val,
+ int param)
+ throws NoSuchMethodException {
+ // PCHelper.settingField(this, <absolute-index>, old, new);
+ code.aload().setThis();
+ code.constant().setValue(fmd.getIndex());
+ Class type = fmd.getDeclaredType();
+ // we only have special signatures for primitives and Strings
+ if (!type.isPrimitive() && type != String.class)
+ type = Object.class;
+ code.xload().setLocal(val).setType(type);
+ if (param == -1) {
+ loadManagedInstance(code, false);
+ addGetManagedValueCode(code, fmd);
+ } else {
+ code.xload().setParam(param).setType(type);
+ }
+ code.invokestatic().setMethod(RedefinitionHelper.class, "settingField",
+ void.class, new Class[] {
+ Object.class, int.class, type, type
+ });
+ }
+
+ /**
+ * Return true if the given instruction accesses a field that is a backing
+ * field of another property in this property-access class.
+ */
+ private boolean isBackingFieldOfAnotherProperty(String name, Code code) {
+ String methName = code.getMethod().getName();
+ return !"<init>".equals(methName)
+ && _backingFields != null
+ && !name.equals(_backingFields.get(methName))
+ && _backingFields.containsValue(name);
+ }
+
+ /**
+ * Helper method to return the declaring PersistenceCapable class of
+ * the given field.
+ *
+ * @param fieldName the name of the field
+ * @param owner the nominal owner of the field
+ * @return the metadata for the PersistenceCapable type that
+ * declares the field (and therefore has the static method), or null if none
+ */
+ private ClassMetaData getPersistenceCapableOwner(String fieldName,
+ Class owner) {
+ // find the actual ancestor class that declares the field, then
+ // check if the class is persistent, and if the field is managed
+ Field f = Reflection.findField(owner, fieldName, false);
+ if (f == null)
+ return null;
+
+ // managed interface
+ if (_meta != null && _meta.getDescribedType().isInterface())
+ return _meta;
+
+ return _repos.getMetaData(f.getDeclaringClass(), null, false);
+ }
+
+ /**
+ * Adds all synthetic methods to the bytecode by delegating to
+ * the various addXXXMethods () functions in this class. Includes
+ * all static field access methods.
+ * Note that the 'stock' methods like <code>pcIsTransactional</code>,
+ * <code>pcFetchObjectId</code>, etc are defined only in the
+ * least-derived PersistenceCapable type.
+ */
+ private void addPCMethods()
+ throws NoSuchMethodException {
+ addClearFieldsMethod();
+ addNewInstanceMethod(true);
+ addNewInstanceMethod(false);
+ addManagedFieldCountMethod();
+ addReplaceFieldsMethods();
+ addProvideFieldsMethods();
+ addCopyFieldsMethod();
+
+ if (_meta.getPCSuperclass() == null || getCreateSubclass()) {
+ addStockMethods();
+ addGetVersionMethod();
+ addReplaceStateManagerMethod();
+
+ if (_meta.getIdentityType() != ClassMetaData.ID_APPLICATION)
+ addNoOpApplicationIdentityMethods();
+ }
+
+ // add the app id methods to each subclass rather
+ // than just the superclass, since it is possible to have
+ // a subclass with an app id hierarchy that matches the
+ // persistent class inheritance hierarchy
+ if (_meta.getIdentityType() == ClassMetaData.ID_APPLICATION
+ && (_meta.getPCSuperclass() == null || getCreateSubclass() ||
+ _meta.getObjectIdType() !=
+ _meta.getPCSuperclassMetaData().getObjectIdType())) {
+ addCopyKeyFieldsToObjectIdMethod(true);
+ addCopyKeyFieldsToObjectIdMethod(false);
+ addCopyKeyFieldsFromObjectIdMethod(true);
+ addCopyKeyFieldsFromObjectIdMethod(false);
+ if (_meta.hasAbstractPKField() == true) {
+ addGetIDOwningClass();
+ }
+
+ if (_meta.isEmbeddable() && _meta.getIdentityType() == ClassMetaData.ID_APPLICATION) {
+ _log.warn(_loc.get("ID-field-in-embeddable-unsupported", _meta.toString()));
+ }
+
+ addNewObjectIdInstanceMethod(true);
+ addNewObjectIdInstanceMethod(false);
+ }
+ else if (_meta.hasPKFieldsFromAbstractClass()){
+ addGetIDOwningClass();
+ }
+ }
+
+ /**
+ * Add a method to clear all persistent fields; we'll call this from
+ * the new instance method to ensure that unloaded fields have
+ * default values.
+ */
+ private void addClearFieldsMethod()
+ throws NoSuchMethodException {
+ // protected void pcClearFields ()
+ BCMethod method = _pc.declareMethod(PRE + "ClearFields", void.class,
+ null);
+ method.makeProtected();
+ Code code = method.getCode(true);
+
+ // super.pcClearFields ()
+ if (_meta.getPCSuperclass() != null && !getCreateSubclass()) {
+ code.aload().setThis();
+ code.invokespecial().setMethod(getType(_meta.
+ getPCSuperclassMetaData()), PRE + "ClearFields", void.class,
+ null);
+ }
+
+ FieldMetaData[] fmds = _meta.getDeclaredFields();
+ for (int i = 0; i < fmds.length; i++) {
+ if (fmds[i].getManagement() != FieldMetaData.MANAGE_PERSISTENT)
+ continue;
+
+ loadManagedInstance(code, false);
+ switch (fmds[i].getDeclaredTypeCode()) {
+ case JavaTypes.BOOLEAN:
+ case JavaTypes.BYTE:
+ case JavaTypes.CHAR:
+ case JavaTypes.INT:
+ case JavaTypes.SHORT:
+ code.constant().setValue(0);
+ break;
+ case JavaTypes.DOUBLE:
+ code.constant().setValue(0D);
+ break;
+ case JavaTypes.FLOAT:
+ code.constant().setValue(0F);
+ break;
+ case JavaTypes.LONG:
+ code.constant().setValue(0L);
+ break;
+ default:
+ code.constant().setNull();
+ break;
+ }
+
+ addSetManagedValueCode(code, fmds[i]);
+ }
+
+ code.vreturn();
+ code.calculateMaxStack();
+ code.calculateMaxLocals();
+ }
+
+ /**
+ * Adds the <code>pcNewInstance</code> method to the bytecode.
+ * These methods are used by the impl helper to create new
+ * managed instances efficiently without reflection.
+ *
+ * @param oid set to true to mimic the method version that takes
+ * an oid value as well as a state manager
+ */
+ private void addNewInstanceMethod(boolean oid) {
+ // public PersistenceCapable pcNewInstance (...)
+ Class[] args =
+ (oid) ? new Class[]{ SMTYPE, Object.class, boolean.class }
+ : new Class[]{ SMTYPE, boolean.class };
+ BCMethod method = _pc.declareMethod(PRE + "NewInstance", PCTYPE, args);
+ Code code = method.getCode(true);
+
+ // if the type is abstract, throw a UserException
+ if (_pc.isAbstract()) {
+ throwException(code, USEREXCEP);
+
+ code.calculateMaxStack();
+ code.calculateMaxLocals();
+ return;
+ }
+
+ // XXX pc = new XXX ();
+ code.anew().setType(_pc);
+ code.dup();
+ code.invokespecial().setMethod("<init>", void.class, null);
+ int inst = code.getNextLocalsIndex();
+ code.astore().setLocal(inst);
+
+ // if (clear)
+ // pc.pcClearFields ();
+ code.iload().setParam((oid) ? 2 : 1);
+ JumpInstruction noclear = code.ifeq();
+ code.aload().setLocal(inst);
+ code.invokevirtual().setMethod(PRE + "ClearFields", void.class, null);
+
+ // pc.pcStateManager = sm;
+ noclear.setTarget(code.aload().setLocal(inst));
+ code.aload().setParam(0);
+ code.putfield().setField(SM, SMTYPE);
+
+ // copy key fields from oid
+ if (oid) {
+ code.aload().setLocal(inst);
+ code.aload().setParam(1);
+ code.invokevirtual().setMethod(PRE + "CopyKeyFieldsFromObjectId",
+ void.class, new Class[]{ Object.class });
+ }
+
+ // return pc;
+ code.aload().setLocal(inst);
+ code.areturn();
+
+ code.calculateMaxStack();
+ code.calculateMaxLocals();
+ }
+
+ /**
+ * Adds the <code>protected static int pcGetManagedFieldCount ()</code>
+ * method to the bytecode, returning the inherited field count added
+ * to the number of managed fields in the current PersistenceCapable class.
+ */
+ private void addManagedFieldCountMethod() {
+ // protected static int pcGetManagedFieldCount ()
+ BCMethod method = _pc.declareMethod(PRE + "GetManagedFieldCount",
+ int.class, null);
+ method.setStatic(true);
+ method.makeProtected();
+ Code code = method.getCode(true);
+
+ // return <fields> + pcInheritedFieldCount
+ // awhite: the above should work, but I'm seeing a messed up situation
+ // all of a sudden where when a subclass calls this method, it somehow
+ // happens before <clinit> is ever invoked, and so our
+ // pcInheritedFieldCount field isn't initialized! so instead,
+ // return <fields> + <superclass>.pcGetManagedFieldCount ()
+ code.constant().setValue(_meta.getDeclaredFields().length);
+ if (_meta.getPCSuperclass() != null) {
+ Class superClass = getType(_meta.getPCSuperclassMetaData());
+ String superName = getCreateSubclass() ?
+ PCEnhancerSerp.toPCSubclassName(superClass) :
+ superClass.getName();
+ code.invokestatic().setMethod(superName,
+ PRE + "GetManagedFieldCount", int.class.getName(), null);
+ code.iadd();
+ }
+ code.ireturn();
+ code.calculateMaxStack();
+ }
+
+ /**
+ * Adds the {@link PersistenceCapable#pcProvideField} and
+ * {@link PersistenceCapable#pcProvideFields} methods to the bytecode.
+ */
+ private void addProvideFieldsMethods()
+ throws NoSuchMethodException {
+ // public void pcProvideField (int fieldNumber)
+ BCMethod method = _pc.declareMethod(PRE + "ProvideField", void.class,
+ new Class[]{ int.class });
+ Code code = method.getCode(true);
+
+ // adds everything through the switch ()
+ int relLocal = beginSwitchMethod(PRE + "ProvideField", code);
+
+ // if no fields in this inst, just throw exception
+ FieldMetaData[] fmds = getCreateSubclass() ? _meta.getFields()
+ : _meta.getDeclaredFields();
+ if (fmds.length == 0)
+ throwException(code, IllegalArgumentException.class);
+ else {
+ // switch (val)
+ code.iload().setLocal(relLocal);
+ TableSwitchInstruction tabins = code.tableswitch();
+ tabins.setLow(0);
+ tabins.setHigh(fmds.length - 1);
+
+ // <field> = pcStateManager.provided<type>Field
+ // (this, fieldNumber);
+ for (int i = 0; i < fmds.length; i++) {
+ tabins.addTarget(loadManagedInstance(code, false));
+ code.getfield().setField(SM, SMTYPE);
+ loadManagedInstance(code, false);
+ code.iload().setParam(0);
+ loadManagedInstance(code, false);
+ addGetManagedValueCode(code, fmds[i]);
+ code.invokeinterface().setMethod(getStateManagerMethod
+ (fmds[i].getDeclaredType(), "provided", false, false));
+ code.vreturn();
+ }
+
+ // default: throw new IllegalArgumentException ()
+ tabins.setDefaultTarget(throwException
+ (code, IllegalArgumentException.class));
+ }
+
+ code.calculateMaxStack();
+ code.calculateMaxLocals();
+
+ addMultipleFieldsMethodVersion(method);
+ }
+
+ /**
+ * Adds the {@link PersistenceCapable#pcReplaceField} and
+ * {@link PersistenceCapable#pcReplaceFields} methods to the bytecode.
+ */
+ private void addReplaceFieldsMethods()
+ throws NoSuchMethodException {
+ // public void pcReplaceField (int fieldNumber)
+ BCMethod method = _pc.declareMethod(PRE + "ReplaceField", void.class,
+ new Class[]{ int.class });
+ Code code = method.getCode(true);
+
+ // adds everything through the switch ()
+ int relLocal = beginSwitchMethod(PRE + "ReplaceField", code);
+
+ // if no fields in this inst, just throw exception
+ FieldMetaData[] fmds = getCreateSubclass() ? _meta.getFields()
+ : _meta.getDeclaredFields();
+ if (fmds.length == 0)
+ throwException(code, IllegalArgumentException.class);
+ else {
+ // switch (val)
+ code.iload().setLocal(relLocal);
+ TableSwitchInstruction tabins = code.tableswitch();
+ tabins.setLow(0);
+ tabins.setHigh(fmds.length - 1);
+
+ // <field> = pcStateManager.replace<type>Field
+ // (this, fieldNumber);
+ for (int i = 0; i < fmds.length; i++) {
+ // for the addSetManagedValueCode call below.
+ tabins.addTarget(loadManagedInstance(code, false, fmds[i]));
+
+ loadManagedInstance(code, false, fmds[i]);
+ code.getfield().setField(SM, SMTYPE);
+ loadManagedInstance(code, false, fmds[i]);
+ code.iload().setParam(0);
+ code.invokeinterface().setMethod(getStateManagerMethod
+ (fmds[i].getDeclaredType(), "replace", true, false));
+ if (!fmds[i].getDeclaredType().isPrimitive())
+ code.checkcast().setType(fmds[i].getDeclaredType());
+
+ addSetManagedValueCode(code, fmds[i]);
+ if(_addVersionInitFlag){
+ if(fmds[i].isVersion()){
+ // If this case is setting the version field
+ // pcVersionInit = true;
+ loadManagedInstance(code, false);
+ code.constant().setValue(1);
+ putfield(code, null, VERSION_INIT_STR, boolean.class);
+ }
+ }
+ code.vreturn();
+ }
+
+ // default: throw new IllegalArgumentException ()
+ tabins.setDefaultTarget(throwException
+ (code, IllegalArgumentException.class));
+ }
+
+ code.calculateMaxStack();
+ code.calculateMaxLocals();
+
+ addMultipleFieldsMethodVersion(method);
+ }
+
+ /**
+ * Adds the {@link PersistenceCapable#pcCopyFields} method to the bytecode.
+ */
+ private void addCopyFieldsMethod()
+ throws NoSuchMethodException {
+ // public void pcCopyField (Object pc, int field)
+ BCMethod method = _pc.declareMethod(PRE + "CopyField",
+ void.class.getName(),
+ new String[]{ _managedType.getName(), int.class.getName() });
+ method.makeProtected();
+ Code code = method.getCode(true);
+
+ // adds everything through the switch ()
+ int relLocal = beginSwitchMethod(PRE + "CopyField", code);
+
+ // if no fields in this inst, just throw exception
+ FieldMetaData[] fmds = getCreateSubclass() ? _meta.getFields()
+ : _meta.getDeclaredFields();
+ if (fmds.length == 0)
+ throwException(code, IllegalArgumentException.class);
+ else {
+ // switch (val)
+ code.iload().setLocal(relLocal);
+ TableSwitchInstruction tabins = code.tableswitch();
+ tabins.setLow(0);
+ tabins.setHigh(fmds.length - 1);
+
+ for (int i = 0; i < fmds.length; i++) {
+ // <field> = other.<field>;
+ // or set<field> (other.get<field>);
+ tabins.addTarget(loadManagedInstance(code, false, fmds[i]));
+ code.aload().setParam(0);
+ addGetManagedValueCode(code, fmds[i], false);
+ addSetManagedValueCode(code, fmds[i]);
+
+ // break;
+ code.vreturn();
+ }
+
+ // default: throw new IllegalArgumentException ()
+ tabins.setDefaultTarget(throwException
+ (code, IllegalArgumentException.class));
+ }
+
+ code.calculateMaxStack();
+ code.calculateMaxLocals();
+
+ addMultipleFieldsMethodVersion(method);
+ }
+
+ /**
+ * Helper method to add the code common to the beginning of both the
+ * pcReplaceField method and the pcProvideField method. This includes
+ * calculating the relative field number of the desired field and calling
+ * the superclass if necessary.
+ *
+ * @return the index in which the local variable holding the relative
+ * field number is stored
+ */
+ private int beginSwitchMethod(String name, Code code) {
+ boolean copy = (PRE + "CopyField").equals(name);
+ int fieldNumber = (copy) ? 1 : 0;
+
+ int relLocal = code.getNextLocalsIndex();
+ if (getCreateSubclass()) {
+ code.iload().setParam(fieldNumber);
+ code.istore().setLocal(relLocal);
+ return relLocal;
+ }
+
+ // int rel = fieldNumber - pcInheritedFieldCount
+ code.iload().setParam(fieldNumber);
+ code.getstatic().setField(INHERIT, int.class);
+ code.isub();
+ code.istore().setLocal(relLocal);
+ code.iload().setLocal(relLocal);
+
+ // super: if (rel < 0) super.pcReplaceField (fieldNumber); return;
+ // no super: if (rel < 0) throw new IllegalArgumentException ();
+ JumpInstruction ifins = code.ifge();
+ if (_meta.getPCSuperclass() != null) {
+ loadManagedInstance(code, false);
+ String[] args;
+ if (copy) {
+ args = new String[]{ getType(_meta.getPCSuperclassMetaData()).
+ getName(), int.class.getName() };
+ code.aload().setParam(0);
+ } else
+ args = new String[]{ int.class.getName() };
+ code.iload().setParam(fieldNumber);
+ code.invokespecial().setMethod(getType(_meta.
+ getPCSuperclassMetaData()).getName(), name,
+ void.class.getName(), args);
+ code.vreturn();
+ } else
+ throwException(code, IllegalArgumentException.class);
+
+ ifins.setTarget(code.nop());
+ return relLocal;
+ }
+
+ /**
+ * This helper method, given the pcReplaceField or pcProvideField
+ * method, adds the bytecode for the corresponding 'plural' version
+ * of the method -- the version that takes an int[] of fields to
+ * to access rather than a single field. The multiple fields version
+ * simply loops through the provided indexes and delegates to the
+ * singular version for each one.
+ */
+ private void addMultipleFieldsMethodVersion(BCMethod single) {
+ boolean copy = (PRE + "CopyField").equals(single.getName());
+
+ // public void <method>s (int[] fields)
+ Class[] args = (copy) ? new Class[]{ Object.class, int[].class }
+ : new Class[]{ int[].class };
+ BCMethod method = _pc.declareMethod(single.getName() + "s",
+ void.class, args);
+ Code code = method.getCode(true);
+
+ int fieldNumbers = 0;
+ int inst = 0;
+ if (copy) {
+ fieldNumbers = 1;
+
+ if (getCreateSubclass()) {
+ // get the managed instance into the local variable table
+ code.aload().setParam(0);
+ code.invokestatic().setMethod(ImplHelper.class,
+ "getManagedInstance", Object.class,
+ new Class[] { Object.class });
+ code.checkcast().setType(_managedType);
+ inst = code.getNextLocalsIndex();
+ code.astore().setLocal(inst);
+
+ // there might be a difference between the classes of 'this'
+ // vs 'other' in this context; use the PC methods to get the SM
+ code.aload().setParam(0);
+ code.aload().setThis();
+ code.getfield().setField(SM, SMTYPE);
+ code.invokestatic().setMethod(ImplHelper.class,
+ "toPersistenceCapable", PersistenceCapable.class,
+ new Class[] { Object.class, Object.class });
+ code.invokeinterface().setMethod(PersistenceCapable.class,
+ "pcGetStateManager", StateManager.class, null);
+ } else {
+ // XXX other = (XXX) pc;
+ code.aload().setParam(0);
+ code.checkcast().setType(_pc);
+ inst = code.getNextLocalsIndex();
+ code.astore().setLocal(inst);
+
+ // access the other's sm field directly
+ code.aload().setLocal(inst);
+ code.getfield().setField(SM, SMTYPE);
+ }
+
+ // if (other.pcStateManager != pcStateManager)
+ // throw new IllegalArgumentException
+
+ loadManagedInstance(code, false);
+ code.getfield().setField(SM, SMTYPE);
+ JumpInstruction ifins = code.ifacmpeq();
+ throwException(code, IllegalArgumentException.class);
+ ifins.setTarget(code.nop());
+
+ // if (pcStateManager == null)
+ // throw new IllegalStateException
+ loadManagedInstance(code, false);
+ code.getfield().setField(SM, SMTYPE);
+ ifins = code.ifnonnull();
+ throwException(code, IllegalStateException.class);
+ ifins.setTarget(code.nop());
+ }
+
+ // for (int i = 0;
+ code.constant().setValue(0);
+ int idx = code.getNextLocalsIndex();
+ code.istore().setLocal(idx);
+ JumpInstruction testins = code.go2();
+
+ // <method> (fields[i]);
+ Instruction bodyins = loadManagedInstance(code, false);
+ if (copy)
+ code.aload().setLocal(inst);
+ code.aload().setParam(fieldNumbers);
+ code.iload().setLocal(idx);
+ code.iaload();
+ code.invokevirtual().setMethod(single);
+
+ // i++;
+ code.iinc().setIncrement(1).setLocal(idx);
+
+ // i < fields.length
+ testins.setTarget(code.iload().setLocal(idx));
+ code.aload().setParam(fieldNumbers);
+ code.arraylength();
+ code.ificmplt().setTarget(bodyins);
+ code.vreturn();
+
+ code.calculateMaxStack();
+ code.calculateMaxLocals();
+ }
+
+ /**
+ * Adds the 'stock' methods to the bytecode; these include methods
+ * like {@link PersistenceCapable#pcFetchObjectId}
+ * and {@link PersistenceCapable#pcIsTransactional}.
+ */
+ private void addStockMethods()
+ throws NoSuchMethodException {
+ try {
+ // pcGetGenericContext
+ translateFromStateManagerMethod(
+ AccessController.doPrivileged(
+ J2DoPrivHelper.getDeclaredMethodAction(
+ SMTYPE, "get" + CONTEXTNAME, (Class[]) null)), false);
+
+ // pcFetchObjectId
+ translateFromStateManagerMethod(
+ AccessController.doPrivileged(
+ J2DoPrivHelper.getDeclaredMethodAction(
+ SMTYPE, "fetchObjectId", (Class[]) null)), false);
+
+ // pcIsDeleted
+ translateFromStateManagerMethod(
+ AccessController.doPrivileged(
+ J2DoPrivHelper.getDeclaredMethodAction(
+ SMTYPE, "isDeleted", (Class[]) null)), false);
+
+ // pcIsDirty
+ translateFromStateManagerMethod(
+ AccessController.doPrivileged(
+ J2DoPrivHelper.getDeclaredMethodAction(
+ SMTYPE, "isDirty", (Class[]) null)), true);
+
+ // pcIsNew
+ translateFromStateManagerMethod(
+ AccessController.doPrivileged(
+ J2DoPrivHelper.getDeclaredMethodAction(
+ SMTYPE, "isNew", (Class[]) null)), false);
+
+ // pcIsPersistent
+ translateFromStateManagerMethod(
+ AccessController.doPrivileged(
+ J2DoPrivHelper.getDeclaredMethodAction(
+ SMTYPE, "isPersistent", (Class[]) null)), false);
+
+ // pcIsTransactional
+ translateFromStateManagerMethod(
+ AccessController.doPrivileged(
+ J2DoPrivHelper.getDeclaredMethodAction(
+ SMTYPE, "isTransactional", (Class[]) null)), false);
+
+ // pcSerializing
+ translateFromStateManagerMethod(
+ AccessController.doPrivileged(
+ J2DoPrivHelper.getDeclaredMethodAction(
+ SMTYPE, "serializing", (Class[]) null)), false);
+
+ // pcDirty
+ translateFromStateManagerMethod(
+ AccessController.doPrivileged(
+ J2DoPrivHelper.getDeclaredMethodAction(
+ SMTYPE, "dirty", new Class[]{ String.class })), false);
+
+ // pcGetStateManager
+ BCMethod meth = _pc.declareMethod(PRE + "GetStateManager",
+ StateManager.class, null);
+ Code code = meth.getCode(true);
+ loadManagedInstance(code, false);
+ code.getfield().setField(SM, StateManager.class);
+ code.areturn();
+ code.calculateMaxStack();
+ code.calculateMaxLocals();
+ } catch (PrivilegedActionException pae) {
+ throw (NoSuchMethodException) pae.getException();
+ }
+ }
+
+ /**
+ * Helper method to add a stock method to the bytecode. Each
+ * stock method simply delegates to a corresponding StateManager method.
+ * Given the StateManager method, then, this function translates it into
+ * the wrapper method that should be added to the bytecode.
+ */
+ private void translateFromStateManagerMethod(Method m,
+ boolean isDirtyCheckMethod) {
+ // form the name of the method by prepending 'pc' to the sm method
+ String name = PRE + StringUtil.capitalize(m.getName());
+ Class[] params = m.getParameterTypes();
+ Class returnType = m.getReturnType();
+
+ // add the method to the pc
+ BCMethod method = _pc.declareMethod(name, returnType, params);
+ Code code = method.getCode(true);
+
+ // if (pcStateManager == null) return <default>;
+ loadManagedInstance(code, false);
+ code.getfield().setField(SM, SMTYPE);
+ JumpInstruction ifins = code.ifnonnull();
+ if (returnType.equals(boolean.class))
+ code.constant().setValue(false);
+ else if (!returnType.equals(void.class))
+ code.constant().setNull();
+ code.xreturn().setType(returnType);
+
+ // if this is the dirty-check method and we're subclassing but not
+ // redefining, hook into PCHelper to do the dirty check
+ if (isDirtyCheckMethod && !getRedefine()) {
+ // RedefinitionHelper.dirtyCheck(sm);
+ ifins.setTarget(loadManagedInstance(code, false));
+ code.getfield().setField(SM, SMTYPE);
+ code.dup(); // for the return statement below
+ code.invokestatic().setMethod(RedefinitionHelper.class,
+ "dirtyCheck", void.class, new Class[] { SMTYPE });
+ } else {
+ ifins.setTarget(loadManagedInstance(code, false));
+ code.getfield().setField(SM, SMTYPE);
+ }
+
+ // return pcStateManager.<method> (<args>);
+ // managed instance loaded above in if-else block
+ for (int i = 0; i < params.length; i++)
+ code.xload().setParam(i);
+ code.invokeinterface().setMethod(m);
+ code.xreturn().setType(returnType);
+
+ code.calculateMaxStack();
+ code.calculateMaxLocals();
+ }
+
+ /**
+ * Adds the {@link PersistenceCapable#pcGetVersion} method to the bytecode.
+ */
+ private void addGetVersionMethod()
+ throws NoSuchMethodException {
+ BCMethod method = _pc.declareMethod(PRE + "GetVersion", Object.class,
+ null);
+ Code code = method.getCode(true);
+
+ // if (pcStateManager == null)
+ loadManagedInstance(code, false);
+ code.getfield().setField(SM, SMTYPE);
+ JumpInstruction ifins = code.ifnonnull();
+ FieldMetaData versionField = _meta.getVersionField();
+
+ if (versionField == null)
+ code.constant().setNull(); // return null;
+ else {
+ // return <versionField>;
+ Class wrapper = toPrimitiveWrapper(versionField);
+ if (wrapper != versionField.getDeclaredType()) {
+ code.anew().setType(wrapper);
+ code.dup();
+ }
+ loadManagedInstance(code, false);
+ addGetManagedValueCode(code, versionField);
+ if (wrapper != versionField.getDeclaredType())
+ code.invokespecial().setMethod(wrapper, "<init>", void.class,
+ new Class[]{ versionField.getDeclaredType() });
+ }
+ code.areturn();
+
+ // return pcStateManager.getVersion ();
+ ifins.setTarget(loadManagedInstance(code, false));
+ code.getfield().setField(SM, SMTYPE);
+ code.invokeinterface().setMethod(SMTYPE, "getVersion", Object.class,
+ null);
+ code.areturn();
+
+ code.calculateMaxStack();
+ code.calculateMaxLocals();
+ }
+
+ /**
+ * Return the version field type as a primitive wrapper, or null if
+ * the version field is not primitive.
+ */
+ private Class toPrimitiveWrapper(FieldMetaData fmd) {
+ switch (fmd.getDeclaredTypeCode()) {
+ case JavaTypes.BOOLEAN:
+ return Boolean.class;
+ case JavaTypes.BYTE:
+ return Byte.class;
+ case JavaTypes.CHAR:
+ return Character.class;
+ case JavaTypes.DOUBLE:
+ return Double.class;
+ case JavaTypes.FLOAT:
+ return Float.class;
+ case JavaTypes.INT:
+ return Integer.class;
+ case JavaTypes.LONG:
+ return Long.class;
+ case JavaTypes.SHORT:
+ return Short.class;
+ }
+ return fmd.getDeclaredType();
+ }
+
+ /**
+ * Adds the {@link PersistenceCapable#pcReplaceStateManager}
+ * method to the bytecode.
+ */
+ private void addReplaceStateManagerMethod() {
+ // public void pcReplaceStateManager (StateManager sm)
+ BCMethod method = _pc.declareMethod(PRE + "ReplaceStateManager",
+ void.class, new Class[]{ SMTYPE });
+ method.getExceptions(true).addException(SecurityException.class);
+ Code code = method.getCode(true);
+
+ // if (pcStateManager != null)
+ // pcStateManager = pcStateManager.replaceStateManager(sm);
+ loadManagedInstance(code, false);
+ code.getfield().setField(SM, SMTYPE);
+ JumpInstruction ifins = code.ifnull();
+ loadManagedInstance(code, false);
+ loadManagedInstance(code, false);
+ code.getfield().setField(SM, SMTYPE);
+ code.aload().setParam(0);
+ code.invokeinterface().setMethod(SMTYPE, "replaceStateManager",
+ SMTYPE, new Class[]{ SMTYPE });
+ code.putfield().setField(SM, SMTYPE);
+ code.vreturn();
+
+ // SecurityManager sec = System.getSecurityManager ();
+ // if (sec != null)
+ // sec.checkPermission (Permission.SET_STATE_MANAGER);
+ ifins.setTarget(code.invokestatic().setMethod(System.class,
+ "getSecurityManager", SecurityManager.class, null));
+
+ // pcStateManager = sm;
+ ifins.setTarget(loadManagedInstance(code, false));
+ code.aload().setParam(0);
+ code.putfield().setField(SM, SMTYPE);
+ code.vreturn();
+
+ code.calculateMaxStack();
+ code.calculateMaxLocals();
+ }
+
+ /**
+ * Creates the PersistenceCapable methods dealing with application
+ * identity and gives them no-op implementations.
+ */
+ private void addNoOpApplicationIdentityMethods() {
+ // public void pcCopyKeyFieldsToObjectId (ObjectIdFieldSupplier fs,
+ // Object oid)
+ BCMethod method = _pc.declareMethod(PRE + "CopyKeyFieldsToObjectId",
+ void.class, new Class[]{ OIDFSTYPE, Object.class });
+ Code code = method.getCode(true);
+ code.vreturn();
+ code.calculateMaxLocals();
+
+ // public void pcCopyKeyFieldsToObjectId (Object oid)
+ method = _pc.declareMethod(PRE + "CopyKeyFieldsToObjectId",
+ void.class, new Class[]{ Object.class });
+ code = method.getCode(true);
+ code.vreturn();
+ code.calculateMaxLocals();
+
+ // public void pcCopyKeyFieldsFromObjectId (ObjectIdFieldConsumer fc,
+ // Object oid)
+ method = _pc.declareMethod(PRE + "CopyKeyFieldsFromObjectId",
+ void.class, new Class[]{ OIDFCTYPE, Object.class });
+ code = method.getCode(true);
+ code.vreturn();
+ code.calculateMaxLocals();
+
+ // public void pcCopyKeyFieldsFromObjectId (Object oid)
+ method = _pc.declareMethod(PRE + "CopyKeyFieldsFromObjectId",
+ void.class, new Class[]{ Object.class });
+ code = method.getCode(true);
+ code.vreturn();
+ code.calculateMaxLocals();
+
+ // public Object pcNewObjectIdInstance ()
+ method = _pc.declareMethod(PRE + "NewObjectIdInstance",
+ Object.class, null);
+ code = method.getCode(true);
+ code.constant().setNull();
+ code.areturn();
+ code.calculateMaxStack();
+ code.calculateMaxLocals();
+
+ // public Object pcNewObjectIdInstance (Object obj)
+ method = _pc.declareMethod(PRE + "NewObjectIdInstance",
+ Object.class, new Class[]{ Object.class });
+ code = method.getCode(true);
+ code.constant().setNull();
+ code.areturn();
+ code.calculateMaxStack();
+ code.calculateMaxLocals();
+ }
+
+ /**
+ * Adds the <code>pcCopyKeyFieldsToObjectId</code> methods
+ * to classes using application identity.
+ */
+ private void addCopyKeyFieldsToObjectIdMethod(boolean fieldManager)
+ throws NoSuchMethodException {
+ // public void pcCopyKeyFieldsToObjectId (ObjectIdFieldSupplier fs,
+ // Object oid)
+ String[] args = (fieldManager) ?
+ new String[]{ OIDFSTYPE.getName(), Object.class.getName() }
+ : new String[]{ Object.class.getName() };
+ BCMethod method = _pc.declareMethod(PRE + "CopyKeyFieldsToObjectId",
+ void.class.getName(), args);
+ Code code = method.getCode(true);
+
+ // single field identity always throws exception
+ if (_meta.isOpenJPAIdentity()) {
+ throwException(code, INTERNEXCEP);
+
+ code.calculateMaxStack();
+ code.calculateMaxLocals();
+ return;
+ }
+
+ // call superclass method
+ if (_meta.getPCSuperclass() != null && !getCreateSubclass()) {
+ loadManagedInstance(code, false);
+ for (int i = 0; i < args.length; i++)
+ code.aload().setParam(i);
+ code.invokespecial().setMethod(getType(_meta.
+ getPCSuperclassMetaData()).getName(),
+ PRE + "CopyKeyFieldsToObjectId", void.class.getName(), args);
+ }
+
+ // Object id = oid;
+ if (fieldManager)
+ code.aload().setParam(1);
+ else
+ code.aload().setParam(0);
+
+ if (_meta.isObjectIdTypeShared()) {
+ // oid = ((ObjectId) id).getId ();
+ code.checkcast().setType(ObjectId.class);
+ code.invokevirtual().setMethod(ObjectId.class, "getId",
+ Object.class, null);
+ }
+
+ // <oid type> id = (<oid type>) oid;
+ int id = code.getNextLocalsIndex();
+ Class oidType = _meta.getObjectIdType();
+ code.checkcast().setType(oidType);
+ code.astore().setLocal(id);
+
+ // int inherited = pcInheritedFieldCount;
+ int inherited = 0;
+ if (fieldManager) {
+ code.getstatic().setField(INHERIT, int.class);
+ inherited = code.getNextLocalsIndex();
+ code.istore().setLocal(inherited);
+ }
+
+ // id.<field> = fs.fetch<type>Field (<index>); or...
+ // id.<field> = pc.<field>;
+ FieldMetaData[] fmds = getCreateSubclass() ? _meta.getFields()
+ : _meta.getDeclaredFields();
+ Class<?> type;
+ String name;
+ Field field;
+ Method setter;
+ boolean reflect;
+ // If optimizeIdCopy is enabled and not a field manager method, try to
+ // optimize the copyTo by using a public constructor instead of reflection
+ if (_optimizeIdCopy) {
+ ArrayList<Integer> pkfields = optimizeIdCopy(oidType, fmds);
+ if (pkfields != null) {
+ // search for a constructor on the IdClass that can be used
+ // to construct the IdClass
+ int parmOrder[] = getIdClassConstructorParmOrder(oidType, pkfields, fmds);
+ if (parmOrder != null) {
+ // If using a field manager, values must be loaded into locals so they can be properly ordered
+ // as constructor parameters.
+ int[] localIndexes = new int[fmds.length];
+ if (fieldManager) {
+ for (int k = 0; k < fmds.length; k++) {
+ if (!fmds[k].isPrimaryKey())
+ continue;
+ code.aload().setParam(0);
+ code.constant().setValue(k);
+ code.iload().setLocal(inherited);
+ code.iadd();
+ code.invokeinterface().setMethod(getFieldSupplierMethod(fmds[k].getObjectIdFieldType()));
+ localIndexes[k] = code.getNextLocalsIndex();
+ storeLocalValue(code, localIndexes[k], fmds[k].getObjectIdFieldTypeCode());
+ }
+ }
+
+ // found a matching constructor. parm array is constructor parm order
+ code.anew().setType(oidType);
+ code.dup();
+ // build the parm list in order
+ Class<?>[] clsArgs = new Class<?>[parmOrder.length];
+ for (int i = 0; i < clsArgs.length; i++) {
+ int parmIndex = parmOrder[i];
+ clsArgs[i] = fmds[parmIndex].getObjectIdFieldType();
+ if (!fieldManager) {
+ loadManagedInstance(code, false);
+ addGetManagedValueCode(code, fmds[parmIndex]);
+ } else {
+ // Load constructor parameters in appropriate order
+ loadLocalValue(code, localIndexes[parmIndex], fmds[parmIndex].getObjectIdFieldTypeCode());
+ if (fmds[parmIndex].getObjectIdFieldTypeCode() == JavaTypes.OBJECT &&
+ !fmds[parmIndex].getDeclaredType().isEnum()) {
+ code.checkcast().setType(ObjectId.class);
+ code.invokevirtual().setMethod(ObjectId.class, "getId",
+ Object.class, null);
+ }
+ // if the type of this field meta data is
+ // non-primitive and non-string, be sure to cast
+ // to the appropriate type.
+ if (!clsArgs[i].isPrimitive()
+ && !clsArgs[i].getName().equals(String.class.getName()))
+ code.checkcast().setType(clsArgs[i]);
+ }
+ }
+ // invoke the public constructor to create a new local id
+ code.invokespecial().setMethod(oidType, "<init>", void.class, clsArgs);
+ int ret = code.getNextLocalsIndex();
+ code.astore().setLocal(ret);
+
+ // swap out the app id with the new one
+ code.aload().setLocal( fieldManager ? 2 : 1);
+ code.checkcast().setType(ObjectId.class);
+ code.aload().setLocal(ret);
+ code.invokestatic().setMethod(ApplicationIds.class,
+ "setAppId", void.class, new Class[] { ObjectId.class,
+ Object.class });
+ code.vreturn();
+
+ code.calculateMaxStack();
+ code.calculateMaxLocals();
+ return;
+ }
+ }
+ }
+
+ for (int i = 0; i < fmds.length; i++) {
+ if (!fmds[i].isPrimaryKey())
+ continue;
+ code.aload().setLocal(id);
+
+ name = fmds[i].getName();
+ type = fmds[i].getObjectIdFieldType();
+ if (isFieldAccess(fmds[i])) {
+ setter = null;
+ field = Reflection.findField(oidType, name, true);
+ reflect = !Modifier.isPublic(field.getModifiers());
+ if (reflect) {
+ code.classconstant().setClass(oidType);
+ code.constant().setValue(name);
+ code.constant().setValue(true);
+ code.invokestatic().setMethod(Reflection.class,
+ "findField", Field.class, new Class[] { Class.class,
+ String.class, boolean.class });
+ }
+ } else {
+ field = null;
+ setter = Reflection.findSetter(oidType, name, type, true);
+ reflect = !Modifier.isPublic(setter.getModifiers());
+ if (reflect) {
+ code.classconstant().setClass(oidType);
+ code.constant().setValue(name);
+ code.classconstant().setClass(type);
+ code.constant().setValue(true);
+ code.invokestatic().setMethod(Reflection.class,
+ "findSetter", Method.class, new Class[] { Class.class,
+ String.class, Class.class, boolean.class });
+ }
+ }
+
+ if (fieldManager) {
+ code.aload().setParam(0);
+ code.constant().setValue(i);
+ code.iload().setLocal(inherited);
+ code.iadd();
+ code.invokeinterface().setMethod
+ (getFieldSupplierMethod(type));
+ if (fmds[i].getObjectIdFieldTypeCode() == JavaTypes.OBJECT &&
+ !fmds[i].getDeclaredType().isEnum()) {
+ code.checkcast().setType(ObjectId.class);
+ code.invokevirtual().setMethod(ObjectId.class, "getId",
+ Object.class, null);
+ }
+
+ // if the type of this field meta data is
+ // non-primitive and non-string, be sure to cast
+ // to the appropriate type.
+ if (!reflect && !type.isPrimitive()
+ && !type.getName().equals(String.class.getName()))
+ code.checkcast().setType(type);
+ } else {
+ loadManagedInstance(code, false);
+ addGetManagedValueCode(code, fmds[i]);
+
+ // get id/pk from pc instance
+ if (fmds[i].getDeclaredTypeCode() == JavaTypes.PC)
+ addExtractObjectIdFieldValueCode(code, fmds[i]);
+ }
+
+ if (reflect && field != null) {
+ code.invokestatic().setMethod(Reflection.class, "set",
+ void.class, new Class[] { Object.class, Field.class,
+ (type.isPrimitive()) ? type : Object.class });
+ } else if (reflect) {
+ code.invokestatic().setMethod(Reflection.class, "set",
+ void.class, new Class[] { Object.class, Method.class,
+ (type.isPrimitive()) ? type : Object.class });
+ } else if (field != null)
+ code.putfield().setField(field);
+ else
+ code.invokevirtual().setMethod(setter);
+ }
+ code.vreturn();
+
+ code.calculateMaxStack();
+ code.calculateMaxLocals();
+ }
+
+ /**
+ * Adds the appropriate load method for the given type and local
+ * index.
+ */
+ private void loadLocalValue(Code code, int locidx, int typeCode) {
+ switch (typeCode) {
+ case JavaTypes.CHAR:
+ case JavaTypes.BYTE:
+ case JavaTypes.SHORT:
+ case JavaTypes.INT:
+ code.iload().setLocal(locidx);
+ break;
+ case JavaTypes.DOUBLE:
+ code.dload().setLocal(locidx);
+ break;
+ case JavaTypes.FLOAT:
+ code.fload().setLocal(locidx);
+ break;
+ case JavaTypes.LONG:
+ code.lload().setLocal(locidx);
+ break;
+ default:
+ code.aload().setLocal(locidx);
+ break;
+ }
+ }
+
+ /**
+ * Adds the appropriate store method for the given type and local
+ * index.
+ */
+ private void storeLocalValue(Code code, int locidx, int typeCode) {
+ switch (typeCode) {
+ case JavaTypes.CHAR:
+ case JavaTypes.BYTE:
+ case JavaTypes.SHORT:
+ case JavaTypes.INT:
+ code.istore().setLocal(locidx);
+ break;
+ case JavaTypes.DOUBLE:
+ code.dstore().setLocal(locidx);
+ break;
+ case JavaTypes.FLOAT:
+ code.fstore().setLocal(locidx);
+ break;
+ case JavaTypes.LONG:
+ code.lstore().setLocal(locidx);
+ break;
+ default:
+ code.astore().setLocal(locidx);
+ break;
+ }
+ }
+
+ /**
+ * Add code to extract the id of the given primary key relation field for
+ * setting into an objectid instance.
+ */
+ private void addExtractObjectIdFieldValueCode(Code code, FieldMetaData pk) {
+ // if (val != null)
+ // val = ((PersistenceCapable) val).pcFetchObjectId();
+ int pc = code.getNextLocalsIndex();
+ code.astore().setLocal(pc);
+ code.aload().setLocal(pc);
+ JumpInstruction ifnull1 = code.ifnull();
+ code.aload().setLocal(pc);
+ code.checkcast().setType(PersistenceCapable.class);
+ if (!pk.getTypeMetaData().isOpenJPAIdentity())
+ code.invokeinterface().setMethod(PersistenceCapable.class,
+ PRE + "FetchObjectId", Object.class, null);
+ else
+ code.invokeinterface().setMethod(PersistenceCapable.class,
[... 2727 lines stripped ...]