Bug 468712: [otdre] stack overflow at callin-bound base method involving
wicked super call
- fixes regarding return value conversions & loading of int constant
diff --git a/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/AbstractTransformableClassNode.java b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/AbstractTransformableClassNode.java
index 1fd9ba9..004f890 100644
--- a/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/AbstractTransformableClassNode.java
+++ b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/AbstractTransformableClassNode.java
@@ -274,16 +274,19 @@
return new InsnNode(Opcodes.ICONST_0+constant);
else if (constant < Byte.MAX_VALUE)
return new IntInsnNode(Opcodes.BIPUSH, constant);
+ else if (constant < Short.MAX_VALUE)
+ return new IntInsnNode(Opcodes.SIPUSH, constant);
else
return new LdcInsnNode(constant);
}
/** Call back interface for {@link #replaceSuperCallsWithCallToCallOrig()}. */
protected interface IBoundMethodIdInsnProvider {
- IntInsnNode getLoadBoundMethodIdInsn(MethodInsnNode methodInsn);
+ AbstractInsnNode getLoadBoundMethodIdInsn(MethodInsnNode methodInsn);
}
- protected void replaceSuperCallsWithCallToCallOrig(InsnList instructions, List<MethodInsnNode> superCalls, IBoundMethodIdInsnProvider insnProvider) {
+ protected void replaceSuperCallsWithCallToCallOrig(InsnList instructions, List<MethodInsnNode> superCalls,
+ boolean returnsJLObject, IBoundMethodIdInsnProvider insnProvider) {
for (MethodInsnNode oldNode : superCalls) {
Type[] args = Type.getArgumentTypes(oldNode.desc);
Type returnType = Type.getReturnType(oldNode.desc);
@@ -296,22 +299,27 @@
instructions.insertBefore(firstInsert, insnProvider.getLoadBoundMethodIdInsn(oldNode));
// prepare array as second arg to _OT$callOrig():
- instructions.insertBefore(firstInsert, new IntInsnNode(Opcodes.BIPUSH, args.length));
+ instructions.insertBefore(firstInsert, createLoadIntConstant(args.length));
instructions.insertBefore(firstInsert, new TypeInsnNode(Opcodes.ANEWARRAY, "java/lang/Object"));
for (int i = 0; i < insertionPoints.length; i++) {
// NB: each iteration has an even stack balance, where the top is the Object[].
instructions.insertBefore(insertionPoints[i], new InsnNode(Opcodes.DUP));
- instructions.insertBefore(insertionPoints[i], new IntInsnNode(Opcodes.BIPUSH, i));
+ instructions.insertBefore(insertionPoints[i], createLoadIntConstant(i));
// leave the original loading sequence in tact and continue at the next point:
AbstractInsnNode insertAt = (i +1 < insertionPoints.length) ? insertionPoints[i+1] : oldNode;
instructions.insertBefore(insertAt, AsmTypeHelper.getBoxingInstructionForType(args[i]));
instructions.insertBefore(insertAt, new InsnNode(Opcodes.AASTORE));
}
+ // before an areturn j.l.Object we don't need any type adjustments
+ // (incl. the case where we change another return to areturn j.l.O):
+ boolean nextIsGeneralizedReturn = false;
AbstractInsnNode next = oldNode.getNext();
- boolean nextIsReturn = next != null && next.getOpcode() >= Opcodes.IRETURN && next.getOpcode() <= Opcodes.ARETURN;
- if (!nextIsReturn) {
+ if (returnsJLObject)
+ nextIsGeneralizedReturn = next != null && next.getOpcode() >= Opcodes.IRETURN && next.getOpcode() <= Opcodes.ARETURN;
+
+ if (!nextIsGeneralizedReturn) {
if (returnType == Type.VOID_TYPE) {
instructions.insert(oldNode, new InsnNode(Opcodes.POP));
} else {
@@ -324,7 +332,7 @@
MethodInsnNode newMethodNode = new MethodInsnNode(Opcodes.INVOKESPECIAL, ((MethodInsnNode)oldNode).owner, callOrig.getName(), callOrig.getSignature(), false);
instructions.set(oldNode, newMethodNode);
- if (next != null && nextIsReturn && next.getOpcode() != Opcodes.ARETURN)
+ if (nextIsGeneralizedReturn && next != null && next.getOpcode() != Opcodes.ARETURN)
instructions.set(next, new InsnNode(Opcodes.ARETURN)); // prevent further manipulation by replaceReturn()
}
}
diff --git a/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/MoveCodeToCallOrigAdapter.java b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/MoveCodeToCallOrigAdapter.java
index 27562f6..e9e3351 100644
--- a/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/MoveCodeToCallOrigAdapter.java
+++ b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/MoveCodeToCallOrigAdapter.java
@@ -147,8 +147,8 @@
if (toReplace.isEmpty())
return;
// replace:
- replaceSuperCallsWithCallToCallOrig(instructions, toReplace, new IBoundMethodIdInsnProvider() {
- @Override public IntInsnNode getLoadBoundMethodIdInsn(MethodInsnNode methodInsn) {
+ replaceSuperCallsWithCallToCallOrig(instructions, toReplace, true, new IBoundMethodIdInsnProvider() {
+ @Override public AbstractInsnNode getLoadBoundMethodIdInsn(MethodInsnNode methodInsn) {
return new IntInsnNode(Opcodes.ILOAD, boundMethodIdSlot);
}
});
diff --git a/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/ReplaceWickedSuperCallsAdapter.java b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/ReplaceWickedSuperCallsAdapter.java
index 279ceba..9aa8b50 100644
--- a/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/ReplaceWickedSuperCallsAdapter.java
+++ b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/ReplaceWickedSuperCallsAdapter.java
@@ -15,6 +15,8 @@
**********************************************************************/
package org.eclipse.objectteams.otredyn.bytecode.asm;
+import static org.eclipse.objectteams.otredyn.transformer.names.ClassNames.OBJECT_SLASH;
+
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -26,8 +28,9 @@
import org.eclipse.objectteams.otredyn.runtime.IMethod;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.InsnList;
-import org.objectweb.asm.tree.IntInsnNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
@@ -118,18 +121,21 @@
// morph an arg-load sequence into its packing variant.
for (Map.Entry<MethodNode,List<MethodInsnNode>> toWeave : instructionsToWeave.entrySet()) {
- InsnList instructions = toWeave.getKey().instructions;
+ MethodNode enclosingMethod = toWeave.getKey();
+ InsnList instructions = enclosingMethod.instructions;
List<MethodInsnNode> superCallsToReplace = toWeave.getValue();
- replaceSuperCallsWithCallToCallOrig(instructions, superCallsToReplace, this);
+ Type returnType = Type.getReturnType(enclosingMethod.desc);
+ boolean returnsJLObject = returnType.getSort() == Type.OBJECT ? returnType.getInternalName().equals(OBJECT_SLASH) : false;
+ replaceSuperCallsWithCallToCallOrig(instructions, superCallsToReplace, returnsJLObject, this);
}
return true;
}
/** Callback. */
- public IntInsnNode getLoadBoundMethodIdInsn(MethodInsnNode methodInsn) {
+ public AbstractInsnNode getLoadBoundMethodIdInsn(MethodInsnNode methodInsn) {
// boundMethodId can be statically determined based on the target method.
IMethod otdreMethod = superclass.getMethod(methodInsn.name, methodInsn.desc, false, false);
int methodID = otdreMethod.getGlobalId(superclass);
- return new IntInsnNode(Opcodes.BIPUSH, methodID);
+ return createLoadIntConstant(methodID);
}
}
diff --git a/testplugins/org.eclipse.objectteams.otdt.tests/otjld/org/eclipse/objectteams/otdt/tests/otjld/regression/DevelopmentExamples.java b/testplugins/org.eclipse.objectteams.otdt.tests/otjld/org/eclipse/objectteams/otdt/tests/otjld/regression/DevelopmentExamples.java
index fd71767..5bd4b84 100644
--- a/testplugins/org.eclipse.objectteams.otdt.tests/otjld/org/eclipse/objectteams/otdt/tests/otjld/regression/DevelopmentExamples.java
+++ b/testplugins/org.eclipse.objectteams.otdt.tests/otjld/org/eclipse/objectteams/otdt/tests/otjld/regression/DevelopmentExamples.java
@@ -961,59 +961,61 @@
"\n" +
"public team class TeamX114e {\n" +
" protected class R playedBy MyBaseX114e {\n" +
- " callin void rm() {\n" +
+ " callin int rm() {\n" +
" System.out.println(\"> R.rm\");\n" +
- " base.rm();\n" +
+ " int res = base.rm();\n" +
" System.out.println(\"< R.rm\");\n" +
+ " return res+1;\n" +
" }\n" +
" rm <- replace bmA, bmB;\n" +
" }\n" +
" public static void main(String[] args) {\n" +
" new TeamX114e().activate();\n" +
" MySub2BaseX114e b = new MySub2BaseX114e();\n" +
- " b.bmA();\n" +
- " b.bmB();\n" +
+ " int res = b.bmA();\n" +
+ " res = b.bmB(res);\n" +
+ " System.out.println(res);\n" +
" }\n" +
"}\n" +
" \n",
"MyBaseX114e.java",
"\n" +
"public class MyBaseX114e {\n" +
- " void bmA() { System.out.println(\"MyBase.bmA\"); }\n" +
- " void bmB() { System.out.println(\"MyBase.bmB\"); }\n" + // 2 methods to challenge reuse of existing ReplaceWickedSuperCallsAdapter (OTDRE)
+ " protected int bmA() { System.out.println(\"MyBase.bmA\"); return 1; }\n" +
+ " protected int bmB(int in) { System.out.println(\"MyBase.bmB\"); return in+2; }\n" + // 2 methods to challenge reuse of existing ReplaceWickedSuperCallsAdapter (OTDRE)
"}\n" +
" \n",
"MySubBaseX114e.java",
"\n" +
"public class MySubBaseX114e extends MyBaseX114e {\n" +
- " void bmA2() {\n" +
+ " protected int bmA2() {\n" +
" System.out.println(\"MySubBase.bmA2\");\n" +
- " super.bmA();\n" +
+ " return super.bmA() + 4;\n" +
" }\n" +
- " void bmA() {\n" +
+ " protected int bmA() {\n" +
" System.out.println(\"MySubBase.bmA\");\n" +
- " bmA2();\n" +
+ " return bmA2() + 8;\n" +
" }\n" +
- " void bmB2() {\n" +
+ " protected int bmB2(int in) {\n" +
" System.out.println(\"MySubBase.bmB2\");\n" +
- " super.bmB();\n" +
+ " return super.bmB(in+16);\n" + // invoke is last before return
" }\n" +
- " void bmB() {\n" +
+ " protected int bmB(int in) {\n" +
" System.out.println(\"MySubBase.bmB\");\n" +
- " bmB2();\n" +
+ " return bmB2(in+32) + 64;\n" +
" }\n" +
"}\n" +
" \n",
"MySub2BaseX114e.java",
"\n" +
"public class MySub2BaseX114e extends MySubBaseX114e {\n" +
- " void bmA() {\n" +
+ " protected int bmA() {\n" +
" System.out.println(\"MySub2Base.bmA\");\n" +
- " super.bmA();\n" +
+ " return super.bmA() + 128;\n" +
" }\n" +
- " void bmB() {\n" +
+ " protected int bmB(int in) {\n" +
" System.out.println(\"MySub2Base.bmB\");\n" +
- " super.bmB();\n" +
+ " return super.bmB(in+256) + 512;\n" +
" }\n" +
"}\n" +
" \n"
@@ -1029,7 +1031,8 @@
"MySubBase.bmB\n" +
"MySubBase.bmB2\n" +
"MyBase.bmB\n" +
-"< R.rm");
+ "< R.rm\n" +
+ "1025");
}
// after callin inherited from super role, before callin to the same base method added