Bug 473549 - [otdre] trouble with decapsulation in a base class
hierarchy
- WIP, regression in test1126_nestingAndLayering4pf
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/IntLiteral.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/IntLiteral.java
index 2fa4070..b1c7fb5 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/IntLiteral.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/IntLiteral.java
@@ -53,6 +53,9 @@
 		}
 		return new IntLiteral(token, intReducedToken != token ? intReducedToken : null, s, e);
 	}
+//{ObjectTeams:
+protected
+// SH}
 IntLiteral(char[] token, char[] reducedForm, int start, int end) {
 	super(token, start, end);
 	this.reducedForm = reducedForm;
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MessageSend.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MessageSend.java
index 1d7dbf4..d6a019b 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MessageSend.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MessageSend.java
@@ -64,6 +64,7 @@
 import static org.eclipse.jdt.internal.compiler.ast.ExpressionContext.*;
 import static org.eclipse.objectteams.otdt.core.compiler.IOTConstants.CALLIN_FLAG_DEFINITELY_MISSING_BASECALL;
 import static org.eclipse.objectteams.otdt.core.compiler.IOTConstants.CALLIN_FLAG_POTENTIALLY_MISSING_BASECALL;
+import static org.eclipse.objectteams.otdt.internal.core.compiler.lookup.SyntheticOTTargetMethod.OTDREMethodDecapsulation;
 
 import java.util.HashMap;
 
@@ -117,7 +118,6 @@
 import org.eclipse.objectteams.otdt.internal.core.compiler.control.ITranslationStates;
 import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.AnchorMapping;
 import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.DependentTypeBinding;
-import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.SyntheticOTTargetMethod;
 import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.SyntheticRoleBridgeMethodBinding;
 import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.WeakenedTypeBinding;
 import org.eclipse.objectteams.otdt.internal.core.compiler.mappings.CalloutImplementor;
@@ -1074,7 +1074,11 @@
 					this.accessId = scope.enclosingSourceType().roleModel.addInaccessibleBaseMethod(this.binding);
 				if (weavingScheme == WeavingScheme.OTDRE) {
 					MethodBinding accessor = CalloutImplementorDyn.ensureAccessor(scope, this.binding.declaringClass, this.binding.isStatic());
-					this.syntheticAccessor = new SyntheticOTTargetMethod.OTDREMethodDecapsulation(accessor, this.binding.parameters, this.binding.returnType, this.accessId, scope, this);
+					OTDREMethodDecapsulation updatableAccessor = new OTDREMethodDecapsulation(accessor, this.binding.parameters, this.binding.returnType, this.accessId, scope, this);
+					ReferenceBinding enclosingType = scope.enclosingSourceType().enclosingType();
+					if (enclosingType != null && enclosingType.isTeam())
+						enclosingType.getTeamModel().recordUpdatableAccessId(updatableAccessor);
+					this.syntheticAccessor = updatableAccessor;
 				} else {
 					this.binding = new MethodBinding(this.binding, this.binding.declaringClass.getRealClass());
 					this.binding.selector = CharOperation.concat(IOTConstants.OT_DECAPS, this.selector);
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/TypeDeclaration.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/TypeDeclaration.java
index 64b95b4..7009d65 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/TypeDeclaration.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/TypeDeclaration.java
@@ -964,6 +964,7 @@
 	}
 	if (isTeam()) {
 		CopyInheritance.copySyntheticTeamMethods(this);
+		getTeamModel().updateDecapsAccessIds();
 	}
 	// value paramters:
 	this.binding.computeValueParameterSlotSizes();
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/ast/BaseAllocationExpression.java b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/ast/BaseAllocationExpression.java
index 53958d7..b98f24d 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/ast/BaseAllocationExpression.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/ast/BaseAllocationExpression.java
@@ -67,6 +67,7 @@
 import org.eclipse.objectteams.otdt.internal.core.compiler.mappings.CalloutImplementorDyn;
 import org.eclipse.objectteams.otdt.internal.core.compiler.model.MethodModel;
 import org.eclipse.objectteams.otdt.internal.core.compiler.model.RoleModel;
+import org.eclipse.objectteams.otdt.internal.core.compiler.model.TeamModel.UpdatableIntLiteral;
 import org.eclipse.objectteams.otdt.internal.core.compiler.util.AstGenerator;
 
 /**
@@ -262,11 +263,13 @@
     	int modifiers = ClassFileConstants.AccPublic|ClassFileConstants.AccStatic;
 		Expression[] arguments = expression.arguments;
 		Expression enclosingInstance = null;
+		ReferenceBinding enclosingTeam = null;
 		if (expression instanceof QualifiedAllocationExpression) {
 			enclosingInstance = ((QualifiedAllocationExpression) expression).enclosingInstance;
+			// TODO: enclosing team (for accessId-updating)?
 		} else if (baseclass.isMemberType()) {
 			// extract the enclosing base instance from an outer playedBy:
-			ReferenceBinding enclosingTeam = scope.enclosingReceiverType().enclosingType();
+			enclosingTeam = scope.enclosingReceiverType().enclosingType();
 			if (enclosingTeam != null
 					&& TypeBinding.equalsEquals(baseclass.enclosingType(), enclosingTeam.baseclass)) {
 				enclosingInstance = gen.fieldReference(
@@ -301,7 +304,7 @@
     	allocSend.constant = Constant.NotAConstant;
     	allocSend.actualReceiverType = baseclass;
     	allocSend.accessId = accessId;
-   		allocSend.arguments = createResolvedAccessArguments(gen, accessId, arguments, scope);
+   		allocSend.arguments = createResolvedAccessArguments(gen, accessId, arguments, enclosingTeam, scope);
     	allocSend.binding = new MethodBinding(modifiers, new TypeBinding[] {
     			TypeBinding.INT,
     			TypeBinding.INT,
@@ -315,9 +318,11 @@
     	return gen.resolvedCastExpression(allocSend, baseclass, CastExpression.RAW);
 	}
 
-	private static Expression[] createResolvedAccessArguments(AstGenerator gen, int accessId, Expression[] arguments, BlockScope scope) {
-		IntLiteral accessIdLiteral = gen.intLiteral(accessId);
+	private static Expression[] createResolvedAccessArguments(AstGenerator gen, int accessId, Expression[] arguments, ReferenceBinding enclosingTeam, BlockScope scope) {
+		UpdatableIntLiteral accessIdLiteral = gen.updatableIntLiteral(accessId);
 		accessIdLiteral.resolveType(scope);
+		if (enclosingTeam != null)
+			enclosingTeam.getTeamModel().recordUpdatableAccessId(accessIdLiteral);
 
 		IntLiteral opKindLiteral = gen.intLiteral(0);
 		opKindLiteral.resolveType(scope);
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/bytecode/OTSpecialAccessAttribute.java b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/bytecode/OTSpecialAccessAttribute.java
index 9f1fbfb..0ebab2b 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/bytecode/OTSpecialAccessAttribute.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/bytecode/OTSpecialAccessAttribute.java
@@ -61,9 +61,11 @@
 	private static final int DYN_CALLOUT_FIELD_ACCESS = 5;
 	private static final int DYN_SUPER_METHOD_ACCESS = 6;
 
-	/*
+	/**
 	 * For OTREDyn, each attribute of this type maintains a set of locally unique (per team) access IDs.
-	 * These IDs are consumed and translated by OTREDyn to obtain those IDs that uniquely identify the
+	 * These IDs are later made unique per team-hierarchy by adding {@link #accessIdOffset}.
+	 * This for of ids is then stored in the attribute.
+	 * Stored ids are consumed and translated by OTREDyn to obtain those global IDs that uniquely identify the
 	 * base feature within a generated _OT$access or _OT$accessStatic method.
 	 * 
 	 * AccessIds are generated during resolve and stored in these AST nodes:
@@ -72,13 +74,18 @@
 	 *     the accessId as an argument for the generated _OT$access[Static] call.
 	 * - MessageSend; accessId is preset for message sends implementing decapsulating BaseAllocationExpression
 	 *   - regular base class:
-	 *     - detected during AllocationExpression.resolveType, throws ConstructorDecapsulationExpression
+	 *     - detected during AllocationExpression.resolveType, throws ConstructorDecapsulationException
 	 *     - allocation is then replaced by a MessageSend to the _OT$access method
 	 *   - base is role:
 	 *     - generated AST has accessId = -1 to be updated during MessageSend.resolveType() if decaps needed
+	 * The UpdatableAccessId representing the accessId in any of these generated ASTs / synthetic binding
+	 * is updated after disambiguation from TeamModel.updateDecapsAccessIds().
 	 */
-	int nextAccessId = 0;
-	
+	public int nextAccessId = 0;
+
+	/** Updated by {@link TeamModel#updateDecapsAccessIds()} to denote the space of ids used by super teams. */
+	public int accessIdOffset;
+
 	/** Descriptor for a decapsulated base-method. */
 	private class DecapsulatedMethodDesc {
 		ReferenceBinding boundBaseclass;
@@ -140,7 +147,7 @@
 				writeName(this.method.signature());
 			}
 			if (OTSpecialAccessAttribute.this._weavingScheme == WeavingScheme.OTDRE)
-				writeUnsignedShort(this.accessId);
+				writeUnsignedShort(this.accessId + OTSpecialAccessAttribute.this.accessIdOffset);
 		}
 
 		public String toString() {
@@ -188,7 +195,7 @@
 		void write() {
 			if (OTSpecialAccessAttribute.this._weavingScheme == WeavingScheme.OTDRE) {
 				writeByte((byte)DYN_CALLOUT_FIELD_ACCESS);
-				writeUnsignedShort(this.accessId);
+				writeUnsignedShort(this.accessId + OTSpecialAccessAttribute.this.accessIdOffset);
 			} else {
 				writeByte((byte)CALLOUT_FIELD_ACCESS);
 			}
@@ -452,5 +459,4 @@
 		}
 		return result.toString();
 	}
