Bug 537533 - [otdre] empty callAllBindings() may block callin bound to
super

- track need to insert super calls, possibly trigger reweaving
diff --git a/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/AbstractBoundClass.java b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/AbstractBoundClass.java
index 48e3d4b..2a7393c 100644
--- a/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/AbstractBoundClass.java
+++ b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/AbstractBoundClass.java
@@ -207,6 +207,9 @@
 	// already loaded by a class loader or not
 	private boolean isLoaded;
 	protected boolean isUnweavable;
+
+	/** Within one base hierarchy, callOrig and callAllBindings may need to propagate to supers. */
+	protected boolean hierarchyIsCallinAffected = false;
 	
 	private int modifiers;
 
@@ -700,6 +703,9 @@
 					// at load time
 					startTransformation();
 					prepareAsPossibleBaseClass();
+					if (hierarchyIsCallinAffected()) {
+						createSuperCalls();
+					}
 					prepareTeamActivation();
 					prepareLiftingParticipant();
 					endTransformation(definedClass);
@@ -884,6 +890,20 @@
 		superTransformation(definedClass);
 	}
 
+	protected abstract void createSuperCalls();
+	protected abstract void propagateCallinInfraToSubclasses();
+
+	private boolean hierarchyIsCallinAffected() {
+		if (this.hierarchyIsCallinAffected)
+			return true;
+		AbstractBoundClass zuper = getSuperclass();
+		if (!zuper.isJavaLangObject()) {
+			if (zuper.hierarchyIsCallinAffected())
+				return this.hierarchyIsCallinAffected = true;
+		}
+		return false;
+	}
+
 	<K,V> Set<Entry<K, V>> copyEntrySet(Map<K,V> map) {
 		if (map.isEmpty()) return Collections.emptySet();
 		Map<K, V> bindingMap = new HashMap<K, V>();
@@ -938,11 +958,11 @@
 	}
 
 	@Override
-	public synchronized void commitTransaction() {
+	public synchronized void commitTransaction(Class<?> definedClass) {
 		--this.transactionCount;
 		if (this.transactionCount == 0 && this.isLoaded) {
 			try {
-				handleTaskList(null);
+				handleTaskList(definedClass);
 			} catch (IllegalClassFormatException e) {
 				e.printStackTrace(); // we're called from TeamManager, which can neither log nor handle exceptions
 			}
diff --git a/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/AsmWritableBoundClass.java b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/AsmWritableBoundClass.java
index 49cb2c4..25d3f90 100644
--- a/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/AsmWritableBoundClass.java
+++ b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/AsmWritableBoundClass.java
@@ -32,6 +32,7 @@
 import org.eclipse.objectteams.otredyn.runtime.TeamManager;

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

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

+import org.eclipse.objectteams.runtime.IReweavingTask;

 import org.objectweb.asm.ClassReader;

 import org.objectweb.asm.ClassWriter;

 import org.objectweb.asm.Opcodes;

@@ -455,6 +456,27 @@
 			nodes.add(new CreateSwitchForCallAllBindingsNode());

 			nodes.add(new CreateAddRemoveRoleMethod());

 			isTransformed = true;

+

+			hierarchyIsCallinAffected = true;

+			propagateCallinInfraToSubclasses();

+		}

+	}

+

+	/**

+	 * If subclasses have previously added emtpy methods (callOrig,callAllBindings,access),

+	 * those need to be change to a super call, so they don't interfere with real callin dispatch in super

+	 * (likely on behalf of a different sub class).

+	 */

+	protected void propagateCallinInfraToSubclasses() {

+		for (AbstractBoundClass sub : subclasses.keySet()) {

+			if (sub instanceof AsmWritableBoundClass && !sub.isAnonymous()) {

+				AsmWritableBoundClass writableSub = (AsmWritableBoundClass) sub;

+				if (!writableSub.hierarchyIsCallinAffected) {

+					writableSub.hierarchyIsCallinAffected = true;

+					writableSub.createSuperCalls();

+					writableSub.propagateCallinInfraToSubclasses();

+				}

+			}

 		}

 	}

 

@@ -470,6 +492,39 @@
 		}

 	}

 

+	@Override

+	protected void createSuperCalls() {

+		final String internalSuperClassName = getInternalSuperClassName();

+		final Runnable operation = () -> {

+			nodes.add(new CreateSuperCallAdapter(internalSuperClassName, ConstantMembers.callAllBindingsClient));

+			nodes.add(new CreateSuperCallAdapter(internalSuperClassName, ConstantMembers.callOrig));

+			nodes.add(new CreateSuperCallAdapter(internalSuperClassName, ConstantMembers.access));			

+		};

+		if (isTransformationActive) {

+			operation.run();

+		} else if (this.weavingContext != null) {

+			IReweavingTask task = new IReweavingTask() {

+				@Override public void reweave(Class<?> definedClass) throws IllegalClassFormatException {

+					startTransaction();

+					if (!isTransformationActive) {

+						startTransformation();

+					}

+					operation.run();

+					commitTransaction(definedClass);

+				}

+			};

+			if (!weavingContext.scheduleReweaving(this.getName(), task))

+				try {

+					handleTaskList(null);

+				} catch (IllegalClassFormatException e) {

+					// TODO Auto-generated catch block

+					e.printStackTrace();

+				}

+		} else {

+			new IllegalStateException("weavingContext unexpectedly null").printStackTrace();

+		}

+	}

