blob: 7845da5a9c074737d642b0927e563d7328662677 [file] [log] [blame]
/*
* 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.util.BitSet;
import java.util.Collection;
import java.util.LinkedList;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.openjpa.conf.OpenJPAConfiguration;
import org.apache.openjpa.kernel.AbstractPCData;
import org.apache.openjpa.kernel.FetchConfiguration;
import org.apache.openjpa.kernel.OpenJPAStateManager;
import org.apache.openjpa.kernel.PCData;
import org.apache.openjpa.kernel.StoreContext;
import org.apache.openjpa.lib.log.Log;
import org.apache.openjpa.lib.util.Localizer;
import org.apache.openjpa.lib.util.StringUtil;
import org.apache.openjpa.meta.ClassMetaData;
import org.apache.openjpa.meta.FieldMetaData;
import org.apache.openjpa.meta.JavaTypes;
import org.apache.openjpa.util.InternalException;
import serp.bytecode.BCClass;
import serp.bytecode.BCField;
import serp.bytecode.BCMethod;
import serp.bytecode.Code;
import serp.bytecode.Constants;
import serp.bytecode.ExceptionHandler;
import serp.bytecode.Instruction;
import serp.bytecode.JumpInstruction;
import serp.bytecode.LookupSwitchInstruction;
/**
* Generates {@link PCData} instances which avoid primitive wrappers
* to optimize memory use and performance at the cost of slightly higher
* startup time.
*
* @author Steve Kim
* @since 0.3.2
*/
public class PCDataGenerator
extends DynamicStorageGenerator {
private static final Localizer _loc = Localizer.forPackage
(PCDataGenerator.class);
protected static final String POSTFIX = "$openjpapcdata";
private final Map<Class<?>, DynamicStorage> _generated = new ConcurrentHashMap<>();
private final OpenJPAConfiguration _conf;
private final Log _log;
public PCDataGenerator(OpenJPAConfiguration conf) {
_conf = conf;
_log = _conf.getLogFactory().getLog(OpenJPAConfiguration.LOG_ENHANCE);
}
/**
* Return the configuration.
*/
public OpenJPAConfiguration getConfiguration() {
return _conf;
}
/**
* Return a {@link PCData} instance for the given oid and metadata.
*/
public PCData generatePCData(Object oid, ClassMetaData meta) {
if (meta == null)
return null;
Class<?> type = meta.getDescribedType();
DynamicStorage storage = _generated.get(type);
if (storage == null) {
storage = generateStorage(meta);
_generated.put(type, storage);
if (_log.isTraceEnabled())
_log.trace(_loc.get("pcdata-created", type.getName(), meta));
}
DynamicPCData data = (DynamicPCData) storage.newInstance();
data.setId(oid);
data.setStorageGenerator(this);
finish(data, meta);
return data;
}
/**
* Actually generate the factory instance.
*/
private DynamicStorage generateStorage(ClassMetaData meta) {
if (_log.isTraceEnabled())
_log.trace(_loc.get("pcdata-generate", meta));
FieldMetaData[] fields = meta.getFields();
int[] types = new int[fields.length];
for (int i = 0; i < types.length; i++)
types[i] = replaceType(fields[i]);
return generateStorage(types, meta);
}
/**
* Perform any final actions before the pcdata is returned to client code.
*/
protected void finish(DynamicPCData data, ClassMetaData meta) {
}
@Override
protected int getCreateFieldMethods(int typeCode) {
if (typeCode >= JavaTypes.OBJECT)
return POLICY_SILENT;
// don't bother creating set/get<Primitive> methods
return POLICY_EMPTY;
}
@Override
protected void declareClasses(BCClass bc) {
super.declareClasses(bc);
bc.declareInterface(DynamicPCData.class);
bc.setSuperclass(AbstractPCData.class);
}
@Override
protected final String getClassName(Object obj) {
return getUniqueName(((ClassMetaData) obj).getDescribedType());
}
/**
* Creates a unique name for the given type's pcdata implementation.
*/
protected String getUniqueName(Class<?> type) {
return type.getName() + "$" + System.identityHashCode(type) + POSTFIX;
}
@Override
protected final void decorate(Object obj, BCClass bc, int[] types) {
super.decorate(obj, bc, types);
ClassMetaData meta = (ClassMetaData) obj;
enhanceConstructor(bc);
addBaseFields(bc);
addImplDataMethods(bc, meta);
addFieldImplDataMethods(bc, meta);
addVersionMethods(bc);
addGetType(bc, meta);
addLoadMethod(bc, meta);
addLoadWithFieldsMethod(bc, meta);
addStoreMethods(bc, meta);
addNewEmbedded(bc);
addGetData(bc);
decorate(bc, meta);
}
/**
* Apply additional decoration to generated class.
*/
protected void decorate(BCClass bc, ClassMetaData meta) {
}
/**
* Enhance constructor to initialize fields
*/
private void enhanceConstructor(BCClass bc) {
BCMethod cons = bc.getDeclaredMethod("<init>", (String[]) null);
Code code = cons.getCode(false);
code.afterLast();
code.previous();
// private BitSet loaded = new BitSet();
BCField loaded = addBeanField(bc, "loaded", BitSet.class);
loaded.setFinal(true);
code.aload().setThis();
code.anew().setType(BitSet.class);
code.dup();
code.constant().setValue(bc.getFields().length);
code.invokespecial().setMethod(BitSet.class, "<init>", void.class,
new Class[]{ int.class });
code.putfield().setField(loaded);
code.calculateMaxStack();
code.calculateMaxLocals();
}
/**
* Have to load the type since it may not be available to the
* same classloader (i.e. rar vs. ear). The context classloader
* (i.e. the user app classloader) should be fine.
*/
private void addGetType(BCClass bc, ClassMetaData meta) {
BCField type = bc.declareField("type", Class.class);
type.setStatic(true);
type.makePrivate();
// public Class getType() {
BCMethod getter = bc.declareMethod("getType", Class.class, null);
getter.makePublic();
Code code = getter.getCode(true);
// if (type == null) {
// try {
// type = Class.forName
// (meta.getDescribedType().getName(), true,
// Thread.currentThread().getContextClassLoader());
// } catch (ClassNotFoundException cnfe) {
// throw new InternalException();
// }
// }
code.getstatic().setField(type);
Collection<Instruction> jumps = new LinkedList<>();
jumps.add(code.ifnonnull());
ExceptionHandler handler = code.addExceptionHandler();
handler.setTryStart(code.constant().setValue
(meta.getDescribedType().getName()));
code.constant().setValue(true);
code.invokestatic().setMethod(Thread.class, "currentThread",
Thread.class, null);
code.invokevirtual().setMethod(Thread.class, "getContextClassLoader",
ClassLoader.class, null);
code.invokestatic().setMethod(Class.class, "forName", Class.class,
new Class[]{ String.class, boolean.class, ClassLoader.class });
code.putstatic().setField(type);
Instruction go2 = code.go2();
jumps.add(go2);
handler.setTryEnd(go2);
handler.setCatch(ClassNotFoundException.class);
handler.setHandlerStart(throwException
(code, InternalException.class));
setTarget(code.getstatic().setField(type), jumps);
code.areturn();
code.calculateMaxStack();
code.calculateMaxLocals();
}
/**
* Declare standard dynamic pcdata fields.
*/
private void addBaseFields(BCClass bc) {
addBeanField(bc, "id", Object.class);
BCField field = addBeanField(bc, "storageGenerator",
PCDataGenerator.class);
field.setAccessFlags(field.getAccessFlags()
| Constants.ACCESS_TRANSIENT);
}
/**
* Add methods for loading and storing class-level impl data.
*/
private void addImplDataMethods(BCClass bc, ClassMetaData meta) {
// void storeImplData(OpenJPAStateManager);
BCMethod meth = bc.declareMethod("storeImplData", void.class,
new Class[]{ OpenJPAStateManager.class });
Code code = meth.getCode(true);
BCField impl = null;
if (!usesImplData(meta))
code.vreturn();
else {
// if (sm.isImplDataCacheable())
// setImplData(sm.getImplData());
impl = addBeanField(bc, "implData", Object.class);
code.aload().setParam(0);
code.invokeinterface().setMethod(OpenJPAStateManager.class,
"isImplDataCacheable", boolean.class, null);
JumpInstruction ifins = code.ifeq();
code.aload().setThis();
code.aload().setParam(0);
code.invokeinterface().setMethod(OpenJPAStateManager.class,
"getImplData", Object.class, null);
code.invokevirtual().setMethod("setImplData", void.class,
new Class[]{ Object.class });
ifins.setTarget(code.vreturn());
}
code.calculateMaxStack();
code.calculateMaxLocals();
// void loadImplData(OpenJPAStateManager);
meth = bc.declareMethod("loadImplData", void.class,
new Class[]{ OpenJPAStateManager.class });
code = meth.getCode(true);
if (!usesImplData(meta))
code.vreturn();
else {
// if (sm.getImplData() == null && implData != null)
// sm.setImplData(impl, true);
code.aload().setParam(0);
code.invokeinterface().setMethod(OpenJPAStateManager.class,
"getImplData", Object.class, null);
JumpInstruction ifins = code.ifnonnull();
code.aload().setThis();
code.getfield().setField(impl);
JumpInstruction ifins2 = code.ifnull();
code.aload().setParam(0);
code.aload().setThis();
code.getfield().setField(impl);
code.constant().setValue(true);
code.invokeinterface().setMethod(OpenJPAStateManager.class,
"setImplData", void.class,
new Class[]{ Object.class, boolean.class });
Instruction ins = code.vreturn();
ifins.setTarget(ins);
ifins2.setTarget(ins);
}
code.calculateMaxStack();
code.calculateMaxLocals();
}
/**
* Add methods for loading and storing class-level impl data.
*/
private void addFieldImplDataMethods(BCClass bc, ClassMetaData meta) {
// public void loadImplData(OpenJPAStateManager sm, int i)
BCMethod meth = bc.declareMethod("loadImplData", void.class,
new Class[]{ OpenJPAStateManager.class, int.class });
meth.makePrivate();
Code code = meth.getCode(true);
int count = countImplDataFields(meta);
BCField impl = null;
if (count == 0)
code.vreturn();
else {
// Object[] fieldImpl
impl = bc.declareField("fieldImpl", Object[].class);
impl.makePrivate();
// if (fieldImpl != null)
code.aload().setThis();
code.getfield().setField(impl);
JumpInstruction ifins = code.ifnonnull();
code.vreturn();
// Object obj = null;
int obj = code.getNextLocalsIndex();
ifins.setTarget(code.constant().setNull());
code.astore().setLocal(obj);
// establish switch target, then move before it
Instruction target = code.aload().setLocal(obj);
code.previous();
// switch(i)
code.iload().setParam(1);
LookupSwitchInstruction lswitch = code.lookupswitch();
FieldMetaData[] fields = meta.getFields();
int cacheable = 0;
for (int i = 0; i < fields.length; i++) {
if (!usesImplData(fields[i]))
continue;
// case x: obj = fieldImpl[y]; break;
lswitch.addCase(i, code.aload().setThis());
code.getfield().setField(impl);
code.constant().setValue(cacheable++);
code.aaload();
code.astore().setLocal(obj);
code.go2().setTarget(target);
}
lswitch.setDefaultTarget(target);
// if (obj != null)
code.next(); // jump back over target
ifins = code.ifnonnull();
code.vreturn();
// sm.setImplData(index, impl);
ifins.setTarget(code.aload().setParam(0));
code.iload().setParam(1);
code.aload().setLocal(obj);
code.invokeinterface().setMethod(OpenJPAStateManager.class,
"setImplData", void.class,
new Class[]{ int.class, Object.class });
code.vreturn();
}
code.calculateMaxLocals();
code.calculateMaxStack();
// void storeImplData(OpenJPAStateManager sm, int index, boolean loaded)
meth = bc.declareMethod("storeImplData", void.class,
new Class[]{ OpenJPAStateManager.class, int.class, boolean.class });
code = meth.getCode(true);
if (count == 0)
code.vreturn();
else {
// int arrIdx = -1;
// switch(index)
int arrIdx = code.getNextLocalsIndex();
code.constant().setValue(-1);
code.istore().setLocal(arrIdx);
code.iload().setParam(1);
LookupSwitchInstruction lswitch = code.lookupswitch();
// establish switch target, then move before it
Instruction switchTarget = code.iload().setLocal(arrIdx);
code.previous();
FieldMetaData[] fields = meta.getFields();
int cacheable = 0;
for (int i = 0; i < fields.length; i++) {
if (!usesImplData(fields[i]))
continue;
// case x: arrIdx = y; break;
lswitch.addCase(i, code.constant().setValue(cacheable++));
code.istore().setLocal(arrIdx);
code.go2().setTarget(switchTarget);
}
lswitch.setDefaultTarget(switchTarget);
code.next(); // step over switch target
// if (arrIdx != -1)
code.constant().setValue(-1);
JumpInstruction ifins = code.ificmpne();
code.vreturn();
// create null target, then move before it
Instruction nullTarget = code.aload().setThis();
code.previous();
// if (loaded)
ifins.setTarget(code.iload().setParam(2));
code.ifeq().setTarget(nullTarget);
// Object obj = sm.getImplData(index)
int obj = code.getNextLocalsIndex();
code.aload().setParam(0);
code.iload().setParam(1);
code.invokeinterface().setMethod(OpenJPAStateManager.class,
"getImplData", Object.class, new Class[]{ int.class });
code.astore().setLocal(obj);
// if (obj != null)
code.aload().setLocal(obj);
code.ifnull().setTarget(nullTarget);
// if (fieldImpl == null)
// fieldImpl = new Object[fields];
code.aload().setThis();
code.getfield().setField(impl);
ifins = code.ifnonnull();
code.aload().setThis();
code.constant().setValue(count);
code.anewarray().setType(Object.class);
code.putfield().setField(impl);
// fieldImpl[arrIdx] = obj;
// return;
ifins.setTarget(code.aload().setThis());
code.getfield().setField(impl);
code.iload().setLocal(arrIdx);
code.aload().setLocal(obj);
code.aastore();
code.vreturn();
// if (fieldImpl != null)
// fieldImpl[index] = null;
code.next(); // step over nullTarget
code.getfield().setField(impl);
ifins = code.ifnonnull();
code.vreturn();
ifins.setTarget(code.aload().setThis());
code.getfield().setField(impl);
code.iload().setLocal(arrIdx);
code.constant().setNull();
code.aastore();
code.vreturn();
}
code.calculateMaxStack();
code.calculateMaxLocals();
}
/**
* Add methods for loading and storing version data.
*/
protected void addVersionMethods(BCClass bc) {
// void storeVersion(OpenJPAStateManager sm);
addBeanField(bc, "version", Object.class);
BCMethod meth = bc.declareMethod("storeVersion", void.class,
new Class[]{ OpenJPAStateManager.class });
Code code = meth.getCode(true);
// version = sm.getVersion();
code.aload().setThis();
code.aload().setParam(0);
code.invokeinterface()
.setMethod(OpenJPAStateManager.class, "getVersion",
Object.class, null);
code.putfield().setField("version", Object.class);
code.vreturn();
code.calculateMaxStack();
code.calculateMaxLocals();
// void loadVersion(OpenJPAStateManager sm)
meth = bc.declareMethod("loadVersion", void.class,
new Class[]{ OpenJPAStateManager.class });
code = meth.getCode(true);
// if (sm.getVersion() == null)
// sm.setVersion(version);
code.aload().setParam(0);
code.invokeinterface().setMethod(OpenJPAStateManager.class,
"getVersion", Object.class, null);
JumpInstruction ifins = code.ifnonnull();
code.aload().setParam(0);
code.aload().setThis();
code.getfield().setField("version", Object.class);
code.invokeinterface()
.setMethod(OpenJPAStateManager.class, "setVersion",
void.class, new Class[]{ Object.class });
ifins.setTarget(code.vreturn());
code.calculateMaxStack();
code.calculateMaxLocals();
}
private void addLoadMethod(BCClass bc, ClassMetaData meta) {
// public void load(OpenJPAStateManager sm, FetchConfiguration fetch,
// Object context)
Code code = addLoadMethod(bc, false);
FieldMetaData[] fmds = meta.getFields();
Collection<Instruction> jumps = new LinkedList<>();
Collection<Instruction> jumps2;
int local = code.getNextLocalsIndex();
code.constant().setNull();
code.astore().setLocal(local);
int inter = code.getNextLocalsIndex();
code.constant().setNull();
code.astore().setLocal(inter);
int objectCount = 0;
boolean intermediate;
for (int i = 0; i < fmds.length; i++) {
jumps2 = new LinkedList<>();
intermediate = usesIntermediate(fmds[i]);
setTarget(code.aload().setThis(), jumps);
// if (loaded.get(i)) or (!loaded.get(i)) depending on inter resp
code.getfield().setField("loaded", BitSet.class);
code.constant().setValue(i);
code.invokevirtual().setMethod(BitSet.class, "get",
boolean.class, new Class[]{ int.class });
jumps.add(code.ifne());
if (intermediate)
addLoadIntermediate(code, i, objectCount, jumps2, inter);
jumps2.add(code.go2());
// if (fetch.requiresFetch(fmds[i])!=FetchConfiguration.FETCH_NONE)
setTarget(code.aload().setParam(1), jumps);
code.aload().setParam(0);
code.invokeinterface().setMethod(OpenJPAStateManager.class,
"getMetaData", ClassMetaData.class, null);
code.constant().setValue(fmds[i].getIndex());
code.invokevirtual().setMethod(ClassMetaData.class,
"getField", FieldMetaData.class, new Class[]{int.class});
code.invokeinterface().setMethod (FetchConfiguration.class,
"requiresFetch", int.class, new Class[]{FieldMetaData.class});
code.constant().setValue(FetchConfiguration.FETCH_NONE);
jumps2.add(code.ificmpeq());
addLoad(bc, code, fmds[i], objectCount, local, false);
jumps = jumps2;
if (replaceType(fmds[i]) >= JavaTypes.OBJECT)
objectCount++;
}
setTarget(code.vreturn(), jumps);
code.calculateMaxStack();
code.calculateMaxLocals();
}
private void addLoadWithFieldsMethod(BCClass bc, ClassMetaData meta) {
Code code = addLoadMethod(bc, true);
// public void load(OpenJPAStateManager sm, BitSet fields,
// FetchConfiguration fetch, Object conn)
FieldMetaData[] fmds = meta.getFields();
Collection<Instruction> jumps = new LinkedList<>();
Collection<Instruction> jumps2;
int objectCount = 0;
boolean intermediate;
int local = code.getNextLocalsIndex();
code.constant().setNull();
code.astore().setLocal(local);
int inter = code.getNextLocalsIndex();
code.constant().setNull();
code.astore().setLocal(inter);
for (int i = 0; i < fmds.length; i++) {
jumps2 = new LinkedList<>();
intermediate = usesIntermediate(fmds[i]);
// if (fields.get(i))
// {
// if (loaded.get(i))
setTarget(code.aload().setParam(1), jumps);
code.constant().setValue(i);
code.invokevirtual().setMethod(BitSet.class, "get",
boolean.class, new Class[]{ int.class });
jumps2.add(code.ifeq());
code.aload().setThis();
code.getfield().setField("loaded", BitSet.class);
code.constant().setValue(i);
code.invokevirtual().setMethod(BitSet.class, "get",
boolean.class, new Class[]{ int.class });
if (intermediate)
jumps.add(code.ifeq());
else
jumps2.add(code.ifeq());
addLoad(bc, code, fmds[i], objectCount, local, true);
if (usesImplData(fmds[i])) {
// loadImplData(sm, i);
code.aload().setThis();
code.aload().setParam(0);
code.constant().setValue(i);
code.invokevirtual().setMethod("loadImplData", void.class,
new Class[]{ OpenJPAStateManager.class, int.class });
}
// fields.clear(i);
code.aload().setParam(1);
code.constant().setValue(i);
code.invokevirtual().setMethod(BitSet.class, "clear", void.class,
new Class[] { int.class });
jumps2.add(code.go2());
if (intermediate)
setTarget(addLoadIntermediate
(code, i, objectCount, jumps2, inter), jumps);
jumps = jumps2;
if (replaceType(fmds[i]) >= JavaTypes.OBJECT)
objectCount++;
}
setTarget(code.vreturn(), jumps);
code.calculateMaxStack();
code.calculateMaxLocals();
}
/**
* Declare and start the base load method.
*/
private Code addLoadMethod(BCClass bc, boolean fields) {
Class<?>[] args = null;
if (fields)
args = new Class[]{ OpenJPAStateManager.class, BitSet.class,
FetchConfiguration.class, Object.class };
else
args = new Class[]{ OpenJPAStateManager.class,
FetchConfiguration.class, Object.class };
BCMethod load = bc.declareMethod("load", void.class, args);
Code code = load.getCode(true);
//loadVersion(sm);
code.aload().setThis();
code.aload().setParam(0);
code.invokevirtual().setMethod("loadVersion", void.class,
new Class[]{ OpenJPAStateManager.class });
//loadImplData(sm);
code.aload().setThis();
code.aload().setParam(0);
code.invokevirtual().setMethod("loadImplData", void.class,
new Class[]{ OpenJPAStateManager.class });
return code;
}
/**
* Add the field load.
*/
private Instruction addLoad(BCClass bc, Code code, FieldMetaData fmd,
int objectCount, int local, boolean fields) {
int index = fmd.getIndex();
int typeCode = replaceType(fmd);
Instruction first;
if (typeCode < JavaTypes.OBJECT) {
// sm.store<type>(i, field<i>)
Class<?> type = forType(fmd.getTypeCode());
first = code.aload().setParam(0);
code.constant().setValue(index);
code.aload().setThis();
code.getfield().setField(getFieldName(index), type);
code.invokeinterface().setMethod(OpenJPAStateManager.class,
"store" + StringUtil.capitalize(type.getName()),
void.class, new Class[]{ int.class, type });
} else {
// fmd = sm.getMetaData().getField(i);
int offset = fields ? 1 : 0;
first = code.aload().setParam(0);
code.invokeinterface().setMethod(OpenJPAStateManager.class,
"getMetaData", ClassMetaData.class, null);
code.constant().setValue(fmd.getIndex());
code.invokevirtual().setMethod(ClassMetaData.class, "getField",
FieldMetaData.class, new Class[]{ int.class });
code.astore().setLocal(local);
// sm.storeField(i, toField(sm, fmd, objects[objectCount],
// fetch, context);
code.aload().setParam(0);
code.constant().setValue(index);
code.aload().setThis();
code.aload().setParam(0);
code.aload().setLocal(local);
code.aload().setThis();
code.getfield().setField("objects", Object[].class);
code.constant().setValue(objectCount);
code.aaload();
code.aload().setParam(1 + offset);
code.aload().setParam(2 + offset);
code.invokevirtual().setMethod(bc.getName(), "toField",
Object.class.getName(), toStrings(new Class[]{
OpenJPAStateManager.class, FieldMetaData.class,
Object.class, FetchConfiguration.class, Object.class }));
code.invokeinterface().setMethod(OpenJPAStateManager.class,
"storeField", void.class,
new Class[]{ int.class, Object.class });
}
return first;
}
/**
* Load intermediate data if possible.
*/
private Instruction addLoadIntermediate(Code code, int index,
int objectCount, Collection<Instruction> jumps2, int inter) {
// {
// Object inter = objects[objectCount];
Instruction first = code.aload().setThis();
code.getfield().setField("objects", Object[].class);
code.constant().setValue(objectCount);
code.aaload();
code.astore().setLocal(inter);
// if (inter != null && !sm.getLoaded().get(index))
code.aload().setLocal(inter);
jumps2.add(code.ifnull());
code.aload().setParam(0);
code.invokeinterface().setMethod(OpenJPAStateManager.class,
"getLoaded", BitSet.class, null);
code.constant().setValue(index);
code.invokevirtual().setMethod(BitSet.class, "get",
boolean.class, new Class[]{ int.class });
jumps2.add(code.ifne());
// sm.setIntermediate(index, inter);
// } // end else
code.aload().setParam(0);
code.constant().setValue(index);
code.aload().setLocal(inter);
code.invokeinterface().setMethod(OpenJPAStateManager.class,
"setIntermediate", void.class,
new Class[]{ int.class, Object.class });
return first;
}
private void addStoreMethods(BCClass bc, ClassMetaData meta) {
// i.e. void store(OpenJPAStateManager sm, BitSet fields);
addStoreMethod(bc, meta, true);
// i.e. void store(OpenJPAStateManager sm);
addStoreMethod(bc, meta, false);
}
private void addStoreMethod(BCClass bc, ClassMetaData meta,
boolean fields) {
BCMethod store;
if (fields)
store = bc.declareMethod("store", void.class,
new Class[]{ OpenJPAStateManager.class, BitSet.class });
else
store = bc.declareMethod("store", void.class,
new Class[]{ OpenJPAStateManager.class });
Code code = store.getCode(true);
// initialize();
code.aload().setThis();
code.invokevirtual().setMethod("initialize", void.class, null);
// storeVersion(sm);
code.aload().setThis();
code.aload().setParam(0);
code.invokevirtual().setMethod("storeVersion", void.class,
new Class[]{ OpenJPAStateManager.class });
// storeImplData(sm);
code.aload().setThis();
code.aload().setParam(0);
code.invokevirtual().setMethod("storeImplData", void.class,
new Class[]{ OpenJPAStateManager.class });
FieldMetaData[] fmds = meta.getFields();
Collection<Instruction> jumps = new LinkedList<>();
int objectCount = 0;
for (int i = 0; i < fmds.length; i++) {
if (fields) {
// if (fields != null && fields.get(index))
setTarget(code.aload().setParam(1), jumps);
jumps.add(code.ifnull());
code.aload().setParam(1);
code.constant().setValue(i);
code.invokevirtual().setMethod(BitSet.class, "get",
boolean.class, new Class[]{ int.class });
jumps.add(code.ifeq());
} else {
// if (sm.getLoaded().get(index)))
setTarget(code.aload().setParam(0), jumps);
code.invokeinterface().setMethod(OpenJPAStateManager.class,
"getLoaded", BitSet.class, null);
code.constant().setValue(i);
code.invokevirtual().setMethod(BitSet.class, "get",
boolean.class, new Class[]{ int.class });
jumps.add(code.ifeq());
}
addStore(bc, code, fmds[i], objectCount);
if (usesIntermediate(fmds[i])) {
JumpInstruction elseIns = code.go2();
// else if (!loaded.get(index))
setTarget(code.aload().setThis(), jumps);
jumps.add(elseIns);
code.getfield().setField("loaded", BitSet.class);
code.constant().setValue(i);
code.invokevirtual().setMethod(BitSet.class, "get",
boolean.class, new Class[]{ int.class });
jumps.add(code.ifne());
// Object val = sm.getIntermediate(index);
// if (val != null)
// objects[objectCount] = val;
code.aload().setParam(0);
code.constant().setValue(i);
code.invokeinterface().setMethod(OpenJPAStateManager.class,
"getIntermediate", Object.class, new Class[]{ int.class });
int local = code.getNextLocalsIndex();
code.astore().setLocal(local);
code.aload().setLocal(local);
jumps.add(code.ifnull());
code.aload().setThis();
code.getfield().setField("objects", Object[].class);
code.constant().setValue(objectCount);
code.aload().setLocal(local);
code.aastore();
}
if (replaceType(fmds[i]) >= JavaTypes.OBJECT)
objectCount++;
}
setTarget(code.vreturn(), jumps);
code.calculateMaxLocals();
code.calculateMaxStack();
}
private void addStore(BCClass bc, Code code, FieldMetaData fmd,
int objectCount) {
int typeCode = replaceType(fmd);
int index = fmd.getIndex();
if (typeCode < JavaTypes.OBJECT) {
Class<?> type = forType(typeCode);
// field<i> = sm.fetch<Type>(index)
code.aload().setThis();
code.aload().setParam(0);
code.constant().setValue(index);
code.invokeinterface().setMethod(OpenJPAStateManager.class,
"fetch" + StringUtil.capitalize(type.getName()), type,
new Class[]{ int.class });
code.putfield().setField(getFieldName(index), type);
code.aload().setThis();
code.getfield().setField("loaded", BitSet.class);
code.constant().setValue(index);
code.invokevirtual().setMethod(BitSet.class, "set", void.class,
new Class[]{ int.class });
} else {
// Object val = toData(sm.getMetaData().getField(index),
// sm.fetchField(index, false), sm.getContext());
int local = code.getNextLocalsIndex();
code.aload().setThis();
code.aload().setParam(0);
code.invokeinterface().setMethod(OpenJPAStateManager.class,
"getMetaData", ClassMetaData.class, null);
code.constant().setValue(fmd.getIndex());
code.invokevirtual().setMethod(ClassMetaData.class,
"getField", FieldMetaData.class, new Class[]{ int.class });
code.aload().setParam(0);
code.constant().setValue(fmd.getIndex());
code.constant().setValue(false);
code.invokeinterface().setMethod(OpenJPAStateManager.class,
"fetchField", Object.class, new Class[]
{ int.class, boolean.class });
code.aload().setParam(0);
code.invokeinterface().setMethod(OpenJPAStateManager.class,
"getContext", StoreContext.class, null);
code.invokevirtual().setMethod(bc.getName(), "toData",
Object.class.getName(), toStrings(new Class []{
FieldMetaData.class, Object.class, StoreContext.class }));
code.astore().setLocal(local);
// if (val == NULL) {
// val = null;
// loaded.clear(index);
// } else
// loaded.set(index);
// objects[objectCount] = val;
code.aload().setLocal(local);
code.getstatic().setField(AbstractPCData.class, "NULL",
Object.class);
JumpInstruction ifins = code.ifacmpne();
code.constant().setNull();
code.astore().setLocal(local);
code.aload().setThis();
code.getfield().setField("loaded", BitSet.class);
code.constant().setValue(index);
code.invokevirtual().setMethod(BitSet.class, "clear", void.class,
new Class[]{ int.class });
JumpInstruction go2 = code.go2();
ifins.setTarget(code.aload().setThis());
code.getfield().setField("loaded", BitSet.class);
code.constant().setValue(index);
code.invokevirtual().setMethod(BitSet.class, "set", void.class,
new Class[]{ int.class });
go2.setTarget(code.aload().setThis());
code.getfield().setField("objects", Object[].class);
code.constant().setValue(objectCount);
code.aload().setLocal(local);
code.aastore();
}
if (!usesImplData(fmd))
return;
// storeImplData(sm, i, loaded.get(i);
code.aload().setThis();
code.aload().setParam(0);
code.constant().setValue(index);
code.aload().setThis();
code.getfield().setField("loaded", BitSet.class);
code.constant().setValue(index);
code.invokevirtual().setMethod(BitSet.class, "get", boolean.class,
new Class[]{ int.class });
code.invokevirtual().setMethod("storeImplData", void.class,
new Class[]{ OpenJPAStateManager.class, int.class, boolean.class });
}
private void addNewEmbedded(BCClass bc) {
// void newEmbeddedPCData(OpenJPAStateManager embedded)
BCMethod meth = bc.declareMethod("newEmbeddedPCData", PCData.class,
new Class[]{ OpenJPAStateManager.class });
Code code = meth.getCode(true);
// return getStorageGenerator().generatePCData
// (sm.getId(), sm.getMetaData());
code.aload().setThis();
code.getfield().setField("storageGenerator", PCDataGenerator.class);
code.aload().setParam(0);
code.invokeinterface().setMethod(OpenJPAStateManager.class,
"getId", Object.class, null);
code.aload().setParam(0);
code.invokeinterface().setMethod(OpenJPAStateManager.class,
"getMetaData", ClassMetaData.class, null);
code.invokevirtual().setMethod(PCDataGenerator.class,
"generatePCData", PCData.class, new Class[]
{ Object.class, ClassMetaData.class });
code.areturn();
code.calculateMaxLocals();
code.calculateMaxStack();
}
private void addGetData(BCClass bc) {
// return getObjectField(i);
BCMethod method = bc.declareMethod("getData", Object.class,
new Class[]{ int.class });
Code code = method.getCode(true);
code.aload().setThis();
code.iload().setParam(0);
code.invokevirtual().setMethod("getObject", Object.class,
new Class[]{ int.class });
code.areturn();
code.calculateMaxLocals();
code.calculateMaxStack();
}
/////////////
// Utilities
/////////////
/**
* Return a valid {@link JavaTypes} constant for the given field
*/
protected int replaceType(FieldMetaData fmd) {
if (usesIntermediate(fmd))
return JavaTypes.OBJECT;
return fmd.getTypeCode();
}
/**
* Whether the given field uses a cacheable intermediate value.
*/
protected boolean usesIntermediate(FieldMetaData fmd) {
return fmd.usesIntermediate();
}
/**
* Whether the given type might have cacheable class-level impl data.
*/
protected boolean usesImplData(ClassMetaData meta) {
return true;
}
/**
* Whether the given field might have cacheable impl data.
*/
protected boolean usesImplData(FieldMetaData fmd) {
return fmd.usesImplData() == null;
}
/**
* The number of fields with cacheable impl data.
*/
private int countImplDataFields(ClassMetaData meta) {
FieldMetaData[] fmds = meta.getFields();
int count = 0;
for (FieldMetaData fmd : fmds)
if (usesImplData(fmd))
count++;
return count;
}
/**
* Add method which defers to AbstractPCData.
*/
protected void callAbstractPCData(BCClass bc, String name, Class<?> retType,
Class<?>[] args) {
BCMethod meth = bc.declareMethod(name, retType, args);
Code code = meth.getCode(true);
code.aload().setThis();
for (int i = 0; i < args.length; i++)
code.xload().setParam(i).setType(args[i]);
code.invokevirtual().setMethod(AbstractPCData.class, name, retType,
args);
if (!void.class.equals(retType))
code.xreturn().setType(retType);
code.calculateMaxLocals();
code.calculateMaxStack();
}
/**
* Set the collection of {@link JumpInstruction}s to the given instruction,
* clearing the collection in the process.
*/
protected void setTarget(Instruction ins, Collection<Instruction> jumps) {
for (Instruction jump : jumps) {
((JumpInstruction) jump).setTarget(ins);
}
jumps.clear();
}
/**
* Transform the given array of classes to strings.
*/
private static String[] toStrings(Class<?>[] cls) {
String[] strings = new String[cls.length];
for (int i = 0; i < strings.length; i++)
strings[i] = cls[i].getName();
return strings;
}
/**
* Dynamic {@link PCData}s generated will implement this interface
* to simplify initialization.
*/
public interface DynamicPCData extends PCData {
void setId(Object oid);
PCDataGenerator getStorageGenerator();
void setStorageGenerator (PCDataGenerator generator);
}
}