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