OPENJPA-2911 addAccessors in ASM

This commit is contained in:
Mark Struberg 2023-07-03 11:06:17 +02:00
parent 5e89853664
commit 2b9b024f27
2 changed files with 286 additions and 169 deletions

View File

@ -50,10 +50,12 @@ import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.Properties; import java.util.Properties;
import java.util.Set; import java.util.Set;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.stream.Collectors;
import org.apache.openjpa.conf.OpenJPAConfiguration; import org.apache.openjpa.conf.OpenJPAConfiguration;
import org.apache.openjpa.conf.OpenJPAConfigurationImpl; import org.apache.openjpa.conf.OpenJPAConfigurationImpl;
@ -597,15 +599,12 @@ public class PCEnhancer {
if (_meta != null) { if (_meta != null) {
enhanceClass(pc); enhanceClass(pc);
addFields(pc); addFields(pc);
//X For now we cannot take the new version as it uses LDC for classes which Serp doesn't understand.
//X So we can only enable it in a later step
addStaticInitializer(pc); addStaticInitializer(pc);
//X addStaticInitializer(); // removeme
addPCMethods(); addPCMethods();
addAccessors(pc);
AsmHelper.readIntoBCClass(pc, _pc);
addAccessors();
addAttachDetachCode(); addAttachDetachCode();
addSerializationCode(); addSerializationCode();
addCloningCode(); addCloningCode();
@ -989,10 +988,7 @@ public class PCEnhancer {
} }
private static MethodNode findMethodNode(ClassNode classNode, Method meth) { private static MethodNode findMethodNode(ClassNode classNode, Method meth) {
final MethodNode methodNode = classNode.methods.stream() return AsmHelper.getMethodNode(classNode, meth).get();
.filter(mn -> mn.name.equals(meth.getName()) && mn.desc.equals(Type.getMethodDescriptor(meth)))
.findAny().get();
return methodNode;
} }
@ -1226,7 +1222,12 @@ public class PCEnhancer {
"accessingField", "accessingField",
Type.getMethodDescriptor(Type.VOID_TYPE, TYPE_OBJECT, Type.INT_TYPE))); Type.getMethodDescriptor(Type.VOID_TYPE, TYPE_OBJECT, Type.INT_TYPE)));
methodNode.instructions.insertBefore(currentInsn, insns); if (methodNode.instructions.size() == 0) {
methodNode.instructions.add(insns);
}
else {
methodNode.instructions.insertBefore(currentInsn, insns);
}
} }
@Deprecated @Deprecated
@ -1273,7 +1274,7 @@ public class PCEnhancer {
insns.add(new MethodInsnNode(Opcodes.INVOKESTATIC, insns.add(new MethodInsnNode(Opcodes.INVOKESTATIC,
Type.getInternalName(RedefinitionHelper.class), Type.getInternalName(RedefinitionHelper.class),
"settingField", "settingField",
Type.getMethodDescriptor(Type.VOID_TYPE, TYPE_OBJECT, Type.INT_TYPE, Type.getType(type)))); Type.getMethodDescriptor(Type.VOID_TYPE, TYPE_OBJECT, Type.INT_TYPE, Type.getType(type), Type.getType(type))));
methodNode.instructions.insert(currentInsn, insns); methodNode.instructions.insert(currentInsn, insns);
return insns.getLast(); return insns.getLast();
@ -1400,14 +1401,9 @@ public class PCEnhancer {
addNewObjectIdInstanceMethod(true); addNewObjectIdInstanceMethod(true);
addNewObjectIdInstanceMethod(false); addNewObjectIdInstanceMethod(false);
AsmHelper.readIntoBCClass(pc, _pc);
} }
else if (_meta.hasPKFieldsFromAbstractClass()) { else if (_meta.hasPKFieldsFromAbstractClass()) {
addGetIDOwningClass(); addGetIDOwningClass();
AsmHelper.readIntoBCClass(pc, _pc);
}
else {
AsmHelper.readIntoBCClass(pc, _pc);
} }
} }
@ -4313,20 +4309,19 @@ public class PCEnhancer {
* Adds synthetic field access methods that will replace all direct * Adds synthetic field access methods that will replace all direct
* field accesses. * field accesses.
*/ */
private void addAccessors() private void addAccessors(ClassNodeTracker cnt) throws NoSuchMethodException {
throws NoSuchMethodException { ClassNode classNode = cnt.getClassNode();
FieldMetaData[] fmds = getCreateSubclass() ? _meta.getFields() FieldMetaData[] fmds = getCreateSubclass() ? _meta.getFields() : _meta.getDeclaredFields();
: _meta.getDeclaredFields();
for (int i = 0; i < fmds.length; i++) { for (int i = 0; i < fmds.length; i++) {
if (getCreateSubclass()) { if (getCreateSubclass()) {
if (!getRedefine() && isPropertyAccess(fmds[i])) { if (!getRedefine() && isPropertyAccess(fmds[i])) {
addSubclassSetMethod(fmds[i]); addSubclassSetMethod(classNode, fmds[i]);
addSubclassGetMethod(fmds[i]); addSubclassGetMethod(classNode, fmds[i]);
} }
} }
else { else {
addGetMethod(i, fmds[i]); addGetMethod(classNode, i, fmds[i]);
addSetMethod(i, fmds[i]); addSetMethod(classNode, i, fmds[i]);
} }
} }
} }
@ -4335,37 +4330,67 @@ public class PCEnhancer {
* Adds a non-static setter that delegates to the super methods, and * Adds a non-static setter that delegates to the super methods, and
* performs any necessary field tracking. * performs any necessary field tracking.
*/ */
@Deprecated private void addSubclassSetMethod(ClassNode classNode, FieldMetaData fmd) throws NoSuchMethodException {
private void addSubclassSetMethod(FieldMetaData fmd)
throws NoSuchMethodException {
Class propType = fmd.getDeclaredType(); Class propType = fmd.getDeclaredType();
String setterName = getSetterName(fmd); String setterName = getSetterName(fmd);
BCMethod setter = _pc.declareMethod(setterName, void.class,
new Class[]{propType}); MethodNode newMethod = new MethodNode(Opcodes.ACC_PUBLIC,
setVisibilityToSuperMethod(setter); setterName,
Code code = setter.getCode(true); Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(propType)),
null, null);
classNode.methods.add(newMethod);
final InsnList instructions = newMethod.instructions;
int nextFreeVarPos = 2;
setVisibilityToSuperMethod(newMethod);
// not necessary if we're already tracking access via redefinition // not necessary if we're already tracking access via redefinition
if (!getRedefine()) { if (!getRedefine()) {
// get the orig value onto stack // get the orig value onto stack
code.aload().setThis(); instructions.add(new VarInsnNode(Opcodes.ALOAD, 0)); // this
addGetManagedValueCode(code, fmd); addGetManagedValueCode(classNode, instructions, fmd, true);
int val = code.getNextLocalsIndex();
code.xstore().setLocal(val).setType(fmd.getDeclaredType()); int valVarPos = nextFreeVarPos++;
addNotifyMutation(code, fmd, val, 0); instructions.add(new VarInsnNode(AsmHelper.getStoreInsn(fmd.getDeclaredType()), valVarPos));
addNotifyMutation(classNode, newMethod, newMethod.instructions.getLast(), fmd, valVarPos, 0);
} }
// ##### test case: B extends A. Methods defined in A. What // ##### test case: B extends A. Methods defined in A. What
// ##### happens? // ##### happens?
// super.setXXX(...) // super.setXXX(...)
code.aload().setThis(); instructions.add(new VarInsnNode(Opcodes.ALOAD, 0)); // this
code.xload().setParam(0).setType(propType); instructions.add(new VarInsnNode(AsmHelper.getLoadInsn(propType), 1)); // 1st param
code.invokespecial().setMethod(_managedType.getType(), instructions.add(new MethodInsnNode(Opcodes.INVOKESPECIAL,
setterName, void.class, new Class[]{propType}); managedType.getClassNode().name,
setterName,
Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(propType))));
instructions.add(new InsnNode(Opcodes.RETURN));
}
code.vreturn(); private boolean setVisibilityToSuperMethod(MethodNode method) {
code.calculateMaxLocals(); ClassNode classNode = managedType.getClassNode();
code.calculateMaxStack(); final List<MethodNode> methods = classNode.methods.stream()
.filter(m -> m.name.equals(method.name) && Objects.equals(m.parameters, method.parameters))
.collect(Collectors.toList());
if (methods.isEmpty()) {
throw new UserException(_loc.get("no-accessor", _managedType.getName(), method.name));
}
MethodNode superMeth = methods.get(0);
method.access &= ~(Opcodes.ACC_PRIVATE |Opcodes.ACC_PUBLIC | Opcodes.ACC_PROTECTED);
if ((superMeth.access & Opcodes.ACC_PRIVATE) > 0) {
method.access |= Opcodes.ACC_PRIVATE;
return true;
}
if ((superMeth.access & Opcodes.ACC_PROTECTED) > 0) {
method.access |= Opcodes.ACC_PROTECTED;
return true;
}
if ((superMeth.access & Opcodes.ACC_PUBLIC) > 0) {
method.access |= Opcodes.ACC_PUBLIC;
return true;
}
return false;
} }
private boolean setVisibilityToSuperMethod(BCMethod method) { private boolean setVisibilityToSuperMethod(BCMethod method) {
@ -4398,29 +4423,40 @@ public class PCEnhancer {
* Adds a non-static getter that delegates to the super methods, and * Adds a non-static getter that delegates to the super methods, and
* performs any necessary field tracking. * performs any necessary field tracking.
*/ */
@Deprecated private void addSubclassGetMethod(ClassNode classNode, FieldMetaData fmd) {
private void addSubclassGetMethod(FieldMetaData fmd) { String getterName = "get" + StringUtil.capitalize(fmd.getName());
String methName = "get" + StringUtil.capitalize(fmd.getName());
if (_managedType.getMethods(methName, new Class[0]).length == 0) final String finalGetterName = getterName;
methName = "is" + StringUtil.capitalize(fmd.getName()); final boolean hasGetter = managedType.getClassNode().methods.stream()
BCMethod getter = _pc.declareMethod(methName, fmd.getDeclaredType(), .filter(m -> m.name.equals(finalGetterName) && (m.parameters == null || m.parameters.isEmpty()))
null); .findAny()
setVisibilityToSuperMethod(getter); .isPresent();
getter.makePublic(); if (!hasGetter){
Code code = getter.getCode(true); getterName = "is" + StringUtil.capitalize(fmd.getName());
}
final Class propType = fmd.getDeclaredType();
MethodNode getterMethod = new MethodNode(Opcodes.ACC_PUBLIC,
getterName,
Type.getMethodDescriptor(Type.getType(propType)),
null, null);
classNode.methods.add(getterMethod);
final InsnList instructions = getterMethod.instructions;
int nextFreeVarPos = 2;
// if we're not already tracking field access via reflection, then we // if we're not already tracking field access via reflection, then we
// must make the getter hook in lazy loading before accessing the super // must make the getter hook in lazy loading before accessing the super
// method. // method.
if (!getRedefine()) if (!getRedefine()) {
addNotifyAccess(code, fmd); addNotifyAccess(getterMethod, getterMethod.instructions.getLast(), fmd);
}
code.aload().setThis(); instructions.add(new VarInsnNode(Opcodes.ALOAD, 0)); // this
code.invokespecial().setMethod(_managedType.getType(), methName, instructions.add(new MethodInsnNode(Opcodes.INVOKESPECIAL,
fmd.getDeclaredType(), null); managedType.getClassNode().name,
code.xreturn().setType(fmd.getDeclaredType()); getterName,
code.calculateMaxLocals(); Type.getMethodDescriptor(Type.getType(propType))));
code.calculateMaxStack(); instructions.add(new InsnNode(AsmHelper.getReturnInsn(propType)));
} }
/** /**
@ -4431,53 +4467,51 @@ public class PCEnhancer {
* @param index the relative number of the field * @param index the relative number of the field
* @param fmd metadata about the field to get * @param fmd metadata about the field to get
*/ */
@Deprecated private void addGetMethod(ClassNode classNode, int index, FieldMetaData fmd) throws NoSuchMethodException {
private void addGetMethod(int index, FieldMetaData fmd) MethodNode method = createGetMethod(classNode, fmd);
throws NoSuchMethodException { classNode.methods.add(method);
BCMethod method = createGetMethod(fmd); final InsnList instructions = method.instructions;
Code code = method.getCode(true); int nextFreeVarPos = 1;
// if reads are not checked, just return the value // if reads are not checked, just return the value
byte fieldFlag = getFieldFlag(fmd); byte fieldFlag = getFieldFlag(fmd);
if ((fieldFlag & PersistenceCapable.CHECK_READ) == 0 if ((fieldFlag & PersistenceCapable.CHECK_READ) == 0 && (fieldFlag & PersistenceCapable.MEDIATE_READ) == 0) {
&& (fieldFlag & PersistenceCapable.MEDIATE_READ) == 0) { instructions.add(new VarInsnNode(Opcodes.ALOAD, 0)); // this
loadManagedInstance(code, true, fmd); addGetManagedValueCode(classNode, instructions, fmd, true);
addGetManagedValueCode(code, fmd); instructions.add(new InsnNode(AsmHelper.getReturnInsn(fmd.getDeclaredType())));
code.xreturn().setType(fmd.getDeclaredType());
code.calculateMaxStack();
code.calculateMaxLocals();
return; return;
} }
// if (inst.pcStateManager == null) return inst.<field>; // if (inst.pcStateManager == null) return inst.<field>;
loadManagedInstance(code, true, fmd); instructions.add(loadManagedInstance(true, fmd));
code.getfield().setField(SM, SMTYPE); instructions.add(new FieldInsnNode(Opcodes.GETFIELD, classNode.name, SM, Type.getDescriptor(SMTYPE)));
JumpInstruction ifins = code.ifnonnull(); LabelNode afterIfNonNull = new LabelNode();
loadManagedInstance(code, true, fmd); instructions.add(new JumpInsnNode(Opcodes.IFNONNULL, afterIfNonNull));
addGetManagedValueCode(code, fmd); instructions.add(loadManagedInstance(true, fmd));
code.xreturn().setType(fmd.getDeclaredType()); addGetManagedValueCode(classNode, instructions, fmd, true);
instructions.add(new InsnNode(AsmHelper.getReturnInsn(fmd.getDeclaredType())));
instructions.add(afterIfNonNull);
// int field = pcInheritedFieldCount + <fieldindex>; // int field = pcInheritedFieldCount + <fieldindex>;
int fieldLocal = code.getNextLocalsIndex(); int fieldLocalVarPos = nextFreeVarPos++;
ifins.setTarget(code.getstatic().setField(INHERIT, int.class)); instructions.add(new FieldInsnNode(Opcodes.GETSTATIC, classNode.name, INHERIT, Type.INT_TYPE.getDescriptor()));
code.constant().setValue(index); instructions.add(AsmHelper.getLoadConstantInsn(index));
code.iadd(); instructions.add(new InsnNode(Opcodes.IADD));
code.istore().setLocal(fieldLocal); instructions.add(new VarInsnNode(Opcodes.ISTORE, fieldLocalVarPos));
// inst.pcStateManager.accessingField (field); // inst.pcStateManager.accessingField (field);
// return inst.<field>; // return inst.<field>;
loadManagedInstance(code, true, fmd); instructions.add(loadManagedInstance(true, fmd));
code.getfield().setField(SM, SMTYPE); instructions.add(new FieldInsnNode(Opcodes.GETFIELD, classNode.name, SM, Type.getDescriptor(SMTYPE)));
code.iload().setLocal(fieldLocal); instructions.add(new VarInsnNode(Opcodes.ILOAD, fieldLocalVarPos));
code.invokeinterface().setMethod(SMTYPE, "accessingField", void.class, instructions.add(new MethodInsnNode(Opcodes.INVOKEINTERFACE,
new Class[]{int.class}); Type.getInternalName(SMTYPE),
loadManagedInstance(code, true, fmd); "accessingField",
addGetManagedValueCode(code, fmd); Type.getMethodDescriptor(Type.VOID_TYPE, Type.INT_TYPE)));
code.xreturn().setType(fmd.getDeclaredType());
code.calculateMaxStack(); instructions.add(loadManagedInstance(true, fmd));
code.calculateMaxLocals(); addGetManagedValueCode(classNode, instructions, fmd, true);
instructions.add(new InsnNode(AsmHelper.getReturnInsn(fmd.getDeclaredType())));
} }
/** /**
@ -4488,49 +4522,59 @@ public class PCEnhancer {
* @param index the relative number of the field * @param index the relative number of the field
* @param fmd metadata about the field to set * @param fmd metadata about the field to set
*/ */
@Deprecated private void addSetMethod(ClassNode classNode, int index, FieldMetaData fmd) throws NoSuchMethodException {
private void addSetMethod(int index, FieldMetaData fmd) MethodNode method = createSetMethod(classNode, fmd);
throws NoSuchMethodException { classNode.methods.add(method);
BCMethod method = createSetMethod(fmd); final InsnList instructions = method.instructions;
Code code = method.getCode(true);
// PCEnhancer uses static methods; PCSubclasser does not. // PCEnhancer uses static methods; PCSubclasser does not.
int firstParamOffset = getAccessorParameterOffset(fmd); // for a static method there is no 'this', so index starts with zero
// but in that case we have the additional entity parameter on that position!
int fieldParamPos = 1;
// if (inst.pcStateManager == null) inst.<field> = value; // if (inst.pcStateManager == null) inst.<field> = value;
loadManagedInstance(code, true, fmd); instructions.add(loadManagedInstance(true, fmd));
code.getfield().setField(SM, SMTYPE); instructions.add(new FieldInsnNode(Opcodes.GETFIELD, classNode.name, SM, Type.getDescriptor(SMTYPE)));
JumpInstruction ifins = code.ifnonnull();
loadManagedInstance(code, true, fmd); LabelNode lblAfterIfNonNull = new LabelNode();
code.xload().setParam(firstParamOffset); instructions.add(new JumpInsnNode(Opcodes.IFNONNULL, lblAfterIfNonNull));
addSetManagedValueCode(code, fmd); instructions.add(loadManagedInstance(true, fmd));
instructions.add(new VarInsnNode(AsmHelper.getLoadInsn(fmd.getDeclaredType()), fieldParamPos));
addSetManagedValueCode(classNode, instructions, fmd);
if (fmd.isVersion() && _addVersionInitFlag) { if (fmd.isVersion() && _addVersionInitFlag) {
// if we are setting the version, flip the versionInit flag to true // if we are setting the version, flip the versionInit flag to true
loadManagedInstance(code, true);
code.constant().setValue(1); instructions.add(new VarInsnNode(Opcodes.ALOAD, 0)); // this
instructions.add(AsmHelper.getLoadConstantInsn(1));
// pcVersionInit = true; // pcVersionInit = true;
putfield(code, null, VERSION_INIT_STR, boolean.class); instructions.add(new FieldInsnNode(Opcodes.PUTFIELD, classNode.name, VERSION_INIT_STR, Type.BOOLEAN_TYPE.getDescriptor()));
} }
code.vreturn(); instructions.add(new InsnNode(Opcodes.RETURN));
instructions.add(lblAfterIfNonNull);
// inst.pcStateManager.setting<fieldType>Field (inst, // inst.pcStateManager.setting<fieldType>Field (inst,
// pcInheritedFieldCount + <index>, inst.<field>, value, 0); // pcInheritedFieldCount + <index>, inst.<field>, value, 0);
ifins.setTarget(loadManagedInstance(code, true, fmd)); instructions.add(loadManagedInstance(true, fmd));
code.getfield().setField(SM, SMTYPE); instructions.add(new FieldInsnNode(Opcodes.GETFIELD, classNode.name, SM, Type.getDescriptor(SMTYPE)));
loadManagedInstance(code, true, fmd);
code.getstatic().setField(INHERIT, int.class);
code.constant().setValue(index);
code.iadd();
loadManagedInstance(code, true, fmd);
addGetManagedValueCode(code, fmd);
code.xload().setParam(firstParamOffset);
code.constant().setValue(0);
code.invokeinterface().setMethod(getStateManagerMethod
(fmd.getDeclaredType(), "setting", false, true));
code.vreturn();
code.calculateMaxStack(); instructions.add(loadManagedInstance(true, fmd));
code.calculateMaxLocals(); instructions.add(new FieldInsnNode(Opcodes.GETSTATIC, classNode.name, INHERIT, Type.INT_TYPE.getDescriptor()));
instructions.add(AsmHelper.getLoadConstantInsn(index));
instructions.add(new InsnNode(Opcodes.IADD));
instructions.add(loadManagedInstance(true, fmd));
addGetManagedValueCode(classNode, instructions, fmd, true);
instructions.add(new VarInsnNode(AsmHelper.getLoadInsn(fmd.getDeclaredType()), fieldParamPos));
instructions.add(new InsnNode(Opcodes.ICONST_0));
final Method stateMgrMethod = getStateManagerMethod(fmd.getDeclaredType(), "setting", false, true);
instructions.add(new MethodInsnNode(Opcodes.INVOKEINTERFACE,
Type.getInternalName(stateMgrMethod.getDeclaringClass()),
stateMgrMethod.getName(),
Type.getMethodDescriptor(stateMgrMethod)));
instructions.add(new InsnNode(Opcodes.RETURN));
} }
/** /**
@ -5493,13 +5537,28 @@ public class PCEnhancer {
* @return the first instruction added to <code>code</code>. * @return the first instruction added to <code>code</code>.
*/ */
@Deprecated @Deprecated
private Instruction loadManagedInstance(Code code, boolean forStatic, private Instruction loadManagedInstance(Code code, boolean forStatic, FieldMetaData fmd) {
FieldMetaData fmd) {
if (forStatic && isFieldAccess(fmd)) if (forStatic && isFieldAccess(fmd))
return code.aload().setParam(0); return code.aload().setParam(0);
return code.aload().setThis(); return code.aload().setThis();
} }
/**
* Add the {@link Instruction}s to load the instance to modify onto the
* stack, and return it. If <code>forStatic</code> is set, then
* <code>code</code> is in an accessor method or another static method;
* otherwise, it is in one of the PC-specified methods.
*
* @return the first instruction added to <code>code</code>.
*/
private AbstractInsnNode loadManagedInstance(boolean forStatic, FieldMetaData fmd) {
if (forStatic && isFieldAccess(fmd)) {
// 1st method parameter is position 0 on the stack for a STATIC method!
return new VarInsnNode(Opcodes.ALOAD, 0);
}
return new VarInsnNode(Opcodes.ALOAD, 0); // this
}
/** /**
* Add the {@link Instruction}s to load the instance to modify onto the * Add the {@link Instruction}s to load the instance to modify onto the
* stack, and return it. This method should not be used to load static * stack, and return it. This method should not be used to load static
@ -5542,65 +5601,102 @@ public class PCEnhancer {
* Create the generated getter {@link BCMethod} for <code>fmd</code>. The * Create the generated getter {@link BCMethod} for <code>fmd</code>. The
* calling environment will then populate this method's code block. * calling environment will then populate this method's code block.
*/ */
@Deprecated private MethodNode createGetMethod(ClassNode classNode, FieldMetaData fmd) {
private BCMethod createGetMethod(FieldMetaData fmd) {
BCMethod getter;
if (isFieldAccess(fmd)) { if (isFieldAccess(fmd)) {
// static <fieldtype> pcGet<field> (XXX inst) // static <fieldtype> pcGet<field> (XXX inst)
BCField field = _pc.getDeclaredField(fmd.getName()); final FieldNode field = classNode.fields.stream()
getter = _pc.declareMethod(PRE + "Get" + fmd.getName(), fmd. .filter(f -> f.name.equals(fmd.getName()))
getDeclaredType().getName(), new String[]{ _pc.getName() }); .findFirst()
getter.setAccessFlags(field.getAccessFlags() .get();
& ~Constants.ACCESS_TRANSIENT & ~Constants.ACCESS_VOLATILE);
getter.setStatic(true); MethodNode getter = new MethodNode((field.access & ~Constants.ACCESS_TRANSIENT & ~Constants.ACCESS_VOLATILE)
getter.setFinal(true); | Opcodes.ACC_FINAL | Opcodes.ACC_STATIC,
PRE + "Get" + fmd.getName(),
Type.getMethodDescriptor(Type.getType(fmd.getDeclaredType()), Type.getObjectType(classNode.name)),
null, null);
return getter; return getter;
} }
// property access: // property access:
// copy the user's getter method to a new name; we can't just reset // change the user's getter method to a new name and create a new method with the old name
// the name, because that will also reset all calls to the method
Method meth = (Method) fmd.getBackingMember(); Method meth = (Method) fmd.getBackingMember();
getter = _pc.getDeclaredMethod(meth.getName(),
meth.getParameterTypes()); MethodNode getter = AsmHelper.getMethodNode(classNode, meth).get();
BCMethod newgetter = _pc.declareMethod(PRE + meth.getName(),
meth.getReturnType(), meth.getParameterTypes()); // and a new method which replaces the old one
newgetter.setAccessFlags(getter.getAccessFlags()); MethodNode newGetter = new MethodNode(getter.access,
newgetter.makeProtected(); meth.getName(),
transferCodeAttributes(getter, newgetter); Type.getMethodDescriptor(meth),
return getter; null, null);
getter.name = PRE + meth.getName();
getter.access = (getter.access & ~Opcodes.ACC_PUBLIC & ~Opcodes.ACC_PRIVATE) | Opcodes.ACC_PROTECTED;
moveAnnotations(getter, newGetter);
// copy over all ParemeterizedType info if any.
newGetter.signature = getter.signature;
return newGetter;
}
/**
* move the annotations over from the original method to the other method
*/
private void moveAnnotations(MethodNode from, MethodNode to) {
if (from.visibleAnnotations != null) {
if (to.visibleAnnotations == null) {
to.visibleAnnotations = new ArrayList<>();
}
to.visibleAnnotations.addAll(from.visibleAnnotations);
from.visibleAnnotations.clear();
}
} }
/** /**
* Create the generated setter {@link BCMethod} for <code>fmd</code>. The * Create the generated setter {@link BCMethod} for <code>fmd</code>. The
* calling environment will then populate this method's code block. * calling environment will then populate this method's code block.
*/ */
@Deprecated private MethodNode createSetMethod(ClassNode classNode, FieldMetaData fmd) {
private BCMethod createSetMethod(FieldMetaData fmd) {
BCMethod setter;
if (isFieldAccess(fmd)) { if (isFieldAccess(fmd)) {
// static void pcSet<field> (XXX inst, <fieldtype> value) // static void pcSet<field> (XXX inst, <fieldtype> value)
BCField field = _pc.getDeclaredField(fmd.getName()); final FieldNode field = classNode.fields.stream()
setter = _pc.declareMethod(PRE + "Set" + fmd.getName(), void.class, .filter(f -> f.name.equals(fmd.getName()))
new Class[]{ getType(_meta), fmd.getDeclaredType() }); .findFirst()
setter.setAccessFlags(field.getAccessFlags() .get();
& ~Constants.ACCESS_TRANSIENT & ~Constants.ACCESS_VOLATILE); MethodNode setter = new MethodNode((field.access & ~Constants.ACCESS_TRANSIENT & ~Constants.ACCESS_VOLATILE)
setter.setStatic(true); | Opcodes.ACC_FINAL | Opcodes.ACC_STATIC,
setter.setFinal(true); PRE + "Set" + fmd.getName(),
Type.getMethodDescriptor(Type.VOID_TYPE,
Type.getType(getType(_meta)),
Type.getType(fmd.getDeclaredType())),
null, null);
return setter; return setter;
} }
// property access: // property access:
// copy the user's getter method to a new name; we can't just reset // change the user's setter method to a new name and create a new method with the old name
// the name, because that will also reset all calls to the method final MethodNode setter = AsmHelper.getMethodNode(classNode, getSetterName(fmd), void.class, fmd.getDeclaredType()).get();
setter = _pc.getDeclaredMethod(getSetterName(fmd),
new Class[]{ fmd.getDeclaredType() }); final String setterName = setter.name;
BCMethod newsetter = _pc.declareMethod(PRE + setter.getName(),
setter.getReturnName(), setter.getParamNames()); // and a new method which replaces the old one
newsetter.setAccessFlags(setter.getAccessFlags()); MethodNode newSetter = new MethodNode(setter.access,
newsetter.makeProtected(); setterName,
transferCodeAttributes(setter, newsetter); setter.desc,
return setter; null, null);
setter.name = PRE + setterName;
setter.access = (setter.access & ~Opcodes.ACC_PRIVATE & ~Opcodes.ACC_PUBLIC) | Opcodes.ACC_PROTECTED;
moveAnnotations(setter, newSetter);
// copy over all ParemeterizedType info if any.
newSetter.signature = setter.signature;
return newSetter;
} }
private void addGetEnhancementContractVersionMethod(ClassNodeTracker cnt) { private void addGetEnhancementContractVersionMethod(ClassNodeTracker cnt) {

View File

@ -22,6 +22,8 @@ import java.io.InputStream;
import java.lang.reflect.Array; import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Optional;
import org.apache.xbean.asm9.ClassReader; import org.apache.xbean.asm9.ClassReader;
import org.apache.xbean.asm9.ClassWriter; import org.apache.xbean.asm9.ClassWriter;
@ -33,6 +35,7 @@ import org.apache.xbean.asm9.tree.FieldInsnNode;
import org.apache.xbean.asm9.tree.InsnNode; import org.apache.xbean.asm9.tree.InsnNode;
import org.apache.xbean.asm9.tree.IntInsnNode; import org.apache.xbean.asm9.tree.IntInsnNode;
import org.apache.xbean.asm9.tree.LdcInsnNode; import org.apache.xbean.asm9.tree.LdcInsnNode;
import org.apache.xbean.asm9.tree.MethodNode;
import org.apache.xbean.asm9.tree.VarInsnNode; import org.apache.xbean.asm9.tree.VarInsnNode;
import serp.bytecode.BCClass; import serp.bytecode.BCClass;
@ -152,6 +155,24 @@ public final class AsmHelper {
} }
} }
public static Optional<MethodNode> getMethodNode(ClassNode classNode, Method meth) {
final String mDesc = Type.getMethodDescriptor(meth);
return classNode.methods.stream()
.filter(mn -> mn.name.equals(meth.getName()) && mn.desc.equals(mDesc))
.findAny();
}
public static Optional<MethodNode> getMethodNode(ClassNode classNode, String methodName, Class<?> returnType, Class<?>... paramTypes) {
Type[] parms = Arrays.stream(paramTypes)
.map(c -> Type.getType(c))
.toArray(Type[]::new);
final String mDesc = Type.getMethodDescriptor(Type.getType(returnType), parms);
return classNode.methods.stream()
.filter(mn -> mn.name.equals(methodName) && mn.desc.equals(mDesc))
.findAny();
}
/** /**
* Calclates the proper Return instruction opcode for the given class * Calclates the proper Return instruction opcode for the given class
* *