-
 }
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/lookup/SyntheticOTTargetMethod.java b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/lookup/SyntheticOTTargetMethod.java
index cfbdca8..a9d2c47 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/lookup/SyntheticOTTargetMethod.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/lookup/SyntheticOTTargetMethod.java
@@ -27,6 +27,7 @@
 import org.eclipse.jdt.internal.compiler.lookup.SyntheticMethodBinding;
 import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
 import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
+import org.eclipse.objectteams.otdt.internal.core.compiler.model.TeamModel;
 
 /**
  * A synthetic method binding, which used as an invocation target needs to
@@ -145,7 +146,7 @@
 	 * Represents a decapsulating method access while targeting OTDRE (not an explicit callout).
 	 * We need to generate a special sequence to call _OT$access(int,int,Object[],ITeam)
 	 */
-	public static class OTDREMethodDecapsulation extends SyntheticOTTargetMethod {
+	public static class OTDREMethodDecapsulation extends SyntheticOTTargetMethod implements TeamModel.UpdatableAccessId {
 
 		private int accessId;
 		private ReferenceBinding enclosingTeam;
@@ -236,6 +237,11 @@
 			}
 			return 0; // signal we're done
 		}
+
+		@Override
+		public void update(int offset) {
+			this.accessId += offset;
+		}
 	}
 	static int size(TypeBinding type) {
 		switch (type.id) {
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/mappings/CalloutImplementorDyn.java b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/mappings/CalloutImplementorDyn.java
index 9dda280..3216538 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/mappings/CalloutImplementorDyn.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/mappings/CalloutImplementorDyn.java
@@ -50,7 +50,8 @@
 	{
 		char[] selector = ensureAccessor(scope, baseType, baseSpec.isStatic()).selector;
 		TeamModel teamModel = roleModel.getTeamModel();
-		Expression accessIdArg = gen.intLiteral(baseSpec.accessId);
+		TeamModel.UpdatableIntLiteral accessIdArg = gen.updatableIntLiteral(baseSpec.accessId);
+		teamModel.recordUpdatableAccessId(accessIdArg); // may need updating before codeGen.
 		int opKind = 0;
 		if (baseSpec instanceof FieldAccessSpec)
 			if (((FieldAccessSpec) baseSpec).calloutModifier == TerminalTokens.TokenNameset)
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/model/TeamModel.java b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/model/TeamModel.java
index 21d4dd1..4ff7f5c 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/model/TeamModel.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/model/TeamModel.java
@@ -30,8 +30,11 @@
 import org.eclipse.jdt.internal.compiler.ClassFile;
 import org.eclipse.jdt.internal.compiler.ast.ASTNode;
 import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.IntLiteral;
 import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
 import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
+import org.eclipse.jdt.internal.compiler.impl.CompilerOptions.WeavingScheme;
+import org.eclipse.jdt.internal.compiler.impl.IntConstant;
 import org.eclipse.jdt.internal.compiler.lookup.Binding;
 import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment;
 import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
@@ -966,4 +969,58 @@
 		}
 		return 0;
 	}
+
+//	=======================================================================================
+//	=  Support for ensuring uniqueness of accessIds among a team and all its super teams. =
+
+	public static interface UpdatableAccessId {
+		void update(int offset);
+	}
+	
+	public static class UpdatableIntLiteral extends IntLiteral implements UpdatableAccessId {
+		int val;
+		public UpdatableIntLiteral(int val, int start, int end) {
+			super(String.valueOf(val).toCharArray(), null, start, end);
+			this.val = val;
+		}
+		@Override
+		public void update(int offset) {
+			this.constant = IntConstant.fromValue(this.val + offset);
+		}
+	}
+
+	/** Region of ids used by this team and its supers. */
+	int accessIdOffset = -1;
+	/** accessIds that may require updating. */
+	List<UpdatableAccessId> updatableAccessIds = null;
+
+	public void recordUpdatableAccessId(UpdatableAccessId updatableAccessId) {
+		if (this.updatableAccessIds == null)
+			this.updatableAccessIds = new ArrayList<>();
+		this.updatableAccessIds.add(updatableAccessId);
+	}
+
+	/** Update all recorded accessIds to stay clear of id ranges used by super teams. */
+	public int updateDecapsAccessIds() {
+		if (this.accessIdOffset > -1 || this.weavingScheme != WeavingScheme.OTDRE) return this.accessIdOffset;
+		
+		this.accessIdOffset = 0;
+
+		TeamModel superTeam = getSuperTeam();
+		if (superTeam != null)
+			this.accessIdOffset += superTeam.updateDecapsAccessIds();
+
+		// update ASTs above super's id range if needed:
+		if (this.updatableAccessIds != null && this.accessIdOffset > 0)
+			for (UpdatableAccessId updater : this.updatableAccessIds)
+				updater.update(this.accessIdOffset);
+
+		if (this._specialAccess != null) {
+			// update attribute if needed:
+			this._specialAccess.accessIdOffset = this.accessIdOffset;
+			// include local value in the total offset:
+			this.accessIdOffset += this._specialAccess.nextAccessId;
+		}
+		return this.accessIdOffset;
+	}
 }
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/model/TypeModel.java b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/model/TypeModel.java
index 4591c43..abf48e5 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/model/TypeModel.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/model/TypeModel.java
@@ -513,7 +513,7 @@
 		assert this._specialAccess == null;
 		this._specialAccess = attribute;
 	}