+

 	/**

 	 * Prepares the methods access and accessStatic with an empty

 	 * switch statement

diff --git a/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/CreateSuperCallAdapter.java b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/CreateSuperCallAdapter.java
new file mode 100644
index 0000000..1a1d4d7
--- /dev/null
+++ b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/CreateSuperCallAdapter.java
@@ -0,0 +1,51 @@
+/**********************************************************************
+ * This file is part of "Object Teams Development Tooling"-Software
+ * 
+ * Copyright 2018 GK Software SE
+ *  
+ * 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.objectteams.org for updates and contact.
+ * 
+ * Contributors:
+ * 	Stephan Herrmann - Initial API and implementation
+ **********************************************************************/
+package org.eclipse.objectteams.otredyn.bytecode.asm;
+
+import org.eclipse.objectteams.otredyn.bytecode.Method;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.tree.InsnList;
+import org.objectweb.asm.tree.InsnNode;
+import org.objectweb.asm.tree.MethodInsnNode;
+import org.objectweb.asm.tree.MethodNode;
+
+public class CreateSuperCallAdapter extends AbstractTransformableClassNode {
+	
+	private String superClassName;
+	private Method method;
+
+	public CreateSuperCallAdapter(String superClassName, Method method) {
+		this.superClassName = superClassName;
+		this.method = method;
+	}
+
+	@Override
+	protected boolean transform() {
+		MethodNode methodNode = getMethod(method);
+		InsnList instructions = new InsnList();
+		Type[] args = Type.getArgumentTypes(methodNode.desc);
+		addInstructionsForLoadArguments(instructions, args, false);
+
+		instructions.add(new MethodInsnNode(Opcodes.INVOKESPECIAL,
+				superClassName, method.getName(),
+				method.getSignature(), false));
+		instructions.add(new InsnNode(Opcodes.ARETURN));
+		methodNode.instructions = instructions;
+		methodNode.maxStack = Math.max(methodNode.maxStack, args.length + 1);
+		return true;
+	}
+}
diff --git a/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/transformer/jplis/ObjectTeamsTransformer.java b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/transformer/jplis/ObjectTeamsTransformer.java
index c1037c4..b7eaad4 100644
--- a/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/transformer/jplis/ObjectTeamsTransformer.java
+++ b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/transformer/jplis/ObjectTeamsTransformer.java
@@ -103,10 +103,11 @@
 			if (classBeingRedefined == null && !clazz.isFirstTransformation()) {
 				return clazz.getBytecode();
 			}
+			if (clazz.isTransformationActive()) {
+				return null;
+			}
 			try {
-				if (clazz.isTransformationActive()) {
-					return null;
-				}
+				clazz.startTransaction();
 				clazz = classRepo.getBoundClass(
 						className, classId, classfileBuffer, loader);
 				clazz.setWeavingContext(this.weavingContext);
@@ -120,6 +121,8 @@
 				throw e; // expected, propagate to caller (OT/Equinox?)
 			} catch(Throwable t) {
 				t.printStackTrace();
+			} finally {
+				clazz.commitTransaction(classBeingRedefined);
 			}
 		}
 		clazz.dump(classfileBuffer, "initial");
diff --git a/plugins/org.eclipse.objectteams.runtime/src/org/eclipse/objectteams/otredyn/runtime/IBoundClass.java b/plugins/org.eclipse.objectteams.runtime/src/org/eclipse/objectteams/otredyn/runtime/IBoundClass.java
index ec2c55b..8e8624d 100644
--- a/plugins/org.eclipse.objectteams.runtime/src/org/eclipse/objectteams/otredyn/runtime/IBoundClass.java
+++ b/plugins/org.eclipse.objectteams.runtime/src/org/eclipse/objectteams/otredyn/runtime/IBoundClass.java
@@ -86,7 +86,7 @@
 	/**
 	 * Commit any modifications since the call to startTransaction.
 	 */
-	void commitTransaction();
+	void commitTransaction(Class<?> definedClass);
 
 	/** 
 	 * Add a {@link ISubclassWiringTask wiring task} to be performed when a 
diff --git a/plugins/org.eclipse.objectteams.runtime/src/org/eclipse/objectteams/otredyn/runtime/TeamManager.java b/plugins/org.eclipse.objectteams.runtime/src/org/eclipse/objectteams/otredyn/runtime/TeamManager.java
index 1001a20..7aa9390 100644
--- a/plugins/org.eclipse.objectteams.runtime/src/org/eclipse/objectteams/otredyn/runtime/TeamManager.java
+++ b/plugins/org.eclipse.objectteams.runtime/src/org/eclipse/objectteams/otredyn/runtime/TeamManager.java
@@ -281,7 +281,7 @@
 			}
 		}
 		for (IBoundClass base : baseClasses) {
-			base.commitTransaction();
+			base.commitTransaction(null);
 		}
 	}
 	/**