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);
}
}
/**