Bug 436759 [otdre] Finish implementation of OTDRE
- fix DevelopmentExamples.testX11_bindingInheritance4*
  Translate super.m() into super.callOrig() with proper arg packing
diff --git a/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/OtreRedefineStrategy.java b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/OtreRedefineStrategy.java
index 7f854cf..41f6b58 100644
--- a/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/OtreRedefineStrategy.java
+++ b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/OtreRedefineStrategy.java
@@ -30,7 +30,14 @@
 

 	public void redefine(Class<?> clazz, byte[] bytecode) throws ClassNotFoundException, UnmodifiableClassException {

 		ClassDefinition arr_cd[] = { new ClassDefinition(clazz, bytecode) };

-		otreAgent.getInstrumentation().redefineClasses(arr_cd);

+		try {

+			otreAgent.getInstrumentation().redefineClasses(arr_cd);

+		} catch (ClassFormatError cfe) {

+			// error output during redefinition tends to swallow the stack, print it now:

+			System.err.println("Error redifining "+clazz.getName());

+			cfe.printStackTrace();

+			throw cfe;

+		}

 	}

 

 }

diff --git a/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/AsmTypeHelper.java b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/AsmTypeHelper.java
index d59fcb1..5528322 100644
--- a/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/AsmTypeHelper.java
+++ b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/AsmTypeHelper.java
@@ -1,7 +1,7 @@
 /**********************************************************************

  * This file is part of "Object Teams Dynamic Runtime Environment"

  * 

- * Copyright 2009, 2012 Oliver Frank and others.

+ * Copyright 2009, 2014 Oliver Frank and others.

  * 

  * All rights reserved. This program and the accompanying materials

  * are made available under the terms of the Eclipse Public License v1.0

@@ -27,6 +27,14 @@
  * @author Oliver Frank

  */

 class AsmTypeHelper {

+

+	public static AbstractInsnNode getUnboxingInstructionForType(Type primitiveType) {

+		String objectType = getObjectType(primitiveType);

+		if (objectType == null)

+			return new InsnNode(Opcodes.NOP);

+		return getUnboxingInstructionForType(primitiveType, objectType);

+	}

+

 	public static AbstractInsnNode getUnboxingInstructionForType(Type primitiveType, String objectType) {

 		String methodName = primitiveType.getClassName() + "Value";

 		String desc = Type.getMethodDescriptor(primitiveType, new Type[] {});

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 47e1b3f..409d51b 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
@@ -16,13 +16,19 @@
  **********************************************************************/

 package org.eclipse.objectteams.otredyn.bytecode.asm;

 

+import java.util.ArrayList;

+import java.util.List;

+import java.util.ListIterator;

+

 import org.eclipse.objectteams.otredyn.bytecode.Method;

 import org.eclipse.objectteams.otredyn.transformer.names.ConstantMembers;

 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.InsnNode;

 import org.objectweb.asm.tree.IntInsnNode;

+import org.objectweb.asm.tree.MethodInsnNode;

 import org.objectweb.asm.tree.MethodNode;

 import org.objectweb.asm.tree.TypeInsnNode;

 

@@ -68,7 +74,14 @@
 		//Unboxing arguments

 		Type[] args = Type.getArgumentTypes(orgMethod.desc);

 		

+		int boundMethodIdSlot = firstArgIndex;

+		

 		if (args.length > 0) {

+			// move boundMethodId to a higher slot, to make lower slots available for original locals

+			newInstructions.add(new IntInsnNode(Opcodes.ILOAD, boundMethodIdSlot));

+			boundMethodIdSlot = callOrig.maxLocals+1;

+			newInstructions.add(new IntInsnNode(Opcodes.ISTORE, boundMethodIdSlot));

+			

 			newInstructions.add(new IntInsnNode(Opcodes.ALOAD, firstArgIndex + argOffset + 1));

 			

 			int slot = firstArgIndex + argOffset;

@@ -91,6 +104,8 @@
 				slot += arg.getSize();

 			}

 		}

+

+		adjustSuperCalls(orgMethod.instructions, orgMethod.name, args, returnType, boundMethodIdSlot);

 		

 		// replace return of the original method with areturn and box the result value if needed

 		replaceReturn(orgMethod.instructions, returnType);

@@ -99,7 +114,7 @@
 		

 		addNewLabelToSwitch(callOrig.instructions, newInstructions, boundMethodId);

 		

-		// a minimum stacksize of 3 is neede to box the arguments

+		// a minimum stacksize of 3 is needed to box the arguments

 		callOrig.maxStack = Math.max(Math.max(callOrig.maxStack, orgMethod.maxStack), 3);

 		

 		// we have to increment the max. stack size, because we have to put NULL on the stack

@@ -109,4 +124,48 @@
 		callOrig.maxLocals = Math.max(callOrig.maxLocals, orgMethod.maxLocals);

 		return true;

 	}