-	protected OTSpecialAccessAttribute getSpecialAccessAttribute() {
+	public OTSpecialAccessAttribute getSpecialAccessAttribute() {
 		if (this._specialAccess == null) {
 			this._specialAccess = new OTSpecialAccessAttribute(this._binding, getWeavingScheme());
 			addAttribute(this._specialAccess);
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/util/AstGenerator.java b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/util/AstGenerator.java
index 7f6b01c..71986b4 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/util/AstGenerator.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/util/AstGenerator.java
@@ -71,6 +71,7 @@
 import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.ITeamAnchor;
 import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.TThisBinding;
 import org.eclipse.objectteams.otdt.internal.core.compiler.model.RoleModel;
+import org.eclipse.objectteams.otdt.internal.core.compiler.model.TeamModel;
 
 /**
  * This factory creates "generated" elements, ie., elements that have
@@ -334,6 +335,9 @@
 		IntLiteral result = IntLiteral.buildIntLiteral(String.valueOf(val).toCharArray(), this.sourceStart, this.sourceEnd);
 		return result;
 	}
+	public TeamModel.UpdatableIntLiteral updatableIntLiteral(int val) {
+		return new TeamModel.UpdatableIntLiteral(val, this.sourceStart, this.sourceEnd);
+	}
 	public Literal booleanLiteral(boolean val) {
 		MagicLiteral result = val
 			? new TrueLiteral(this.sourceStart, this.sourceEnd)
diff --git a/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/internal/osgi/weaving/DelegatingTransformer.java b/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/internal/osgi/weaving/DelegatingTransformer.java
index 1755f00..9996724 100644
--- a/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/internal/osgi/weaving/DelegatingTransformer.java
+++ b/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/internal/osgi/weaving/DelegatingTransformer.java
@@ -24,7 +24,6 @@
 import java.net.URL;
 import java.security.ProtectionDomain;
 import java.util.Collection;
-import java.util.List;
 
 import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.objectteams.internal.osgi.weaving.OTWeavingHook.WeavingReason;
@@ -134,14 +133,6 @@
 		return new IWeavingContext() {
 			@Override
 			public boolean isWeavable(String className) {
-				// currently, we don't support implicit crossing of bundle boundaries,
-				// so check if the requested class is provided by the current bundle:
-				int lastDot = className.lastIndexOf('.');
-				String path = className.substring(0, lastDot).replace('.', '/');
-				String filePattern = className.substring(lastDot+1)+".class";
-				List<URL> entry = bundleWiring.findEntries(path, filePattern, 0);
-				if (entry == null || entry.isEmpty())
-					return false;
 				return hook.requiresWeaving(bundleWiring, className, null) != WeavingReason.None;
 			}
 		};
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 b41ee8f..10246fe 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
@@ -460,10 +460,9 @@
 	@Override

 	protected void prepareForFirstMemberAccess() {

 		if (!isTransformedForMemberAccess && !isInterface()) {

-			nodes.add(new CreateSwitchForAccessAdapter(ConstantMembers.access,

-					getInternalSuperClassName(), this));

-			nodes.add(new CreateSwitchForAccessAdapter(ConstantMembers.accessStatic,

-					getInternalSuperClassName(), this));

+			String superClassName = this.weavingContext.isWeavable(getSuperClassName()) ? getInternalSuperClassName() : null;

+			nodes.add(new CreateSwitchForAccessAdapter(ConstantMembers.access, superClassName, this));

+			nodes.add(new CreateSwitchForAccessAdapter(ConstantMembers.accessStatic, superClassName, this));

 			isTransformedForMemberAccess = true;

 		}

 

diff --git a/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/CreateSwitchForAccessAdapter.java b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/CreateSwitchForAccessAdapter.java
index 9d47646..ebeedfe 100644
--- a/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/CreateSwitchForAccessAdapter.java
+++ b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/CreateSwitchForAccessAdapter.java
@@ -53,11 +53,9 @@
 	@Override

 	protected void addPreSwitchInstructions(MethodNode method) {

 		// put "accessId" on the stack

-		method.instructions.add(new IntInsnNode(Opcodes.ILOAD,

-				getFirstArgIndex()));

+		method.instructions.add(new IntInsnNode(Opcodes.ILOAD, getFirstArgIndex()));

 		// put "caller".getClass() on the stack

-		method.instructions.add(new IntInsnNode(Opcodes.ALOAD,

-				getFirstArgIndex() + 3));

+		method.instructions.add(new IntInsnNode(Opcodes.ALOAD, getFirstArgIndex() + 3));

 		method.instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;", false));

 		// call "getMemberId(accessId, callerClass)

 		method.instructions

@@ -70,7 +68,7 @@
 	

 	@Override

 	protected void addInstructionForDefaultLabel(MethodNode method) {

-		if (superClassName.equals("java/lang/Object")) {

+		if (superClassName == null || superClassName.equals("java/lang/Object")) {

 			method.instructions.add(new TypeInsnNode(Opcodes.NEW, "org/objectteams/NoSuchMethodError"));

 			method.instructions.add(new InsnNode(Opcodes.DUP));

 			method.instructions.add(new IntInsnNode(Opcodes.ILOAD, getFirstArgIndex())); // accessId

@@ -88,14 +86,14 @@
 			}

 			method.instructions.add(new MethodInsnNode(opcode,

 					superClassName, getMethod().getName(),

-					getMethod().getSignature()));

+					getMethod().getSignature(), false));

 			method.instructions.add(new InsnNode(Opcodes.ARETURN));

 		}

 	}

 	

 	@Override

 	protected int getMaxStack() {

-		return 5;

+		return 6;

 	}

 

 }

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 4a088df..fb4cf2c 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
@@ -41,7 +41,7 @@
 	private static List<List<ITeam>> _teams = new ArrayList<List<ITeam>>();
 	private static List<List<Integer>> _callinIds = new ArrayList<List<Integer>>();
 	private static Map<String, Integer> joinpointMap = new HashMap<String, Integer>();
-	// key: Team class, value: list of global memberIds, indexed by local accessId
+	// key: Team class, value: list of global memberIds, indexed by local accessId, id of null means "not mapped in this team (try super)"
 	private static Map<Class<?>, List<Integer>> accessIdMap = new HashMap<Class<?>, List<Integer>>();
 	private static int currentJoinpointId = 0;
 	// map all original joinpoints to their inherited versions in subclasses
@@ -116,15 +116,14 @@
 	 */
 	public static int getMemberId(int accessId, Class<? extends ITeam> teamClass) {
 		List<Integer> teamMap = accessIdMap.get(teamClass);
-		if (teamMap == null) {
-			// TODO: is it safe to assume that the accessId is the same between sub & super teams?
+		Integer id = -1;
+		if (teamMap == null || (id = teamMap.get(accessId)) == null) {
 			Class<?> superClass = teamClass.getSuperclass();
 			if (ITeam.class.isAssignableFrom(superClass)) {
 				@SuppressWarnings("unchecked") Class<? extends ITeam> superTeam = (Class<? extends ITeam>) superClass;
 				return getMemberId(accessId, superTeam);
 			}
 		}
-		Integer id = teamMap.get(accessId);
 		return id;
 	}
 
@@ -294,7 +293,7 @@
 			int highestAccessId = teem.getHighestAccessId() + 1;
 			accessIds = new ArrayList<Integer>(highestAccessId);
 			for (int i = 0; i <= highestAccessId; i++) {
-				accessIds.add(0);
+				accessIds.add(null);
 			}
 			accessIdMap.put(teamClass, accessIds);
 		}