Modified: openjpa/branches/fb-3.0-asm/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCEnhancer.java URL: http://svn.apache.org/viewvc/openjpa/branches/fb-3.0-asm/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCEnhancer.java?rev=1760580&r1=1760579&r2=1760580&view=diff ============================================================================== --- openjpa/branches/fb-3.0-asm/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCEnhancer.java (original) +++ openjpa/branches/fb-3.0-asm/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCEnhancer.java Tue Sep 13 17:29:01 2016 @@ -1,4745 +1,46 @@ /* - * 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 + * 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 + * 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. + * 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.conf.OpenJPAConfigurationImpl; import org.apache.openjpa.lib.conf.Configurations; -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.Options; -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 PCEnhancer { - // 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 int ENHANCE_NONE = 0; - public static final int ENHANCE_AWARE = 2 << 0; - public static final int ENHANCE_INTERFACE = 2 << 1; - public static final int ENHANCE_PC = 2 << 2; - - 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 AuxiliaryEnhancer[] _auxEnhancers; - static { - Class[] classes = Services.getImplementorClasses( - AuxiliaryEnhancer.class, - AccessController.doPrivileged( - J2DoPrivHelper.getClassLoaderAction(AuxiliaryEnhancer.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 = (AuxiliaryEnhancer[]) auxEnhancers.toArray - (new AuxiliaryEnhancer[auxEnhancers.size()]); - - int rev = 0; - Properties revisionProps = new Properties(); - try { - InputStream in = PCEnhancer.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. - ENHANCER_VERSION = 2; - } - } - - 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 PCEnhancer(OpenJPAConfiguration conf, Class type) { - this(conf, AccessController.doPrivileged(J2DoPrivHelper - .loadProjectClassAction(new Project(), type)), - (MetaDataRepository) null); - } - - /** - * Constructor. Supply configuration and type to enhance. This will look - * up the metadata for <code>meta</code> by converting back to a class - * and then loading from <code>conf</code>'s repository. - */ - public PCEnhancer(OpenJPAConfiguration conf, ClassMetaData meta) { - this(conf, AccessController.doPrivileged(J2DoPrivHelper - .loadProjectClassAction(new Project(), meta.getDescribedType())), - meta.getRepository()); - } - - /** - * 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 #PCEnhancer(OpenJPAConfiguration, BCClass, - MetaDataRepository, ClassLoader)} instead. - */ - public PCEnhancer(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 PCEnhancer(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 PCEnhancer(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(PCEnhancer.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(PCEnhancer.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(PCEnhancer.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 PCEnhancer(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; - } - - /** - * Return the current {@link BytecodeWriter} to write to or null if none. - */ - public BytecodeWriter getBytecodeWriter() { - return _writer; - } - - /** - * 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 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 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 ENHANCE_PC; - } - return 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() ? - PCEnhancer.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. - */
[... 2923 lines stripped ...]