+	

+	/** To avoid infinite recursion, calls super.m(a1, a2) must be translated to super.callOrig(boundMethodId, new Object[] {a1, a2}). */

+	private void adjustSuperCalls(InsnList instructions, String selector, Type[] args, Type returnType, int boundMethodIdSlot) {

+		// search:

+		List<MethodInsnNode> toReplace = new ArrayList<MethodInsnNode>();

+		ListIterator<AbstractInsnNode> orgMethodIter = instructions.iterator();

+		while (orgMethodIter.hasNext()) {

+			AbstractInsnNode orgMethodNode = orgMethodIter.next();

+			if (orgMethodNode.getOpcode() == Opcodes.INVOKESPECIAL && ((MethodInsnNode)orgMethodNode).name.equals(selector))

+				toReplace.add((MethodInsnNode) orgMethodNode);

+		}

+		if (toReplace.isEmpty())

+			return;

+		// replace:

+		for (MethodInsnNode oldNode : toReplace) {

+			// we need to insert into the loading sequence before the invocation, find the insertion points:

+			AbstractInsnNode[] insertionPoints = StackBalanceAnalyzer.findInsertionPointsBefore(oldNode, args);

+			AbstractInsnNode firstInsert = insertionPoints.length > 0 ? insertionPoints[0] : oldNode;

+			

+			// push first arg to _OT$callOrig():

+			instructions.insertBefore(firstInsert, new IntInsnNode(Opcodes.ILOAD, boundMethodIdSlot));

+			

+			// prepare array as second arg to _OT$callOrig():

+			instructions.insertBefore(firstInsert, new IntInsnNode(Opcodes.BIPUSH, 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));

+				// 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));

+			}

+

+			if (returnType == Type.VOID_TYPE)

+				instructions.insert(oldNode, new InsnNode(Opcodes.POP));

+			else

+				instructions.insert(oldNode, AsmTypeHelper.getUnboxingInstructionForType(returnType));

+

+			instructions.set(oldNode, new MethodInsnNode(Opcodes.INVOKESPECIAL, ((MethodInsnNode)oldNode).owner, callOrig.getName(), callOrig.getSignature()));

+		}

+	}

 }

diff --git a/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/StackBalanceAnalyzer.java b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/StackBalanceAnalyzer.java
new file mode 100644
index 0000000..1c416b8
--- /dev/null
+++ b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/StackBalanceAnalyzer.java
@@ -0,0 +1,87 @@
+/**********************************************************************
+ * This file is part of "Object Teams Dynamic Runtime Environment"
+ * 
+ * Copyright 2014 Stephan Herrmann.
+ * 
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * 
+ * Please visit http://www.eclipse.org/objectteams for updates and contact.
+ * 
+ * Contributors:
+ *		Stephan Herrmann - Initial API and implementation
+ **********************************************************************/
+package org.eclipse.objectteams.otredyn.bytecode.asm;
+
+import org.objectweb.asm.Type;
+import org.objectweb.asm.tree.AbstractInsnNode;
+
+public class StackBalanceAnalyzer {
+
+    /**
+     * Answer an array of insertion points such that each element points to the start of a loading sequence
+     * that produces as many bytes on the stack as are required for the corresponding type in 'args'.
+     * 
+     * @param location point in an instruction list at which all args are present on the stack
+     * @param args types of the arguments on the top of the stack at 'location'
+     * @return one instruction per element in 'args'
+     */
+	public static AbstractInsnNode[] findInsertionPointsBefore(AbstractInsnNode location, Type[] args) {
+		AbstractInsnNode[] nodes = new AbstractInsnNode[args.length];
+		AbstractInsnNode current = location;
+		for (int i = args.length-1; i >= 0; i--) {
+			current = findInsertionPointBefore(current, - args[i].getSize());
+			nodes[args.length-i-1] = current;
+		}
+		return nodes;
+	}
+
+    /**
+     * Answer the closest point in the instruction list before 'location'
+     * where the stack has 'stackDifference' more bytes than at 'location'.
+     * Negative 'stackDifference' means we are looking for smaller stack.
+     * 
+     * @param location start searching here
+     * @param stackDifference 
+     * @return the last instruction before which the required stack size was present 
+     */
+    public static AbstractInsnNode findInsertionPointBefore(AbstractInsnNode location, int stackDifference) {
+    	int offset = 0;    	
+    	AbstractInsnNode current = location;
+    	while (offset != stackDifference) {
+    		current = location.getPrevious();
+    		if (current == null)
+    			return null;
+    		offset -= SIZE[current.getOpcode()];
+    	}
+    	return current;
+    }
+
+	
+	// Field from org.objectweb.asm.Frame, made accessible:
+    /**
+     * The stack size variation corresponding to each JVM instruction. This
+     * stack variation is equal to the size of the values produced by an
+     * instruction, minus the size of the values consumed by this instruction.
+     */
+    static final int[] SIZE;
+
+    /**
+     * Computes the stack size variation corresponding to each JVM instruction.
+     */
+    static {
+        int i;
+        int[] b = new int[202];
+        String s = "EFFFFFFFFGGFFFGGFFFEEFGFGFEEEEEEEEEEEEEEEEEEEEDEDEDDDDD"
+                + "CDCDEEEEEEEEEEEEEEEEEEEEBABABBBBDCFFFGGGEDCDCDCDCDCDCDCDCD"
+                + "CDCEEEEDDDDDDDCDCDCEFEFDDEEFFDEDEEEBDDBBDDDDDDCCCCCCCCEFED"
+                + "DDCDCDEEEEEEEEEEFEEEEEEDDEEDDEE";
+        for (i = 0; i < b.length; ++i) {
+            b[i] = s.charAt(i) - 'E';
+        }
+        SIZE = b;
+    }
+    
+}
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 3c61d84..d6f20d3 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
@@ -887,6 +887,69 @@
             "MyBase.bm");
     }
 
