mirror of https://github.com/apache/openjpa.git
OPENJPA-2911 addProvidedFieldsMethod in ASM
This commit is contained in:
parent
03f2d6ae88
commit
d8d829b76b
|
@ -1368,10 +1368,10 @@ public class PCEnhancer {
|
||||||
|
|
||||||
addManagedFieldCountMethod(classNodeTracker.getClassNode());
|
addManagedFieldCountMethod(classNodeTracker.getClassNode());
|
||||||
addReplaceFieldsMethods(classNodeTracker.getClassNode());
|
addReplaceFieldsMethods(classNodeTracker.getClassNode());
|
||||||
|
addProvideFieldsMethods(classNodeTracker.getClassNode());
|
||||||
|
|
||||||
AsmHelper.readIntoBCClass(classNodeTracker, _pc);
|
AsmHelper.readIntoBCClass(classNodeTracker, _pc);
|
||||||
|
|
||||||
addProvideFieldsMethods();
|
|
||||||
addCopyFieldsMethod();
|
addCopyFieldsMethod();
|
||||||
|
|
||||||
if (_meta.getPCSuperclass() == null || getCreateSubclass()) {
|
if (_meta.getPCSuperclass() == null || getCreateSubclass()) {
|
||||||
|
@ -1575,51 +1575,63 @@ public class PCEnhancer {
|
||||||
* Adds the {@link PersistenceCapable#pcProvideField} and
|
* Adds the {@link PersistenceCapable#pcProvideField} and
|
||||||
* {@link PersistenceCapable#pcProvideFields} methods to the bytecode.
|
* {@link PersistenceCapable#pcProvideFields} methods to the bytecode.
|
||||||
*/
|
*/
|
||||||
private void addProvideFieldsMethods()
|
private void addProvideFieldsMethods(ClassNode classNode) throws NoSuchMethodException {
|
||||||
throws NoSuchMethodException {
|
MethodNode provideFieldsMeth = new MethodNode(Opcodes.ACC_PUBLIC,
|
||||||
// public void pcProvideField (int fieldNumber)
|
PRE + "ProvideField",
|
||||||
BCMethod method = _pc.declareMethod(PRE + "ProvideField", void.class,
|
Type.getMethodDescriptor(Type.VOID_TYPE, Type.INT_TYPE),
|
||||||
new Class[]{int.class});
|
null, null);
|
||||||
Code code = method.getCode(true);
|
classNode.methods.add(provideFieldsMeth);
|
||||||
|
final InsnList instructions = provideFieldsMeth.instructions;
|
||||||
|
|
||||||
// adds everything through the switch ()
|
final int relLocal = beginSwitchMethod(classNode, PRE + "ProvideField", instructions, false);
|
||||||
int relLocal = beginSwitchMethod(PRE + "ProvideField", code, false);
|
|
||||||
|
|
||||||
// if no fields in this inst, just throw exception
|
// if no fields in this inst, just throw exception
|
||||||
FieldMetaData[] fmds = getCreateSubclass() ? _meta.getFields()
|
FieldMetaData[] fmds = getCreateSubclass() ? _meta.getFields()
|
||||||
: _meta.getDeclaredFields();
|
: _meta.getDeclaredFields();
|
||||||
if (fmds.length == 0)
|
if (fmds.length == 0) {
|
||||||
throwException(code, IllegalArgumentException.class);
|
instructions.add(throwException(IllegalArgumentException.class));
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
// switch (val)
|
// switch (val)
|
||||||
code.iload().setLocal(relLocal);
|
instructions.add(new VarInsnNode(Opcodes.ILOAD, relLocal));
|
||||||
TableSwitchInstruction tabins = code.tableswitch();
|
|
||||||
tabins.setLow(0);
|
|
||||||
tabins.setHigh(fmds.length - 1);
|
|
||||||
|
|
||||||
// <field> = pcStateManager.provided<type>Field
|
LabelNode defaultCase = new LabelNode();
|
||||||
// (this, fieldNumber);
|
TableSwitchInsnNode ts = new TableSwitchInsnNode(0, fmds.length - 1, defaultCase);
|
||||||
|
instructions.add(ts);
|
||||||
|
|
||||||
|
// <field> = pcStateManager.provided<type>Field(this, fieldNumber);
|
||||||
for (FieldMetaData fmd : fmds) {
|
for (FieldMetaData fmd : fmds) {
|
||||||
tabins.addTarget(loadManagedInstance(code, false));
|
// case xxx:
|
||||||
code.getfield().setField(SM, SMTYPE);
|
LabelNode caseLabel = new LabelNode();
|
||||||
loadManagedInstance(code, false);
|
instructions.add(caseLabel);
|
||||||
code.iload().setParam(0);
|
ts.labels.add(caseLabel);
|
||||||
loadManagedInstance(code, false);
|
|
||||||
addGetManagedValueCode(code, fmd);
|
// load pcStateManager to stack
|
||||||
code.invokeinterface().setMethod(getStateManagerMethod
|
instructions.add(new VarInsnNode(Opcodes.ALOAD, 0)); // this
|
||||||
(fmd.getDeclaredType(), "provided", false, false));
|
instructions.add(new FieldInsnNode(Opcodes.GETFIELD, classNode.name, SM, Type.getDescriptor(SMTYPE)));
|
||||||
code.vreturn();
|
|
||||||
|
// invoke StateManager#provided
|
||||||
|
final Method smProvidedMeth = getStateManagerMethod(fmd.getDeclaredType(), "provided", false, false);
|
||||||
|
|
||||||
|
instructions.add(new VarInsnNode(Opcodes.ALOAD, 0)); // this
|
||||||
|
instructions.add(new VarInsnNode(Opcodes.ILOAD, 1)); // fieldNr int
|
||||||
|
|
||||||
|
instructions.add(new VarInsnNode(Opcodes.ALOAD, 0)); // this for the getfield
|
||||||
|
addGetManagedValueCode(classNode, instructions, fmd, true);
|
||||||
|
|
||||||
|
instructions.add(new MethodInsnNode(Opcodes.INVOKEINTERFACE,
|
||||||
|
Type.getInternalName(SMTYPE),
|
||||||
|
smProvidedMeth.getName(),
|
||||||
|
Type.getMethodDescriptor(smProvidedMeth)));
|
||||||
|
|
||||||
|
instructions.add(new InsnNode(Opcodes.RETURN));
|
||||||
}
|
}
|
||||||
|
|
||||||
// default: throw new IllegalArgumentException ()
|
instructions.add(defaultCase);
|
||||||
tabins.setDefaultTarget(throwException
|
instructions.add(throwException(IllegalArgumentException.class));
|
||||||
(code, IllegalArgumentException.class));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
code.calculateMaxStack();
|
addMultipleFieldsMethodVersion(classNode, provideFieldsMeth, false);
|
||||||
code.calculateMaxLocals();
|
|
||||||
|
|
||||||
addMultipleFieldsMethodVersion(method, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1649,13 +1661,13 @@ public class PCEnhancer {
|
||||||
TableSwitchInsnNode ts = new TableSwitchInsnNode(0, fmds.length - 1, defaultCase);
|
TableSwitchInsnNode ts = new TableSwitchInsnNode(0, fmds.length - 1, defaultCase);
|
||||||
instructions.add(ts);
|
instructions.add(ts);
|
||||||
|
|
||||||
// <field> = pcStateManager.replace<type>Field
|
// <field> = pcStateManager.replace<type>Field(this, fieldNumber);
|
||||||
// (this, fieldNumber);
|
|
||||||
for (FieldMetaData fmd : fmds) {
|
for (FieldMetaData fmd : fmds) {
|
||||||
// case xxx:
|
// case xxx:
|
||||||
LabelNode caseLabel = new LabelNode();
|
LabelNode caseLabel = new LabelNode();
|
||||||
instructions.add(caseLabel);
|
instructions.add(caseLabel);
|
||||||
ts.labels.add(caseLabel);
|
ts.labels.add(caseLabel);
|
||||||
|
|
||||||
instructions.add(new VarInsnNode(Opcodes.ALOAD, 0)); // this
|
instructions.add(new VarInsnNode(Opcodes.ALOAD, 0)); // this
|
||||||
|
|
||||||
// load pcStateManager to stack
|
// load pcStateManager to stack
|
||||||
|
@ -1665,7 +1677,6 @@ public class PCEnhancer {
|
||||||
// invoke StateManager#replace
|
// invoke StateManager#replace
|
||||||
instructions.add(new VarInsnNode(Opcodes.ALOAD, 0)); // this
|
instructions.add(new VarInsnNode(Opcodes.ALOAD, 0)); // this
|
||||||
instructions.add(new VarInsnNode(Opcodes.ILOAD, 1)); // fieldNr int
|
instructions.add(new VarInsnNode(Opcodes.ILOAD, 1)); // fieldNr int
|
||||||
|
|
||||||
final Method rmReplaceMeth = getStateManagerMethod(fmd.getDeclaredType(), "replace", true, false);
|
final Method rmReplaceMeth = getStateManagerMethod(fmd.getDeclaredType(), "replace", true, false);
|
||||||
instructions.add(new MethodInsnNode(Opcodes.INVOKEINTERFACE,
|
instructions.add(new MethodInsnNode(Opcodes.INVOKEINTERFACE,
|
||||||
Type.getInternalName(SMTYPE),
|
Type.getInternalName(SMTYPE),
|
||||||
|
@ -4552,6 +4563,54 @@ public class PCEnhancer {
|
||||||
* The instance to access must already be on the top of the
|
* The instance to access must already be on the top of the
|
||||||
* stack when this is invoked.
|
* stack when this is invoked.
|
||||||
*/
|
*/
|
||||||
|
private void getfield(ClassNode classNode, InsnList instructions, Class declarer, String attrName, Class fieldType) {
|
||||||
|
// first, see if we can convert the attribute name to a field name
|
||||||
|
String fieldName = toBackingFieldName(attrName);
|
||||||
|
FieldNode field = findField(classNode, declarer, fieldName);
|
||||||
|
|
||||||
|
if (getCreateSubclass() && (field == null || !((field.access & Opcodes.ACC_PUBLIC) > 0))) {
|
||||||
|
// we're creating the subclass, not redefining the user type.
|
||||||
|
|
||||||
|
// Reflection.getXXX(this, Reflection.findField(...));
|
||||||
|
throw new UnsupportedOperationException("MSX TODO IMPLEMENT!");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
instructions.add(new FieldInsnNode(Opcodes.GETFIELD, Type.getInternalName(declarer), fieldName, Type.getDescriptor(fieldType)));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private FieldNode findField(ClassNode classNode, Class clazz, String fieldName) {
|
||||||
|
if (classNode != null) {
|
||||||
|
final Optional<FieldNode> field = classNode.fields.stream()
|
||||||
|
.filter(f -> f.name.equals(fieldName))
|
||||||
|
.findFirst();
|
||||||
|
if (field.isPresent()) {
|
||||||
|
return field.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
final Field field = clazz.getDeclaredField(fieldName);
|
||||||
|
return new FieldNode(Opcodes.ACC_PRIVATE, field.getName(), Type.getDescriptor(field.getType()), null, null);
|
||||||
|
}
|
||||||
|
catch (NoSuchFieldException e) {
|
||||||
|
if (clazz.getSuperclass() != Object.class) {
|
||||||
|
return findField(null, clazz.getSuperclass(), fieldName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds to <code>code</code> the instructions to get field
|
||||||
|
* <code>attrName</code> declared in type <code>declarer</code>
|
||||||
|
* onto the top of the stack.
|
||||||
|
* <p>
|
||||||
|
* The instance to access must already be on the top of the
|
||||||
|
* stack when this is invoked.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
private void getfield(Code code, BCClass declarer, String attrName) {
|
private void getfield(Code code, BCClass declarer, String attrName) {
|
||||||
if (declarer == null)
|
if (declarer == null)
|
||||||
declarer = _managedType;
|
declarer = _managedType;
|
||||||
|
@ -5115,6 +5174,7 @@ public class PCEnhancer {
|
||||||
addGetManagedValueCode(code, fmd, true);
|
addGetManagedValueCode(code, fmd, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load the field value specified by <code>fmd</code> onto the stack.
|
* Load the field value specified by <code>fmd</code> onto the stack.
|
||||||
* Before this method is called, the object that the data should be loaded
|
* Before this method is called, the object that the data should be loaded
|
||||||
|
@ -5125,6 +5185,54 @@ public class PCEnhancer {
|
||||||
* context. If <code>false</code>, then the instance on the top of the stack
|
* context. If <code>false</code>, then the instance on the top of the stack
|
||||||
* might be a superclass of the current execution context's 'this' instance.
|
* might be a superclass of the current execution context's 'this' instance.
|
||||||
*/
|
*/
|
||||||
|
private void addGetManagedValueCode(ClassNode classNode, InsnList instructions, FieldMetaData fmd, boolean fromSameClass) {
|
||||||
|
// if redefining, then we must always reflect (or access the field
|
||||||
|
// directly if accessible), since the redefined methods will always
|
||||||
|
// trigger method calls to StateManager, even from internal direct-
|
||||||
|
// access usage. We could work around this by not redefining, and
|
||||||
|
// just do a subclass approach instead. But this is not a good option,
|
||||||
|
// since it would sacrifice lazy loading and efficient dirty tracking.
|
||||||
|
|
||||||
|
if (getRedefine() || isFieldAccess(fmd)) {
|
||||||
|
getfield(classNode, instructions, getType(_meta), fmd.getName(), fmd.getDeclaredType());
|
||||||
|
}
|
||||||
|
else if (getCreateSubclass()) {
|
||||||
|
// property access, and we're not redefining. If we're operating
|
||||||
|
// on an instance that is definitely the same type as 'this', then
|
||||||
|
// call superclass method to bypass tracking. Otherwise, reflect
|
||||||
|
// to both bypass tracking and avoid class verification errors.
|
||||||
|
if (fromSameClass) {
|
||||||
|
Method meth = (Method) fmd.getBackingMember();
|
||||||
|
instructions.add(new MethodInsnNode(Opcodes.INVOKESPECIAL,
|
||||||
|
Type.getInternalName(meth.getDeclaringClass()),
|
||||||
|
meth.getName(),
|
||||||
|
Type.getMethodDescriptor(meth)));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
getfield(classNode, instructions, getType(_meta), fmd.getName(), fmd.getDeclaredType());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// regular enhancement + property access
|
||||||
|
Method meth = (Method) fmd.getBackingMember();
|
||||||
|
instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL,
|
||||||
|
classNode.name,
|
||||||
|
PRE + meth.getName(),
|
||||||
|
Type.getMethodDescriptor(meth)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load the field value specified by <code>fmd</code> onto the stack.
|
||||||
|
* Before this method is called, the object that the data should be loaded
|
||||||
|
* from will be on the top of the stack.
|
||||||
|
*
|
||||||
|
* @param fromSameClass if <code>true</code>, then <code>fmd</code> is
|
||||||
|
* being loaded from an instance of the same class as the current execution
|
||||||
|
* context. If <code>false</code>, then the instance on the top of the stack
|
||||||
|
* might be a superclass of the current execution context's 'this' instance.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
private void addGetManagedValueCode(Code code, FieldMetaData fmd,
|
private void addGetManagedValueCode(Code code, FieldMetaData fmd,
|
||||||
boolean fromSameClass)
|
boolean fromSameClass)
|
||||||
throws NoSuchMethodException {
|
throws NoSuchMethodException {
|
||||||
|
|
Loading…
Reference in New Issue