+    // super call in base method - like 4a but with relevant signature
+    public void testX11_bindingInheritance4d() {
+       
+       runConformTest(
+            new String[] {
+		"MainX114d.java",
+			    "\n" +
+			    "public class MainX114d {\n" +
+			    "    public static void main(String[] args) {\n" +
+			    "        MyBaseX114d b = new MyBaseX114d();\n" +
+			    "        MyTeamX114d t = new MyTeamX114d();\n" +
+			    "        t.activate();\n" +
+			    "        System.out.println(b.bm(\"OK\"));\n" +
+			    "        System.out.println(\"----------------\");\n" +
+			    "        MySubSubBaseX114d ssb = new MySubSubBaseX114d();\n" +
+			    "        System.out.println(ssb.bm(\"Hoki\"));\n" +
+			    "    }\n" +
+			    "}   \n" +
+			    "    \n",
+		"MyBaseX114d.java",
+			    "\n" +
+			    "public class MyBaseX114d {\n" +
+			    "    public String bm(String in) {\n" +
+			    "        System.out.println(\"MyBase.bm(\"+in+\")\");\n" +
+			    "        return \"retBase\";\n" +
+			    "    }\n" +
+			    "}\n" +
+			    "    \n",
+		"MySubBaseX114d.java",
+			    "\n" +
+			    "public class MySubBaseX114d extends MyBaseX114d {}\n" +
+			    "    \n",
+		"MySubSubBaseX114d.java",
+			    "\n" +
+			    "public class MySubSubBaseX114d extends MySubBaseX114d {\n" +
+			    "    public String bm(String in) {\n" +
+			    "        String res = super.bm(in);\n" +
+			    "        System.out.println(\"MySubSubBase.bm(\"+in+\")\");\n" +
+			    "        return res;\n" +
+			    "    }\n" +
+			    "}\n" +
+			    "    \n",
+		"MyTeamX114d.java",
+			    "\n" +
+			    "public team class MyTeamX114d {\n" +
+			    "    public class MyRoleX114d playedBy MyBaseX114d {\n" +
+			    "        public void rm(String in) {\n" +
+			    "            System.out.println(\"MyTeam.MyRole.rm(\"+in+\")\");\n" +
+			    "        }\n" +
+			    "        rm <- after bm;\n" +
+			    "    }\n" +
+			    "}\n" +
+			    "    \n"
+            },
+            "MyBase.bm(OK)\n" +
+			"MyTeam.MyRole.rm(OK)\n" +
+			"retBase\n" +
+			"----------------\n" +
+			"MyBase.bm(Hoki)\n" +
+			"MySubSubBase.bm(Hoki)\n" +
+			"MyTeam.MyRole.rm(Hoki)\n" +
+			"retBase");
+    }
     // after callin inherited from super role, before callin to the same base method added
     // X.1.1-otjld-binding-inheritance-5
     public void testX11_bindingInheritance5() {