Merge commit 'b0d35359aa83b498d87d32dcd0c20d8defd0e8a7'
diff --git a/features/org.eclipse.objectteams.otdt.source.feature/feature.xml b/features/org.eclipse.objectteams.otdt.source.feature/feature.xml
index eca2051..b04cf3b 100644
--- a/features/org.eclipse.objectteams.otdt.source.feature/feature.xml
+++ b/features/org.eclipse.objectteams.otdt.source.feature/feature.xml
@@ -2,7 +2,7 @@
<feature
id="org.eclipse.objectteams.otdt.source.feature"
label="%featureName"
- version="2.3.0.qualifier"
+ version="2.3.1.qualifier"
provider-name="%providerName">
<description url="http://www.eclipse.org/objectteams">
diff --git a/features/org.eclipse.objectteams.otequinox.feature/feature.xml b/features/org.eclipse.objectteams.otequinox.feature/feature.xml
index 09cb1d1..4871187 100644
--- a/features/org.eclipse.objectteams.otequinox.feature/feature.xml
+++ b/features/org.eclipse.objectteams.otequinox.feature/feature.xml
@@ -2,7 +2,7 @@
<feature
id="org.eclipse.objectteams.otequinox"
label="%featureName"
- version="2.3.0.qualifier"
+ version="2.3.1.qualifier"
provider-name="%providerName"
plugin="org.eclipse.objectteams.otequinox"
colocation-affinity="org.eclipse.rcp">
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/AbstractNullAnnotationTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/AbstractNullAnnotationTest.java
index b1be1c8..8d10ee5 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/AbstractNullAnnotationTest.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/AbstractNullAnnotationTest.java
@@ -123,9 +123,15 @@
// runtime options
JavacTestOptions.Excuse.EclipseWarningConfiguredAsError);
}
+//{ObjectTeams: make visible to downstream:
+ protected
+// SH}
void runNegativeTestWithLibs(String[] testFiles, Map customOptions, String expectedErrorLog) {
runNegativeTestWithLibs(false /* flush output directory */, testFiles, customOptions, expectedErrorLog);
}
+//{ObjectTeams: make visible to downstream:
+ protected
+// SH}
void runConformTestWithLibs(String[] testFiles, Map customOptions, String expectedCompilerLog) {
runConformTestWithLibs(false /* flush output directory */, testFiles, customOptions, expectedCompilerLog);
}
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/runtime/LocalVMLauncher.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/runtime/LocalVMLauncher.java
index e3de69a..6f5fa63 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/runtime/LocalVMLauncher.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/runtime/LocalVMLauncher.java
@@ -60,10 +60,12 @@
if (vmName != null && vmName.indexOf("JRockit") != -1) {
return new JRockitVMLauncher();
}
- final String osName = System.getProperty("os.name");
- if (osName.startsWith("Mac")) {
- return new MacVMLauncher();
- }
+//{ObjectTeams: see https://bugs.eclipse.org/bugs/show_bug.cgi?id=413850
+// final String osName = System.getProperty("os.name");
+// if (osName.startsWith("Mac")) {
+// return new MacVMLauncher();
+// }
+// SH}
File file = new File(Util.getJREDirectory() + "/lib/rt.jar");
if (file.exists()) {
return new StandardVMLauncher();
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Argument.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Argument.java
index e4da37c..83b3cd0 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Argument.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Argument.java
@@ -24,6 +24,7 @@
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.impl.Constant;
import org.eclipse.jdt.internal.compiler.lookup.*;
+import org.eclipse.objectteams.otdt.internal.core.compiler.ast.LiftingTypeReference;
/**
* OTDT changes:
@@ -97,6 +98,10 @@
Annotation.isTypeUseCompatible(this.type, scope, this.annotations);
scope.validateNullAnnotation(this.binding.tagBits, this.type, this.annotations);
}
+//{ObjectTeams: LTR needs to do more work:
+ if (this.type instanceof LiftingTypeReference)
+ ((LiftingTypeReference) this.type).updateBindingAndCheckNullness(scope);
+//SH}
}
this.binding.declaration = this;
return this.binding.type; // might have been updated during resolveAnnotations (for typeAnnotations)
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/EqualExpression.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/EqualExpression.java
index 9e244bd..c90401a 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/EqualExpression.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/EqualExpression.java
@@ -36,7 +36,12 @@
public EqualExpression(Expression left, Expression right,int operator) {
super(left,right,operator);
}
+//{ObjectTeams: made overridable:
+/*orig:
private void checkNullComparison(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo, FlowInfo initsWhenTrue, FlowInfo initsWhenFalse) {
+ :giro */
+ protected void checkNullComparison(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo, FlowInfo initsWhenTrue, FlowInfo initsWhenFalse) {
+// SH}
// collect null status of child nodes:
int rightStatus = this.right.nullStatus(flowInfo, flowContext);
@@ -193,9 +198,6 @@
result = FlowInfo.conditional(result.copy(), result.copy());
// TODO (maxime) check, reintroduced copy
}
-//{ObjectTeams: no warnings/errors for generated (null-)checks:
- if (!this.isGenerated)
-// SH}
checkNullComparison(currentScope, flowContext, result, result.initsWhenTrue(), result.initsWhenFalse());
return result;
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/FieldReference.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/FieldReference.java
index 1731a3a..affb2ef 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/FieldReference.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/FieldReference.java
@@ -44,7 +44,6 @@
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
import org.eclipse.jdt.internal.compiler.lookup.Scope;
import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
-import org.eclipse.jdt.internal.compiler.lookup.SyntheticMethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.TagBits;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
@@ -53,6 +52,7 @@
import org.eclipse.objectteams.otdt.internal.core.compiler.ast.CalloutMappingDeclaration;
import org.eclipse.objectteams.otdt.internal.core.compiler.ast.FieldAccessSpec;
import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.RoleTypeBinding;
+import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.SyntheticOTTargetMethod;
import org.eclipse.objectteams.otdt.internal.core.compiler.mappings.CalloutImplementor;
import org.eclipse.objectteams.otdt.internal.core.compiler.model.FieldModel;
import org.eclipse.objectteams.otdt.internal.core.compiler.util.RoleTypeCreator;
@@ -740,7 +740,7 @@
if (this.syntheticAccessors == null)
this.syntheticAccessors = new MethodBinding[2];
this.syntheticAccessors[isSetter ? FieldReference.WRITE : FieldReference.READ] =
- new SyntheticMethodBinding(callout.roleMethodSpec.resolvedMethod, SyntheticMethodBinding.InferredCalloutToField);
+ new SyntheticOTTargetMethod.CalloutToField(callout.roleMethodSpec.resolvedMethod);
fieldBinding = ((FieldAccessSpec)callout.baseMethodSpec).resolvedField;
this.binding = fieldBinding;
setDepth(fieldBinding.isStatic() ? 1 : 0); // static c-t-f needs to pass the enclosing team
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/JavadocSingleTypeReference.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/JavadocSingleTypeReference.java
index 8e7bb3f..7abcefa 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/JavadocSingleTypeReference.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/JavadocSingleTypeReference.java
@@ -18,6 +18,7 @@
import org.eclipse.jdt.internal.compiler.lookup.Binding;
import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
+import org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope;
import org.eclipse.jdt.internal.compiler.lookup.PackageBinding;
import org.eclipse.jdt.internal.compiler.lookup.ProblemReasons;
import org.eclipse.jdt.internal.compiler.lookup.Scope;
@@ -82,15 +83,19 @@
Scope currentScope = scope;
while (currentScope != null) {
if (currentScope instanceof OTClassScope) {
- Scope baseScope = ((OTClassScope)currentScope).getBaseImportScope();
+ CompilationUnitScope baseScope = ((OTClassScope)currentScope).getBaseImportScope(scope);
if (baseScope != null) {
- TypeBinding previousType = this.resolvedType;
- this.resolvedType = null;
- TypeBinding baseImportedType = getTypeBinding(baseScope);
- if (baseImportedType != null && baseImportedType.isValidBinding())
- return this.resolvedType = baseImportedType;
- this.resolvedType = previousType;
- break;
+ try {
+ TypeBinding previousType = this.resolvedType;
+ this.resolvedType = null;
+ TypeBinding baseImportedType = getTypeBinding(baseScope);
+ if (baseImportedType != null && baseImportedType.isValidBinding())
+ return this.resolvedType = baseImportedType;
+ this.resolvedType = previousType;
+ break;
+ } finally {
+ baseScope.originalScope = null;
+ }
}
}
currentScope = currentScope.parent;
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 4f9c41c..b1cd758 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
@@ -73,7 +73,6 @@
import org.eclipse.jdt.internal.compiler.impl.Constant;
import org.eclipse.jdt.internal.compiler.impl.ReferenceContext;
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions.WeavingScheme;
-import org.eclipse.jdt.internal.compiler.lookup.BaseTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.Binding;
import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
import org.eclipse.jdt.internal.compiler.lookup.ExtraCompilerModifiers;
@@ -109,6 +108,7 @@
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;
@@ -117,7 +117,6 @@
import org.eclipse.objectteams.otdt.internal.core.compiler.model.TeamModel;
import org.eclipse.objectteams.otdt.internal.core.compiler.statemachine.copyinheritance.CopyInheritance.RoleConstructorCall;
import org.eclipse.objectteams.otdt.internal.core.compiler.statemachine.transformer.StandardElementGenerator;
-import org.eclipse.objectteams.otdt.internal.core.compiler.util.AstGenerator;
import org.eclipse.objectteams.otdt.internal.core.compiler.util.RoleTypeCreator;
/**
@@ -1054,22 +1053,13 @@
if (this.accessId == -1) // happens for BaseAllocationExpression with role-as-base
this.accessId = scope.enclosingSourceType().roleModel.addInaccessibleBaseMethod(this.binding);
// pretend that accessor method were already there:
- this.binding = new MethodBinding(this.binding, this.binding.declaringClass.getRealClass());
if (weavingScheme == WeavingScheme.OTDRE) {
- if (this.binding.isStatic())
- this.binding.selector = CalloutImplementorDyn.OT_ACCESS_STATIC;
- else
- this.binding.selector = CalloutImplementorDyn.OT_ACCESS;
- TypeBinding originalReturnType = this.binding.returnType;
- if (originalReturnType.id != TypeIds.T_void) {
- if (originalReturnType.isBaseType()) {
- this.valueCast = scope.getType(AstGenerator.boxTypeName((BaseTypeBinding) originalReturnType), 3);
- computeConversion(scope, this.valueCast, originalReturnType);
- } else {
- this.valueCast = originalReturnType;
- }
- }
+ MethodBinding accessor = CalloutImplementorDyn.ensureAccessor(scope, this.binding.declaringClass, this.binding.isStatic());
+ if (this.accessId == 0)
+ this.accessId = scope.enclosingSourceType().roleModel.addInaccessibleBaseMethod(this.binding);
+ this.syntheticAccessor = new SyntheticOTTargetMethod.OTDREMethodDecapsulation(accessor, this.binding.parameters, this.binding.returnType, this.accessId, scope, this);
} 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/ParameterizedSingleTypeReference.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ParameterizedSingleTypeReference.java
index 24a009a..d9e4fd1 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ParameterizedSingleTypeReference.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ParameterizedSingleTypeReference.java
@@ -244,20 +244,24 @@
this.typeArguments = new TypeReference[len], 0, len); // FIXME(SH): reducing this array conflicts with loop condition
// note: handling of arrays differs for role and regular types
- if (len == 0)
+ if (len == 0) {
+ resolveAnnotations(scope, location);
+ if (checkBounds)
+ checkNullConstraints(scope, this.typeArguments);
return this.resolvedType; // we're done
+ }
// proceed with a word of warning:
scope.problemReporter().experimentalFeature(this, "Implementation for mixed type and value parameters is experimental.");
}
}
// find a base import scope if that's allowed:
- Scope importScope = null;
+ CompilationUnitScope importScope = null;
if (getBaseclassDecapsulation().isAllowed()) {
Scope currentScope = scope;
while (currentScope != null) {
if (currentScope instanceof OTClassScope) {
- importScope = ((OTClassScope)currentScope).getBaseImportScope();
+ importScope = ((OTClassScope)currentScope).getBaseImportScope(scope);
if (importScope != null)
break;
}
@@ -268,6 +272,8 @@
/* orig:
TypeBinding type = internalResolveLeafType(scope, enclosingType, checkBounds);
:giro */
+ if (importScope != null)
+ importScope.originalScope = null;
// SH}
// handle three different outcomes:
@@ -553,11 +559,15 @@
Scope currentScope = scope;
while (currentScope != null) {
if (currentScope instanceof OTClassScope) {
- Scope baseImportScope = ((OTClassScope)currentScope).getBaseImportScope();
+ CompilationUnitScope baseImportScope = ((OTClassScope)currentScope).getBaseImportScope(scope);
if (baseImportScope != null) {
- this.resolvedType = getTypeBinding(baseImportScope);
- if (this.resolvedType != null && this.resolvedType.isValidBinding())
- return this.resolvedType = checkResolvedType(this.resolvedType, baseImportScope, location, false);
+ try {
+ this.resolvedType = getTypeBinding(baseImportScope);
+ if (this.resolvedType != null && this.resolvedType.isValidBinding())
+ return this.resolvedType = checkResolvedType(this.resolvedType, baseImportScope, location, false);
+ } finally {
+ baseImportScope.originalScope = null;
+ }
}
}
currentScope = currentScope.parent;
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedNameReference.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedNameReference.java
index c2e277c..b02a358 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedNameReference.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedNameReference.java
@@ -34,6 +34,7 @@
import org.eclipse.jdt.internal.compiler.flow.FlowContext;
import org.eclipse.jdt.internal.compiler.flow.FlowInfo;
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
+import org.eclipse.jdt.internal.compiler.impl.CompilerOptions.WeavingScheme;
import org.eclipse.jdt.internal.compiler.impl.Constant;
import org.eclipse.jdt.internal.compiler.lookup.Binding;
import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
@@ -59,9 +60,11 @@
import org.eclipse.objectteams.otdt.core.compiler.IOTConstants;
import org.eclipse.objectteams.otdt.internal.core.compiler.ast.CalloutMappingDeclaration;
import org.eclipse.objectteams.otdt.internal.core.compiler.ast.FieldAccessSpec;
+import org.eclipse.objectteams.otdt.internal.core.compiler.ast.MethodSpec.ImplementationStrategy;
import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.DependentTypeBinding;
import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.ITeamAnchor;
import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.RoleTypeBinding;
+import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.SyntheticOTTargetMethod;
import org.eclipse.objectteams.otdt.internal.core.compiler.mappings.CalloutImplementor;
import org.eclipse.objectteams.otdt.internal.core.compiler.model.FieldModel;
import org.eclipse.objectteams.otdt.internal.core.compiler.model.TeamModel;
@@ -854,7 +857,7 @@
// realize decapsulation by simulating an inferred callout-to-field
field = closestField;
scope.problemReporter().decapsulation(this, field, scope);
- accessAsCalloutToField(enclosingReceiver, field, index);
+ accessAsCalloutToField(scope, enclosingReceiver, field, index, scope.compilerOptions().weavingScheme);
fixedByDecapsulation = true;
}
}
@@ -924,19 +927,23 @@
}
//{ObjectTeams: replace this field reference with an accessor call (simulated as callout-to-field):
-private void accessAsCalloutToField(ReferenceBinding enclosingReceiver, FieldBinding baseclassField, int idx)
+private void accessAsCalloutToField(BlockScope scope, ReferenceBinding enclosingReceiver, FieldBinding baseclassField, int idx, WeavingScheme weaving)
{
ReferenceBinding baseClass = baseclassField.declaringClass;
// manually create and add binding as if it were a callout to field:
- final MethodBinding fakedAccessorBinding = FieldModel.getDecapsulatingFieldAccessor(baseClass, baseclassField, true);
- baseClass.addMethod(fakedAccessorBinding);
+ ImplementationStrategy strategy = weaving == WeavingScheme.OTDRE ? ImplementationStrategy.DYN_ACCESS : ImplementationStrategy.DECAPS_WRAPPER;
+ final MethodBinding fakedAccessorBinding = FieldModel.getDecapsulatingFieldAccessor(scope, baseClass, baseclassField, true, strategy);
// record the need to have the OTRE create the accessor:
- enclosingReceiver.roleModel.addAccessedBaseField(baseclassField, TerminalTokens.TokenNameget);
+ int accessid = enclosingReceiver.roleModel.addAccessedBaseField(baseclassField, TerminalTokens.TokenNameget);
+ SyntheticMethodBinding accessor = (strategy == ImplementationStrategy.DYN_ACCESS)
+ ? new SyntheticOTTargetMethod.OTDREFieldDecapsulation(fakedAccessorBinding, baseclassField.type, accessid, 0/*get*/, scope, this)
+ : new SyntheticOTTargetMethod.CalloutToField(fakedAccessorBinding);
+
// convert to a synthetic method that generateCode can use:
- setSyntheticAccessor(baseclassField, idx, new SyntheticMethodBinding(fakedAccessorBinding, SyntheticMethodBinding.InferredCalloutToField));
+ setSyntheticAccessor(baseclassField, idx, accessor);
}
// SH}
@@ -1304,8 +1311,7 @@
if (callout != null) {
if (this.syntheticReadAccessors == null)
this.syntheticReadAccessors = new SyntheticMethodBinding[this.tokens.length];
- this.syntheticReadAccessors[0] =
- new SyntheticMethodBinding(callout.roleMethodSpec.resolvedMethod, SyntheticMethodBinding.InferredCalloutToField);
+ this.syntheticReadAccessors[0] = new SyntheticOTTargetMethod.CalloutToField(callout.roleMethodSpec.resolvedMethod);
FieldBinding baseField = ((FieldAccessSpec)callout.baseMethodSpec).resolvedField;
// update resolved information:
this.binding = baseField;
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/SingleNameReference.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/SingleNameReference.java
index f20c7f1..bce8fbf 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/SingleNameReference.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/SingleNameReference.java
@@ -54,6 +54,7 @@
import org.eclipse.jdt.internal.compiler.problem.ProblemSeverities;
import org.eclipse.objectteams.otdt.internal.core.compiler.ast.CalloutMappingDeclaration;
import org.eclipse.objectteams.otdt.internal.core.compiler.ast.FieldAccessSpec;
+import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.SyntheticOTTargetMethod;
import org.eclipse.objectteams.otdt.internal.core.compiler.mappings.CalloutImplementor;
import org.eclipse.objectteams.otdt.internal.core.compiler.model.TeamModel;
import org.eclipse.objectteams.otdt.internal.core.compiler.util.RoleTypeCreator;
@@ -1089,7 +1090,7 @@
if (this.syntheticAccessors == null)
this.syntheticAccessors = new MethodBinding[2];
this.syntheticAccessors[isSetter ? FieldReference.WRITE : FieldReference.READ] =
- new SyntheticMethodBinding(callout.roleMethodSpec.resolvedMethod, SyntheticMethodBinding.InferredCalloutToField);
+ new SyntheticOTTargetMethod.CalloutToField(callout.roleMethodSpec.resolvedMethod);
FieldBinding baseField = ((FieldAccessSpec)callout.baseMethodSpec).resolvedField;
// update resolved information:
this.binding = baseField;
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/SingleTypeReference.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/SingleTypeReference.java
index 658bc0f..7ee4c81 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/SingleTypeReference.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/SingleTypeReference.java
@@ -137,11 +137,15 @@
Scope currentScope = scope;
while (currentScope != null) {
if (currentScope instanceof OTClassScope) {
- Scope baseImportScope = ((OTClassScope)currentScope).getBaseImportScope();
+ CompilationUnitScope baseImportScope = ((OTClassScope)currentScope).getBaseImportScope(scope);
if (baseImportScope != null) {
- this.resolvedType = getTypeBinding(baseImportScope);
- if (this.resolvedType != null && this.resolvedType.isValidBinding())
- return this.resolvedType = checkResolvedType(this.resolvedType, baseImportScope, location, false);
+ try {
+ this.resolvedType = getTypeBinding(baseImportScope);
+ if (this.resolvedType != null && this.resolvedType.isValidBinding())
+ return this.resolvedType = checkResolvedType(this.resolvedType, baseImportScope, location, false);
+ } finally {
+ baseImportScope.originalScope = null;
+ }
}
}
currentScope = currentScope.parent;
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/codegen/CodeStream.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/codegen/CodeStream.java
index a065983..c99094a 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/codegen/CodeStream.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/codegen/CodeStream.java
@@ -60,6 +60,7 @@
import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.ITeamAnchor;
import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.RoleTypeBinding;
import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.SyntheticOTMethodBinding;
+import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.SyntheticOTTargetMethod;
import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.SyntheticRoleFieldAccess;
import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.WeakenedTypeBinding;
@@ -4420,16 +4421,17 @@
if ((declaringClass.tagBits & TagBits.ContainsNestedTypeReferences) != 0) {
Util.recordNestedType(this.classFile, declaringClass);
}
-//{ObjectTeams: invoking role field accessors works differently:
+//{ObjectTeams: some OT accessor methods require different codegen:
if (opcode == Opcodes.OPC_invokestatic) {
- if (methodBinding instanceof SyntheticRoleFieldAccess) {
- // these have generation built in:
- ((SyntheticRoleFieldAccess)methodBinding).generateInvoke(this);
- return;
- }
- // roles as parameter for field access may need casting:
- if (methodBinding instanceof SyntheticMethodBinding)
- opcode = checkInvokeCalloutToField(opcode, methodBinding);
+ if (methodBinding instanceof SyntheticOTTargetMethod) {
+ // these have generation built in:
+ opcode = ((SyntheticOTTargetMethod)methodBinding).prepareOrGenerateInvocation(this, opcode);
+ } else if (methodBinding instanceof SyntheticMethodBinding) {
+ // roles as parameter for field access may need casting:
+ opcode = checkInvokeCalloutToField(opcode, (SyntheticMethodBinding)methodBinding);
+ }
+ if (opcode == 0)
+ return;
}
// SH}
// compute receiverAndArgsSize
@@ -4515,38 +4517,31 @@
// SH}
}
//{ObjectTeams: misguided callout-to-field accessor??
-private byte checkInvokeCalloutToField(byte opcode, MethodBinding methodBinding) {
- if ( !methodBinding.isStatic()
- && ((SyntheticMethodBinding)methodBinding).purpose == SyntheticMethodBinding.InferredCalloutToField)
+private byte checkInvokeCalloutToField(byte opcode, SyntheticMethodBinding accessor) {
+ if ( ( accessor.purpose == SyntheticMethodBinding.FieldReadAccess
+ || accessor.purpose == SyntheticMethodBinding.FieldWriteAccess)
+ && accessor.parameters.length > 0 // for static fields we have no parameter.
+ && accessor.parameters[0].isRole())
{
- opcode = Opcodes.OPC_invokevirtual;
- } else {
- SyntheticMethodBinding accessor = (SyntheticMethodBinding)methodBinding;
- if ( ( accessor.purpose == SyntheticMethodBinding.FieldReadAccess
- || accessor.purpose == SyntheticMethodBinding.FieldWriteAccess)
- && accessor.parameters.length > 0 // for static fields we have no parameter.
- && accessor.parameters[0].isRole())
- {
- boolean wide = false;
- if (accessor.purpose == SyntheticMethodBinding.FieldWriteAccess) {
- wide = accessor.targetWriteField.type.id == TypeIds.T_long
- || accessor.targetWriteField.type.id == TypeIds.T_double;
- // expose first arg of two (role, value)
- if (wide) {
- dup2_x1(); // ref, wide -> wide, ref, wide
- pop2(); // -> wide, ref
- } else
- this.swap();
- }
- this.checkcast(methodBinding.parameters[0]);
- if (accessor.purpose == SyntheticMethodBinding.FieldWriteAccess) {
- // revert the above swap:
- if (wide) {
- dup_x2(); // wide, ref -> ref, wide, ref
- pop(); // -> ref, wide
- } else
- this.swap();
- }
+ boolean wide = false;
+ if (accessor.purpose == SyntheticMethodBinding.FieldWriteAccess) {
+ wide = accessor.targetWriteField.type.id == TypeIds.T_long
+ || accessor.targetWriteField.type.id == TypeIds.T_double;
+ // expose first arg of two (role, value)
+ if (wide) {
+ dup2_x1(); // ref, wide -> wide, ref, wide
+ pop2(); // -> wide, ref
+ } else
+ this.swap();
+ }
+ this.checkcast(accessor.parameters[0]);
+ if (accessor.purpose == SyntheticMethodBinding.FieldWriteAccess) {
+ // revert the above swap:
+ if (wide) {
+ dup_x2(); // wide, ref -> ref, wide, ref
+ pop(); // -> ref, wide
+ } else
+ this.swap();
}
}
return opcode;
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/CompilationUnitScope.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/CompilationUnitScope.java
index 0c2295e..2880794 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/CompilationUnitScope.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/CompilationUnitScope.java
@@ -91,6 +91,9 @@
private boolean skipCachingImports;
boolean connectingHierarchy;
+//{ObjectTeams: when used as a baseimport scope, remember the original scope during this current lookup
+ public Scope originalScope;
+// SH}
public CompilationUnitScope(CompilationUnitDeclaration unit, LookupEnvironment environment) {
super(COMPILATION_UNIT_SCOPE, null);
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/Scope.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/Scope.java
index eeb4bb2..9105cb7 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/Scope.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/Scope.java
@@ -5660,6 +5660,13 @@
case Scope.METHOD_SCOPE :
resolutionScope = (BlockScope) scope;
break;
+//{ObjectTeams: consider baseImportScopes:
+ case Scope.COMPILATION_UNIT_SCOPE:
+ Scope originalScope = ((CompilationUnitScope)scope).originalScope;
+ if (originalScope instanceof BlockScope)
+ return (BlockScope) originalScope;
+ break;
+// SH}
}
return resolutionScope;
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java
index 9623091..2133ad4 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java
@@ -66,6 +66,7 @@
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
import org.eclipse.jdt.internal.compiler.impl.Constant;
+import org.eclipse.jdt.internal.compiler.impl.CompilerOptions.WeavingScheme;
import org.eclipse.jdt.internal.compiler.parser.Parser;
import org.eclipse.jdt.internal.compiler.problem.ProblemSeverities;
import org.eclipse.jdt.internal.compiler.util.SimpleLookupTable;
@@ -77,6 +78,7 @@
import org.eclipse.objectteams.otdt.internal.core.compiler.ast.RoleFileCache;
import org.eclipse.objectteams.otdt.internal.core.compiler.ast.TypeAnchorReference;
import org.eclipse.objectteams.otdt.internal.core.compiler.ast.TypeValueParameter;
+import org.eclipse.objectteams.otdt.internal.core.compiler.ast.MethodSpec.ImplementationStrategy;
import org.eclipse.objectteams.otdt.internal.core.compiler.control.*;
import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.AnchorMapping;
import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.SyntheticBaseCallSurrogate;
@@ -2351,12 +2353,16 @@
ReferenceBinding originalRole = field.declaringClass;
if (field.copyInheritanceSrc != null)
originalRole = field.copyInheritanceSrc.declaringClass;
- inner = FieldModel.getDecapsulatingFieldAccessor(this, field, true/*isGetter*/);
- ((SourceTypeBinding) enclosingType()).addSyntheticRoleMethodBridge(this, originalRole, inner, SyntheticMethodBinding.RoleMethodBridgeOuter);
- if (!field.isFinal()) { // no setter for final (includes all static role fields)
- // otherwise we would have to handle different signatures (w/ w/o role arg), which we currently don't
- inner = FieldModel.getDecapsulatingFieldAccessor(this, field, false/*isGetter*/);
+ ImplementationStrategy strategy = this.scope.compilerOptions().weavingScheme == WeavingScheme.OTDRE
+ ? ImplementationStrategy.DYN_ACCESS : ImplementationStrategy.DECAPS_WRAPPER;
+ if (strategy != ImplementationStrategy.DYN_ACCESS) {
+ inner = FieldModel.getDecapsulatingFieldAccessor(this.scope, this, field, true/*isGetter*/, strategy);
((SourceTypeBinding) enclosingType()).addSyntheticRoleMethodBridge(this, originalRole, inner, SyntheticMethodBinding.RoleMethodBridgeOuter);
+ if (!field.isFinal()) { // no setter for final (includes all static role fields)
+ // otherwise we would have to handle different signatures (w/ w/o role arg), which we currently don't
+ inner = FieldModel.getDecapsulatingFieldAccessor(this.scope, this, field, false/*isGetter*/, strategy);
+ ((SourceTypeBinding) enclosingType()).addSyntheticRoleMethodBridge(this, originalRole, inner, SyntheticMethodBinding.RoleMethodBridgeOuter);
+ }
}
}
// SH}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SyntheticMethodBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SyntheticMethodBinding.java
index f3afea9..73a188d 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SyntheticMethodBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SyntheticMethodBinding.java
@@ -69,6 +69,7 @@
public final static int InferredCalloutToField = 18; // calling an inferred callout-to-field
public final static int RoleMethodBridgeOuter = 19; // a team-level bridge method towards a private role method (for callout)
public final static int RoleMethodBridgeInner = 20; // a role-level bridge method towards a private role method (for callout)
+ public final static int MethodDecapsulation = 21;
// SH}
public int sourceStart = 0; // start position of the matching declaration
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Parser.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Parser.java
index 177da4b..b62c2d4 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Parser.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Parser.java
@@ -3123,9 +3123,13 @@
optimizedConcatNodeLists();
}
protected void consumeCatchFormalParameter() {
+//{ObjectTeams: support LiftingTypeReference:
+// orig:
+// CatchFormalParameter ::= Modifiersopt CatchType VariableDeclaratorId
+// :giro
// CatchFormalParameter ::= Modifiersopt CatchType CatchLiftingTypeopt VariableDeclaratorId
// CatchLiftingType ::= 'as' Type
-
+// SH}
this.identifierLengthPtr--;
char[] identifierName = this.identifierStack[this.identifierPtr];
long namePositions = this.identifierPositionStack[this.identifierPtr--];
@@ -6671,20 +6675,6 @@
}
protected void consumeOneMoreTypeAnnotation() {
// TypeAnnotations ::= TypeAnnotations TypeAnnotation
-//{ObjectTeams: inside a sentinel-delimited list?
- if (this.typeAnnotationLengthPtr > 1) {
- int l = this.typeAnnotationLengthStack[this.typeAnnotationLengthPtr-2];
- if (this.typeAnnotationPtr - l - 1 > -1) {
- if (this.typeAnnotationStack[this.typeAnnotationPtr - l - 1] == annotationSentinel) {
- // so we have a sentinal delimited list of annotations at lenghts[-2]
- // merge into that list rather than in into the list at [-1]
- this.typeAnnotationLengthStack[this.typeAnnotationLengthPtr-2]++;
- this.typeAnnotationLengthStack[--this.typeAnnotationLengthPtr] = 0;
- return;
- }
- }
- }
-// SH}
this.typeAnnotationLengthStack[--this.typeAnnotationLengthPtr]++;
}
protected void consumeNameArrayType() {
@@ -7602,6 +7592,11 @@
// PushZeroTypeAnnotations ::= $empty
// Name ::= SimpleName
// TypeAnnotationsopt ::= $empty
+//{ObjectTeams: check presence of annotation sentinel (no longer needed, ambiguous tokens will be interpreted as type annotation):
+ if (confirmTypeAnnotation()) {
+ return;
+ }
+// SH}
pushOnTypeAnnotationLengthStack(0); // signal absence of @308 annotations.
}
// This method is part of an automatic generation : do NOT edit-modify
@@ -7687,6 +7682,10 @@
consumeZeroTypeAnnotations();
break;
+ case 85 : if (DEBUG) { System.out.println("TypeAnnotationsopt -> TypeAnnotations"); } //$NON-NLS-1$
+ confirmTypeAnnotation();
+ break;
+
case 88 : if (DEBUG) { System.out.println("TypeAnnotations0 ::= TypeAnnotations0 TypeAnnotation"); } //$NON-NLS-1$
consumeOneMoreTypeAnnotation();
break;
@@ -9390,6 +9389,10 @@
consumeTypeArgument();
break;
+ case 760 : if (DEBUG) { System.out.println("TypeAnchorOrAnnotatedTypeArgument -> AnyTypeAnchor"); } //$NON-NLS-1$
+ confirmTypeAnchor();
+ break;
+
case 761 : if (DEBUG) { System.out.println("TypeAnchorOrAnnotatedTypeArgument ::=..."); } //$NON-NLS-1$
consumeTypeArgumentFromAnchor();
break;
@@ -9398,6 +9401,10 @@
consumeAnnotationsOnTypeArgumentFromAnchor();
break;
+ case 763 : if (DEBUG) { System.out.println("TypeAnchorOrAnnotatedTypeArgument1 ::= AnyTypeAnchor..."); } //$NON-NLS-1$
+ confirmTypeAnchor();
+ break;
+
case 764 : if (DEBUG) { System.out.println("TypeAnchorOrAnnotatedTypeArgument1 ::=..."); } //$NON-NLS-1$
consumeAnnotationsOnTypeArgumentFromAnchor();
break;
@@ -9406,6 +9413,10 @@
consumeAnnotationsOnTypeArgumentFromAnchor();
break;
+ case 766 : if (DEBUG) { System.out.println("TypeAnchorOrAnnotatedTypeArgument2 ::= AnyTypeAnchor..."); } //$NON-NLS-1$
+ confirmTypeAnchor();
+ break;
+
case 767 : if (DEBUG) { System.out.println("TypeAnchorOrAnnotatedTypeArgument2 ::=..."); } //$NON-NLS-1$
consumeAnnotationsOnTypeArgumentFromAnchor();
break;
@@ -9414,6 +9425,10 @@
consumeAnnotationsOnTypeArgumentFromAnchor();
break;
+ case 769 : if (DEBUG) { System.out.println("TypeAnchorOrAnnotatedTypeArgument3 ::= AnyTypeAnchor..."); } //$NON-NLS-1$
+ confirmTypeAnchor();
+ break;
+
case 770 : if (DEBUG) { System.out.println("TypeAnchorOrAnnotatedTypeArgument3 ::=..."); } //$NON-NLS-1$
consumeAnnotationsOnTypeArgumentFromAnchor();
break;
@@ -11406,6 +11421,9 @@
}
// SH}
//{ObjectTeams: new syntax for dependent types.
+
+//==== handle situations of TentativeTypeAnchor: could be either a TypeAnchor or a TypeAnnotation ====
+
protected void consumeTypeAnchor(boolean haveBase) {
// TentativeTypeAnchor ::= '@OT' UnannotatableName
// TypeAnchor ::= '@OT' 'base'
@@ -11413,6 +11431,8 @@
// see also skipThisAnchor() and consumeQualifiedBaseTypeAnchor() for related productions
+ // could be either TypeAnchor or TypeAnnotation
+ // create TypeAnchor for now, two avoid awaiting the decision between Annotation w or w/o member values
NameReference anchor = haveBase ?
newBaseReference()
: getUnspecifiedReference(false);
@@ -11420,9 +11440,11 @@
// anchor has no type annotations, yet it will be consumed in a context where type annotations are possible
pushOnTypeAnnotationLengthStack(0);
}
+
// this sentinel annotation is pushed below a type annotation that was converted from a type anchor.
// it signals to a subsequent type annotation that it shall be merged into the existing list
static final Annotation annotationSentinel = new MarkerAnnotation(new SingleTypeReference("annotationSentinel".toCharArray(), 0), 0); //$NON-NLS-1$
+
protected void convertTypeAnchor(int annotationKind) {
// rule number corresponds to argument annotationKind:
// (0) NotAnAnchor ::= $empty
@@ -11482,6 +11504,7 @@
}
// and push it back
this.typeAnnotationLengthPtr--; // drop the empty list pushed in consumeTypeAnchor()
+ // mark that the following annotation may have to be integrated into a subsequent type annotation list:
pushOnTypeAnnotationStack(annotationSentinel);
pushOnTypeAnnotationStack(annotation);
// still need to check if the type annotation is legal:
@@ -11491,23 +11514,51 @@
problemReporter().invalidUsageOfTypeAnnotations(annotation);
}
}
-protected void consumeTypeArgumentFromAnchor() {
- // TypeAnchorOrAnnotatedTypeArgument -> TentativeTypeAnchor NotAnAnchor ReferenceType
- // the Name in ReferenceType has pushed an annotation length,
- // merge that into the previous empty list (from convertTypeAnchor()):
- int dim = this.intStack[this.intPtr];
- int ptr = this.typeAnnotationLengthPtr;
- if (dim > 1) {
- this.typeAnnotationLengthStack[ptr-dim] += this.typeAnnotationLengthStack[ptr-dim+1];
- System.arraycopy(this.typeAnnotationLengthStack, ptr-dim+2, this.typeAnnotationLengthStack, ptr-dim+1, dim-1);
- } else {
- this.typeAnnotationLengthStack[ptr-1] += this.typeAnnotationLengthStack[ptr];
- }
+// --- The following two methods terminate a sentinal situation, by confirming either a type anchor or a type annotation list: ---
+protected void confirmTypeAnchor() {
+ // TypeAnchorOrAnnotatedTypeArgument -> AnyTypeAnchor
+ // TypeAnchorOrAnnotatedTypeArgument1 -> AnyTypeAnchor '>'
+ // TypeAnchorOrAnnotatedTypeArgument2 -> AnyTypeAnchor '>>'
+ // TypeAnchorOrAnnotatedTypeArgument3 -> AnyTypeAnchor '>>>'
+
+ // tentative type anchor is indeed a type anchor (not converted to type annotation).
+ // need to remove the empty type annotation list now (see consumeTypeAnchor()).
this.typeAnnotationLengthPtr--;
- // collect everything into a regular type argument:
+}
+protected boolean confirmTypeAnnotation() {
+ // TypeAnnotationsopt ::= $empty
+ // /.$putCase consumeZeroTypeAnnotations(); $break ./
+ // - internally calls confirmTypeAnnotation()
+ // TypeAnnotationsopt -> TypeAnnotations
+
+ int sentinelPos = -1;
+ if (this.typeAnnotationLengthPtr != -1) {
+ int len = this.typeAnnotationLengthStack[this.typeAnnotationLengthPtr];
+ sentinelPos = this.typeAnnotationPtr - len;
+ if (sentinelPos > -1 && this.typeAnnotationStack[sentinelPos] == annotationSentinel) {
+ // new (possibly zero) Type Annotation in a sentinal situation means: the sentinel has served its purpose, we move on.
+ // Ergo:
+ // - remove the sentinel, transforming the special list into a regular one (lenght is already correct).
+ // - this leaves the rest of the sentinel list as the pending type annotations (instead of zero)
+ System.arraycopy(this.typeAnnotationStack, sentinelPos+1, this.typeAnnotationStack, sentinelPos, len);
+ this.typeAnnotationPtr--;
+ return true; // yes, we were in a sentinel situation
+ }
+ }
+ return false; // no, nothing ambiuous to confirm
+}
+//--- finally collect the pieces involving the TentativeTypeAnchor that was not an anchor:
+protected void consumeTypeArgumentFromAnchor() {
+ // TypeAnchorOrAnnotatedTypeArgument ::= TentativeTypeAnchor NotAnAnchor ReferenceType
+
+ // original rule:
+ // TypeArgument ::= ReferenceType
+ // /.$putCase consumeTypeArgument(); $break ./
+
consumeTypeArgument();
- this.typeAnnotationPtr--; // drop the annotationSentinel
+ if (this.typeAnnotationPtr > -1 && this.typeAnnotationStack[this.typeAnnotationPtr] == annotationSentinel)
+ this.typeAnnotationPtr--; // drop the annotationSentinel if still present
}
protected void consumeAnnotationsOnTypeArgumentFromAnchor() {
// TypeAnchorOrAnnotatedTypeArgument -> TentativeTypeAnchor NotAnAnchor Wildcard
@@ -11518,39 +11569,17 @@
// TypeAnchorOrAnnotatedTypeArgument2 -> TentativeTypeAnchor NotAnAnchor ReferenceType2
// TypeAnchorOrAnnotatedTypeArgument2 -> TentativeTypeAnchor NotAnAnchor Wildcard2
- // in all these cases a type reference (with dims) already exists
- // and only type annotations need to be added/merged (if any)
-
- TypeReference ref = (TypeReference) this.genericsStack[this.genericsPtr];
- // insert or merge converted annotation at first level into ref's annotations:
- int length = this.typeAnnotationLengthStack[this.typeAnnotationLengthPtr--];
- if (length != 0) {
- if (ref.annotations == null)
- ref.annotations = new Annotation[ref.getAnnotatableLevels()][];
- Annotation[] annotations = ref.annotations[0];
- int oldLen = 0;
- if (annotations != null) {
- oldLen = annotations.length;
- System.arraycopy(annotations, 0, annotations = new Annotation[oldLen+length], length, oldLen);
- } else {
- annotations = new Annotation[length];
- }
- System.arraycopy(
- this.typeAnnotationStack,
- (this.typeAnnotationPtr -= length) + 1,
- annotations,
- 0,
- length);
- ref.annotations[0] = annotations;
- ref.sourceStart = annotations[0].sourceStart;
- ref.bits |= ASTNode.HasTypeAnnotations;
- }
- // type references are already on genericsStack, will be collected by consumeTypeArgumentList1()
- this.typeAnnotationPtr--; // drop the annotationSentinel
+ // in all these cases a type reference (with dims) already exists on the genericsStack
+ // (will be collected by consumeTypeArgumentList1())
+ // type annotations should have been attached via the tail of the production (e.g., consumeReferenceType1())
+
+ // now just perform final clean-up:
+ if (this.typeAnnotationPtr > -1 && this.typeAnnotationStack[this.typeAnnotationPtr] == annotationSentinel)
+ this.typeAnnotationPtr--; // drop the annotationSentinel if still present
}
-protected NameReference newBaseReference() {
- return new SingleNameReference(IOTConstants._OT_BASE, (((long)this.intStack[this.intPtr--])<<32)+this.intStack[this.intPtr--]);
-}
+
+// ==============================================
+
protected void skipThisAnchor() {
// TypeAnchor ::= '@OT' 'this'
// where '@OT' is the synthetic token returned by the parser when a '@' is in a position suitable for a type anchor
@@ -11558,6 +11587,8 @@
// Cannot use ThisReference as type anchor.
// Since R<@this> is redundant, simply drop the argument (see also concatGenericsList()).
this.intPtr-=2;
+ // anchor has not type annotations, yet it will be consumed in a context where type annotations are possible
+ pushOnTypeAnnotationLengthStack(0);
}
protected void consumeQualifiedBaseTypeAnchor() {
// TypeAnchor ::= '@OT' UnannotatableName '.' 'base'
@@ -11566,7 +11597,8 @@
// handle type arguments (see consumePrimaryNoNewArrayNameThis):
pushOnGenericsIdentifiersLengthStack(this.identifierLengthStack[this.identifierLengthPtr]);
pushOnGenericsLengthStack(0); // handle type arguments
- TypeReference prefix = getTypeReference(0);
+ pushOnTypeAnnotationLengthStack(0); // unannotated by construction, but haven't yet pushed zero type annotatations
+ TypeReference prefix = getTypeReference(0); // consumes the above zero type annotations
Reference anchor = new QualifiedBaseReference(prefix, this.intStack[this.intPtr--], this.intStack[this.intPtr--]);
pushOnGenericsStack(new TypeAnchorReference(anchor, this.intStack[this.intPtr--]));
@@ -11605,6 +11637,10 @@
TypeParameter parameter = (TypeParameter) this.genericsStack[this.genericsPtr];
parameter.bounds = new TypeReference[] { bound };
}
+// ----
+protected NameReference newBaseReference() {
+ return new SingleNameReference(IOTConstants._OT_BASE, (((long)this.intStack[this.intPtr--])<<32)+this.intStack[this.intPtr--]);
+}
// SH}
protected void consumeTypeArgument() {
pushOnGenericsStack(getTypeReference(this.intStack[this.intPtr--]));
@@ -14719,10 +14755,9 @@
stackLength);
}
//{ObjectTeams: at the sentinal situation we merge this new annotation into the previous list:
- if (atSentinel) {
- this.typeAnnotationLengthStack[this.typeAnnotationLengthPtr-1]++;
- this.typeAnnotationLengthStack[this.typeAnnotationLengthPtr] = 0; // clear any previous data to start an empty list
- } else
+ if (atSentinel)
+ this.typeAnnotationLengthStack[--this.typeAnnotationLengthPtr]++;
+ else
// SH}
this.typeAnnotationLengthStack[this.typeAnnotationLengthPtr] = 1;
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/parser21.rsc b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/parser21.rsc
index c8f6027..57d2985 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/parser21.rsc
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/parser21.rsc
Binary files differ
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/readableNames.props b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/readableNames.props
index b198740..fd6a148 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/readableNames.props
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/readableNames.props
@@ -370,9 +370,9 @@
TryStatementWithResources=TryStatementWithResources
Type=Type
TypeAnchor=typeAnchor
-TypeAnchorOrAnnotatedTypeArgument1=TypeArgument
-TypeAnchorOrAnnotatedTypeArgument2=TypeArgument
-TypeAnchorOrAnnotatedTypeArgument3=TypeArgument
+TypeAnchorOrAnnotatedTypeArgument1=TypeAnchor
+TypeAnchorOrAnnotatedTypeArgument2=TypeAnchor
+TypeAnchorOrAnnotatedTypeArgument3=TypeAnchor
TypeAnchorOrAnnotatedTypeArgument=TypeArgument
TypeAnnotation=TypeAnnotation
TypeAnnotationName=AnnotationName
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/ast/FieldAccessSpec.java b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/ast/FieldAccessSpec.java
index 7a94687..513bc7a 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/ast/FieldAccessSpec.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/ast/FieldAccessSpec.java
@@ -1,7 +1,7 @@
/**********************************************************************
* This file is part of "Object Teams Development Tooling"-Software
*
- * Copyright 2004, 2011 Fraunhofer Gesellschaft, Munich, Germany,
+ * Copyright 2004, 2014 Fraunhofer Gesellschaft, Munich, Germany,
* for its Fraunhofer Institute for Computer Architecture and Software
* Technology (FIRST), Berlin, Germany and Technical University Berlin,
* Germany.
@@ -185,8 +185,7 @@
targetClass = roleModel.getBaseTypeBinding(); // use the specific declared bound class (avoids weaving into possibly inaccessible super base)
// create accessor method:
- result = createMethod(targetClass, accessorSelector);
- baseType.addMethod(result);
+ result = createMethod(scope, targetClass, accessorSelector);
}
this.selector = accessorSelector;
this.resolvedMethod = result;
@@ -220,7 +219,7 @@
/**
* Create a faked method binding representing the access method to be generated by OTRE.
*/
- private MethodBinding createMethod(ReferenceBinding baseType, char[] accessorSelector) {
+ private MethodBinding createMethod(Scope scope, ReferenceBinding baseType, char[] accessorSelector) {
if (baseType.isRoleType())
baseType = baseType.getRealClass();
if (this.calloutModifier == TerminalTokens.TokenNameget) {
@@ -228,21 +227,31 @@
// because several callouts to the same field could exist.
// RoleTypeCreator.maybeWrapQualifiedRoleType(MessageSend,BlockScope)
// will wrap the type using a faked _OT$base receiver.
- return FieldModel.getDecapsulatingFieldAccessor(baseType, this.resolvedField, true);
+ return FieldModel.getDecapsulatingFieldAccessor(scope, baseType, this.resolvedField, true, this.implementationStrategy);
} else {
TypeBinding declaredFieldType = this.hasSignature ?
this.parameters[0] :
this.fieldType;
- TypeBinding[] argTypes = this.resolvedField.isStatic() ?
+ int access;
+ TypeBinding[] argTypes;
+ if (this.implementationStrategy == ImplementationStrategy.DYN_ACCESS) {
+ access = ClassFileConstants.AccPublic;
+ argTypes = new TypeBinding[]{declaredFieldType};
+ } else {
+ access = ClassFileConstants.AccPublic|ClassFileConstants.AccStatic;
+ argTypes = this.resolvedField.isStatic() ?
new TypeBinding[]{declaredFieldType} :
new TypeBinding[]{baseType, declaredFieldType};
- return new MethodBinding(
- ClassFileConstants.AccPublic|ClassFileConstants.AccStatic,
+ }
+ MethodBinding result = new MethodBinding(
+ access,
accessorSelector,
TypeBinding.VOID,
argTypes,
Binding.NO_EXCEPTIONS,
baseType);
+ baseType.addMethod(result);
+ return result;
}
}
@@ -265,10 +274,10 @@
*/
public TypeBinding[] resolvedParameters() {
if (this.resolvedMethod == null)
- return this.parameters; // empty; FIXME(SH): handle "set" access
+ return this.parameters;
TypeBinding[] methodParams = super.resolvedParameters();
- if (this.resolvedField.isStatic())
- return methodParams; // no base argument when accessing a static field
+ if (this.resolvedField.isStatic() || this.implementationStrategy == ImplementationStrategy.DYN_ACCESS)
+ return methodParams; // no base argument when accessing a static field, or in OTDRE mode
TypeBinding[] result = new TypeBinding[methodParams.length-1];
System.arraycopy(methodParams, 1, result, 0, result.length);
return result;
@@ -329,7 +338,7 @@
if (this.calloutModifier == TerminalTokens.TokenNameget)
return true; // no parameter
int argumentPosition = 0; // safer against AIOOBE
- if (this.resolvedField != null && !this.resolvedField.isStatic())
+ if (this.resolvedField != null && !this.resolvedField.isStatic() && this.implementationStrategy != ImplementationStrategy.DYN_ACCESS)
argumentPosition = 1;
TypeBinding accessorParamType = null;
if (this.resolvedMethod != null)
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/ast/LiftingTypeReference.java b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/ast/LiftingTypeReference.java
index c5bb210..1f43e7d 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/ast/LiftingTypeReference.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/ast/LiftingTypeReference.java
@@ -38,9 +38,9 @@
import org.eclipse.jdt.internal.compiler.ast.IntLiteral;
import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
import org.eclipse.jdt.internal.compiler.ast.LongLiteral;
+import org.eclipse.jdt.internal.compiler.ast.NullAnnotationMatching;
import org.eclipse.jdt.internal.compiler.ast.NullLiteral;
import org.eclipse.jdt.internal.compiler.ast.TypeReference;
-import org.eclipse.jdt.internal.compiler.lookup.Binding;
import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
import org.eclipse.jdt.internal.compiler.lookup.MissingTypeBinding;
@@ -206,7 +206,7 @@
ITeamAnchor anchor = null;
if (roleRefType.baseclass() instanceof RoleTypeBinding)
anchor = ((RoleTypeBinding)roleRefType.baseclass())._teamAnchor;
- roleBase = parameterizedRole.environment.createParameterizedType((ReferenceBinding)roleBase.original(), typeArgs, anchor, -1, roleBase.enclosingType(), Binding.NO_ANNOTATIONS);
+ roleBase = parameterizedRole.environment.createParameterizedType((ReferenceBinding)roleBase.original(), typeArgs, anchor, -1, roleBase.enclosingType(), roleBase.getTypeAnnotations());
}
// THE compatibility check:
if ( !baseType.isCompatibleWith(roleBase)
@@ -260,6 +260,17 @@
return null;
}
+ public void updateBindingAndCheckNullness(BlockScope scope) {
+ this.baseReference.resolvedType = this.resolvedType;
+ TypeBinding roleType = this.roleReference.resolvedType;
+ if (roleType != null && roleType.isValidBinding() && scope.compilerOptions().isAnnotationBasedNullAnalysisEnabled) {
+ NullAnnotationMatching status = NullAnnotationMatching.analyse(roleType, this.resolvedType, -1);
+ if (status.isAnyMismatch()) {
+ scope.problemReporter().nullityMismatchingTypeAnnotation(this.fakedArgument.initialization, this.resolvedType, roleType, status);
+ }
+ }
+ }
+
@Override
public char [][] getTypeName() {
return this.baseTokens;
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/bytecode/BytecodeTransformer.java b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/bytecode/BytecodeTransformer.java
index a758b2e..d241b00 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/bytecode/BytecodeTransformer.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/bytecode/BytecodeTransformer.java
@@ -45,6 +45,7 @@
import org.eclipse.objectteams.otdt.internal.core.compiler.bytecode.ConstantPoolObjectReader.IncompatibleBytecodeException;
import org.eclipse.objectteams.otdt.internal.core.compiler.lifting.ArrayTranslations;
import org.eclipse.objectteams.otdt.internal.core.compiler.lifting.Lifting;
+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;
@@ -161,6 +162,10 @@
dstMethod.binding.bytecodeMissing= true;
return;
}
+
+ if (CharOperation.equals(CalloutImplementorDyn.OT_ACCESS, dstMethod.selector)
+ ||CharOperation.equals(CalloutImplementorDyn.OT_ACCESS_STATIC, dstMethod.selector))
+ return;
byte[] srcConstantPool = null;
int offset = -1;
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/bytecode/ConstantPoolObjectReader.java b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/bytecode/ConstantPoolObjectReader.java
index e652076..39a6940 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/bytecode/ConstantPoolObjectReader.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/bytecode/ConstantPoolObjectReader.java
@@ -55,6 +55,7 @@
import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.SyntheticBaseCallSurrogate;
import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.SyntheticRoleFieldAccess;
import org.eclipse.objectteams.otdt.internal.core.compiler.mappings.CallinImplementorDyn;
+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.TypeModel;
@@ -487,7 +488,9 @@
boolean isSynthMethodName(char[] name) {
return
CharOperation.prefixEquals(TypeConstants.SYNTHETIC_ACCESS_METHOD_PREFIX, name)
- || CharOperation.prefixEquals(TypeConstants.SYNTHETIC_SWITCH_ENUM_TABLE, name);
+ || CharOperation.prefixEquals(TypeConstants.SYNTHETIC_SWITCH_ENUM_TABLE, name)
+ || CharOperation.equals(CalloutImplementorDyn.OT_ACCESS, name)
+ || CharOperation.equals(CalloutImplementorDyn.OT_ACCESS_STATIC, name);
}
/** Detect methods that will be generated by the OTRE. If match return the assumed modifiers */
@@ -507,6 +510,10 @@
return AccPublic;
if (CharOperation.equals(CallinImplementorDyn.OT_CALL_BEFORE, name))
return AccPublic;
+ if (CharOperation.equals(CalloutImplementorDyn.OT_ACCESS, name))
+ return AccPublic;
+ if (CharOperation.equals(CalloutImplementorDyn.OT_ACCESS_STATIC, name))
+ return AccPublic;
return 0;
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/lifting/DeclaredLifting.java b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/lifting/DeclaredLifting.java
index afd177b..0e9ff76 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/lifting/DeclaredLifting.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/lifting/DeclaredLifting.java
@@ -45,6 +45,7 @@
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.lookup.Binding;
import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
+import org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope;
import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment;
import org.eclipse.jdt.internal.compiler.lookup.MemberTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
@@ -244,9 +245,11 @@
ReferenceBinding baseType = null;
if (scope.classScope() instanceof OTClassScope) {
// try base scope first:
- Scope baseScope = ((OTClassScope) scope.classScope()).getBaseImportScope();
- if (baseScope != null)
+ CompilationUnitScope baseScope = ((OTClassScope) scope.classScope()).getBaseImportScope(scope);
+ if (baseScope != null) {
baseType = (ReferenceBinding) baseScope.getType(ltr.baseTokens, ltr.baseTokens.length);
+ baseScope.originalScope = null;
+ }
}
if (baseType == null || !baseType.isValidBinding())
// fall back to normal scope:
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/lookup/OTClassScope.java b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/lookup/OTClassScope.java
index b990fd4..5c6d287 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/lookup/OTClassScope.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/lookup/OTClassScope.java
@@ -301,7 +301,9 @@
this.baseImportScope.topLevelTypes = new SourceTypeBinding[0];
}
- public Scope getBaseImportScope() {
+ public CompilationUnitScope getBaseImportScope(Scope originalScope) {
+ if (this.baseImportScope != null)
+ this.baseImportScope.originalScope = originalScope;
return this.baseImportScope;
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/lookup/RoleTypeBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/lookup/RoleTypeBinding.java
index 7289081..c914805 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/lookup/RoleTypeBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/lookup/RoleTypeBinding.java
@@ -235,6 +235,11 @@
// record as known role type at teamAnchor and in our own cache
registerAnchor();
}
+
+ public TypeBinding clone(TypeBinding outerType) {
+ RoleTypeBinding clone = new RoleTypeBinding(this.type, typeArguments(), this._teamAnchor, (ReferenceBinding) outerType, this.environment);
+ return clone;
+ }
// hook of maybeInstantiate
TypeBinding forAnchor(ITeamAnchor anchor, int dimensions) {
@@ -956,20 +961,18 @@
return super.attributeName();
}
public String toString() {
- String anchorStr = ""; //$NON-NLS-1$
+ StringBuilder sb = new StringBuilder();
+ sb.append(annotatedDebugName());
+ sb.append('<').append('@');
ITeamAnchor[] bestNamePath = this._teamAnchor.getBestNamePath(false);
for (int i = 0; i < bestNamePath.length; i++) {
if (i>0)
- anchorStr += '.';
- anchorStr += new String(bestNamePath[i].readableName());
+ sb.append('.');
+ sb.append(bestNamePath[i].readableName());
}
- String staticTeam = ""; //$NON-NLS-1$
- if (!(this._teamAnchor instanceof TThisBinding))
- staticTeam = '['+new String(this._staticallyKnownTeam.sourceName())+']';
- return
- anchorStr
- +staticTeam
- +'.'
- +new String(sourceName());
+ if (!(this._teamAnchor instanceof TThisBinding))
+ sb.append('[').append(this._staticallyKnownTeam.sourceName()).append(']');
+ sb.append('>');
+ return sb.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
new file mode 100644
index 0000000..cfbdca8
--- /dev/null
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/lookup/SyntheticOTTargetMethod.java
@@ -0,0 +1,251 @@
+/**********************************************************************
+ * This file is part of "Object Teams Development Tooling"-Software
+ *
+ * Copyright 2014 GK Software AG
+ *
+ * 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.otdt.internal.core.compiler.lookup;
+
+import org.eclipse.jdt.internal.compiler.ast.ASTNode;
+import org.eclipse.jdt.internal.compiler.codegen.CodeStream;
+import org.eclipse.jdt.internal.compiler.codegen.Opcodes;
+import org.eclipse.jdt.internal.compiler.lookup.BinaryTypeBinding;
+import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
+import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
+import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
+import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
+import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
+import org.eclipse.jdt.internal.compiler.lookup.SyntheticMethodBinding;
+import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
+import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
+
+/**
+ * A synthetic method binding, which used as an invocation target needs to
+ * influence code generation.
+ *
+ * @since 2.3.1
+ */
+public abstract class SyntheticOTTargetMethod extends SyntheticMethodBinding {
+
+ protected SyntheticOTTargetMethod(MethodBinding targetMethod, int purpose) {
+ super(targetMethod, purpose);
+ }
+
+ protected SyntheticOTTargetMethod(FieldBinding targetField, boolean isReadAccess, boolean isSuperAccess, SourceTypeBinding declaringClass) {
+ super(targetField, isReadAccess, isSuperAccess, declaringClass);
+ }
+
+ protected SyntheticOTTargetMethod(BinaryTypeBinding declaringClass, int modifiers, char[] selector,
+ TypeBinding[] parameters, TypeBinding returnType) {
+ super(declaringClass, modifiers, selector, parameters, returnType);
+ }
+
+ /**
+ * We assume: all arguments of an invocation have already been pushed.
+ * At this point we may insert additional byte codes, or generate the actual invocation.
+ * @return 0 means: no further action needed, else we return the opcode for which the actual
+ * invoke still needs to be generated.
+ */
+ public abstract byte prepareOrGenerateInvocation(CodeStream codeStream, byte opcode);
+
+ /**
+ * Represents an inferred callout-to-field.
+ * May need to tweak invokestatic (for regular access$n methods) into invokevirtual (for the generated c-t-f method).
+ */
+ public static class CalloutToField extends SyntheticOTTargetMethod {
+ public CalloutToField(MethodBinding targetMethod) {
+ super(targetMethod, InferredCalloutToField);
+ }
+
+ @Override
+ public byte prepareOrGenerateInvocation(CodeStream codeStream, byte opcode) {
+ if (!isStatic() && this.purpose == SyntheticMethodBinding.InferredCalloutToField)
+ return Opcodes.OPC_invokevirtual;
+ return opcode;
+ }
+ }
+
+ /**
+ * Represents a decapsulating field access while targeting OTDRE.
+ * We need to generate a special sequence to call _OT$access(int,int,Object[],ITeam)
+ * Currently only supports read access.
+ * Typical use is when a base predicate refers to a private base field.
+ */
+ public static class OTDREFieldDecapsulation extends SyntheticOTTargetMethod {
+
+ private int accessId;
+ private int opKind;
+ private ReferenceBinding enclosingTeam;
+ private TypeBinding originalType;
+ private ASTNode site;
+ private BlockScope scope;
+
+ /**
+ * Create a binding for a field access using decapsulation.
+ * @param fakedMethod this method is member of the base class, representing the otdre-generated access method.
+ * @param originalType return type of the original feature (field)
+ * @param accessId ID by which the base field is identified inside the access method
+ * @param opKind 0 = get, 1 = set
+ * @param scope where this access has been seen
+ * @param site the exact node where decapsulation happened
+ */
+ public OTDREFieldDecapsulation(MethodBinding fakedMethod, TypeBinding originalType, int accessId, int opKind, BlockScope scope, ASTNode site) {
+ super(fakedMethod, SyntheticMethodBinding.InferredCalloutToField);
+ this.accessId = accessId;
+ this.opKind = opKind;
+ ReferenceBinding enclosingRole = scope.enclosingReceiverType();
+ this.enclosingTeam = enclosingRole.enclosingType();
+ this.originalType = originalType;
+ this.site = site;
+ this.scope = scope;
+ }
+
+ @Override
+ public byte prepareOrGenerateInvocation(CodeStream codeStream, byte opcode) {
+ // accessId
+ codeStream.bipush((byte) this.accessId);
+ // 0 = get, 1 = set
+ if (this.opKind == 0)
+ codeStream.iconst_0();
+ else
+ codeStream.iconst_1();
+ // no args to pack for read access:
+ codeStream.aconst_null();
+ // enclosing team instance:
+ Object[] emulationPath = this.scope.getEmulationPath(this.enclosingTeam, true /*only exact match*/, false/*consider enclosing arg*/);
+ codeStream.generateOuterAccess(emulationPath, this.site, this.enclosingTeam, this.scope);
+ // invoke it:
+ byte invoke = this.targetMethod.isStatic() ? Opcodes.OPC_invokestatic : Opcodes.OPC_invokevirtual;
+ codeStream.invoke(invoke, this.targetMethod, this.targetMethod.declaringClass);
+ // convert result?:
+ if (this.originalType != TypeBinding.VOID) {
+ if (this.originalType.isBaseType()) {
+ codeStream.checkcast(this.scope.environment().computeBoxingType(this.originalType));
+ codeStream.generateUnboxingConversion(this.originalType.id);
+ } else {
+ codeStream.checkcast(this.originalType);
+ }
+ } else {
+ // what? - not hit for field read :)
+ }
+ return 0; // signal we're done
+ }
+ }
+
+ /**
+ * 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 {
+
+ private int accessId;
+ private ReferenceBinding enclosingTeam;
+ private TypeBinding[] originalParameters;
+ private TypeBinding originalReturnType;
+ private ASTNode site;
+ private BlockScope scope;
+
+ /**
+ * Create a binding for a method access using decapsulation.
+ * @param targetMethod this method is member of the base class, representing the otdre-generated access method.
+ * @param originalParameters parameters of the original target method (before replacing with _OT$access)
+ * @param originalReturn return type of the original target method (before replacing with _OT$access)
+ * @param accessId ID by which the base field is identified inside the access method
+ * @param scope where this access has been seen
+ * @param site the exact node where decapsulation happened
+ */
+ public OTDREMethodDecapsulation(MethodBinding targetMethod, TypeBinding[] originalParameters, TypeBinding originalReturn, int accessId, BlockScope scope, ASTNode site) {
+ super(targetMethod, SyntheticMethodBinding.MethodDecapsulation);
+ this.accessId = accessId;
+ ReferenceBinding enclosingRole = scope.enclosingReceiverType();
+ this.enclosingTeam = enclosingRole.enclosingType();
+ this.originalParameters = originalParameters;
+ this.originalReturnType = originalReturn;
+ this.site = site;
+ this.scope = scope;
+ }
+
+ @Override
+ public byte prepareOrGenerateInvocation(CodeStream codeStream, byte opcode) {
+ TypeBinding[] tgtParams = this.originalParameters;
+ byte len = (byte) tgtParams.length;
+ // argument array:
+ codeStream.bipush(len);
+ codeStream.anewarray(this.scope.getJavaLangObject());
+ // fold array store into arguments on stack:
+ for (byte i = (byte) (len-1); i >= 0; i--) {
+ // argi, array
+ if (size(tgtParams[i]) == 1) {
+ codeStream.dup_x1();
+ // array, argi, array
+ codeStream.swap();
+ } else {
+ codeStream.dup_x2();
+ // array, argi, array
+ codeStream.dup_x2();
+ // array, array, argi, array
+ codeStream.pop();
+ }
+ // array, array, argi
+ if (tgtParams[i].isPrimitiveType())
+ codeStream.generateBoxingConversion(tgtParams[i].id); // no longer need to handle 2-byte values
+ codeStream.bipush(i);
+ // array, array, argi, i
+ codeStream.swap();
+ // array, array, i, argi
+ codeStream.aastore();
+ // array
+ }
+ // array (containing all arguments)
+
+ // accessId:
+ codeStream.bipush((byte) this.accessId);
+ codeStream.swap();
+ // accessId, array
+
+ // opKind (ignored):
+ codeStream.iconst_0();
+ codeStream.swap();
+ // accessId, opKind, array
+
+ // enclosing team instance:
+ Object[] emulationPath = this.scope.getEmulationPath(this.enclosingTeam, true /*only exact match*/, false/*consider enclosing arg*/);
+ codeStream.generateOuterAccess(emulationPath, this.site, this.enclosingTeam, this.scope);
+ // invoke it:
+ byte invoke = this.targetMethod.isStatic() ? Opcodes.OPC_invokestatic : Opcodes.OPC_invokevirtual;
+ codeStream.invoke(invoke, this, this.targetMethod.declaringClass);
+ // convert result?:
+ if (this.originalReturnType != TypeBinding.VOID) {
+ if (this.originalReturnType.isBaseType()) {
+ codeStream.checkcast(this.scope.environment().computeBoxingType(this.originalReturnType));
+ codeStream.generateUnboxingConversion(this.originalReturnType.id);
+ } else {
+ codeStream.checkcast(this.originalReturnType);
+ }
+ } else {
+ codeStream.pop();
+ }
+ return 0; // signal we're done
+ }
+ }
+ static int size(TypeBinding type) {
+ switch (type.id) {
+ case TypeIds.T_double :
+ case TypeIds.T_long :
+ return 2;
+ case TypeIds.T_void :
+ return 0;
+ default :
+ return 1;
+ }
+ }
+}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/lookup/SyntheticRoleBridgeMethodBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/lookup/SyntheticRoleBridgeMethodBinding.java
index d1471cd..121a644 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/lookup/SyntheticRoleBridgeMethodBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/lookup/SyntheticRoleBridgeMethodBinding.java
@@ -17,10 +17,12 @@
package org.eclipse.objectteams.otdt.internal.core.compiler.lookup;
import static org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants.*;
+
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.codegen.CodeStream;
import org.eclipse.jdt.internal.compiler.codegen.Opcodes;
import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
+import org.eclipse.jdt.internal.compiler.lookup.ParameterizedMethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
import org.eclipse.jdt.internal.compiler.lookup.Scope;
import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
@@ -29,6 +31,7 @@
import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
import org.eclipse.objectteams.otdt.core.compiler.IOTConstants;
import org.eclipse.objectteams.otdt.internal.core.compiler.model.MethodModel;
+import org.eclipse.objectteams.otdt.internal.core.compiler.util.TSuperHelper;
import org.eclipse.objectteams.otdt.internal.core.compiler.util.TypeAnalyzer;
public class SyntheticRoleBridgeMethodBinding extends SyntheticOTMethodBinding {
@@ -36,7 +39,7 @@
public static final char[] PRIVATE = "$private$".toCharArray(); //$NON-NLS-1$
public SyntheticRoleBridgeMethodBinding(SourceTypeBinding declaringRole, ReferenceBinding originalRole, MethodBinding targetMethod, int bridgeKind) {
- super(declaringRole, AccPublic|AccSynthetic, targetMethod.selector, targetMethod.parameters, targetMethod.returnType);
+ super(declaringRole, AccPublic|AccSynthetic, targetMethod.selector, originalParameters(targetMethod), originalReturnType(targetMethod));
this.purpose = bridgeKind;
switch (bridgeKind) {
case RoleMethodBridgeOuter:
@@ -53,15 +56,16 @@
break;
case RoleMethodBridgeInner:
// correction: add role as first parameter:
- len = targetMethod.parameters.length;
+ len = this.parameters.length;
int offset = targetMethod.isStatic()?2:0;
- this.parameters = new TypeBinding[len+1+offset];
- this.parameters[0] = originalRole.getRealType();
+ TypeBinding[] newParameters = new TypeBinding[len+1+offset];
+ newParameters[0] = originalRole.getRealType();
if (offset > 0) {
- this.parameters[1] = TypeBinding.INT; // dummy int
- this.parameters[2] = originalRole.enclosingType(); // team arg
+ newParameters[1] = TypeBinding.INT; // dummy int
+ newParameters[2] = originalRole.enclosingType(); // team arg
}
- System.arraycopy(targetMethod.parameters, 0, this.parameters, 1+offset, len);
+ System.arraycopy(this.parameters, 0, newParameters, 1+offset, len);
+ this.parameters = newParameters;
// correction: this bridge is static:
this.modifiers |= AccStatic;
// correction: generate the bridge method name:
@@ -76,6 +80,40 @@
this.index = methodId;
}
+ private static TypeBinding[] originalParameters(MethodBinding targetMethod) {
+ if (!TSuperHelper.isTSuper(targetMethod)) {
+ MethodBinding top = findTopMethod(targetMethod);
+ if (top != null)
+ return top.original().parameters;
+ }
+ return targetMethod.original().parameters;
+ }
+
+ private static TypeBinding originalReturnType(MethodBinding targetMethod) {
+ if (!TSuperHelper.isTSuper(targetMethod)) {
+ MethodBinding top = findTopMethod(targetMethod);
+ if (top != null)
+ return top.original().returnType;
+ }
+ return targetMethod.original().returnType;
+ }
+
+ static MethodBinding findTopMethod(MethodBinding targetMethod) {
+ while (targetMethod.copyInheritanceSrc != null)
+ targetMethod = targetMethod.copyInheritanceSrc;
+ tsupers: if (targetMethod.overriddenTSupers != null) {
+ for (int i = 0; i < targetMethod.overriddenTSupers.length; i++) {
+ MethodBinding cand = targetMethod.overriddenTSupers[i];
+ if (!(cand instanceof ParameterizedMethodBinding)) {
+ targetMethod = cand;
+ break tsupers;
+ }
+ }
+ if (targetMethod.overriddenTSupers.length > 0)
+ targetMethod = targetMethod.overriddenTSupers[0];
+ }
+ return targetMethod;
+ }
@Override
public void generateInstructions(CodeStream codeStream) {
TypeBinding[] arguments = this.parameters;
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/lookup/SyntheticRoleFieldAccess.java b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/lookup/SyntheticRoleFieldAccess.java
index e1cb40a..ffb6992 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/lookup/SyntheticRoleFieldAccess.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/lookup/SyntheticRoleFieldAccess.java
@@ -66,7 +66,7 @@
*
* @author stephan
*/
-public class SyntheticRoleFieldAccess extends SyntheticMethodBinding {
+public class SyntheticRoleFieldAccess extends SyntheticOTTargetMethod {
static final char[] FIELD_GET_NAME = "_fieldget_".toCharArray(); //$NON-NLS-1$
static final char[] FIELD_GET_PREFIX = CharOperation.concat(
@@ -264,7 +264,7 @@
*
* @param codeStream
*/
- public void generateInvoke(CodeStream codeStream) {
+ public byte prepareOrGenerateInvocation(CodeStream codeStream, byte opcode) {
ReferenceBinding roleType = (ReferenceBinding)this.parameters[0];
if (roleType instanceof UnresolvedReferenceBinding) {
try {
@@ -272,7 +272,7 @@
.resolve(Config.getLookupEnvironment(), false);
} catch (NotConfiguredException e) {
e.logWarning("Failed to generate accessor"); //$NON-NLS-1$
- return;
+ return opcode;
}
this.parameters[0] = roleType;
}
@@ -301,6 +301,7 @@
if (arg.initializationPCs != null) // null checking is asymmetric in LocalVariableBinding.
arg.recordInitializationEndPC(codeStream.position);
}
+ return 0; // done
}
private void insertOuterAccess(CodeStream codeStream, ReferenceBinding roleType)
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/lookup/TeamAnchor.java b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/lookup/TeamAnchor.java
index 2872892..04b31e6 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/lookup/TeamAnchor.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/lookup/TeamAnchor.java
@@ -799,7 +799,7 @@
LookupEnvironment env)
{
DependentTypeBinding dependentTypeBinding =
- (DependentTypeBinding)env.createParameterizedType(typeBinding, arguments, this, paramPosition, typeBinding.enclosingType(), Binding.NO_ANNOTATIONS);
+ (DependentTypeBinding)env.createParameterizedType(typeBinding, arguments, this, paramPosition, typeBinding.enclosingType(), typeBinding.getTypeAnnotations());
return (dimensions > 0)
? dependentTypeBinding.getArrayType(dimensions)
: dependentTypeBinding;
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/mappings/CallinImplementor.java b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/mappings/CallinImplementor.java
index 40e04e6..f72a8b8 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/mappings/CallinImplementor.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/mappings/CallinImplementor.java
@@ -516,7 +516,7 @@
callinBindingDeclaration,
callinWrapperDecl,
baseMethodSpec,
- false,
+ null,
resultName != null /*hasResultArg*/);
if (messageSendArguments == null) {
callinBindingDeclaration.tagAsHavingErrors();
@@ -773,6 +773,7 @@
TypeBinding[] typeArguments = liftMethod[0].typeVariables();
if (typeArguments != Binding.NO_TYPE_VARIABLES)
try {
+ // FIXME: pass null annotations, once JDT supports those on a type declaration.
roleVarType = Config.getLookupEnvironment().createParameterizedType(roleVarType, typeArguments, null, -1, roleModel.getBinding().enclosingType(), Binding.NO_ANNOTATIONS);
} catch (NotConfiguredException e) {
e.logWarning("Cannot lookup parameterized type"); //$NON-NLS-1$
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/mappings/CallinImplementorDyn.java b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/mappings/CallinImplementorDyn.java
index cdbb466..c422134 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/mappings/CallinImplementorDyn.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/mappings/CallinImplementorDyn.java
@@ -308,7 +308,10 @@
// we probably want to avoid generating empty methods here.
final TypeDeclaration teamDecl = aTeam.getAst();
if (teamDecl == null) return;
+
final AstGenerator gen = new AstGenerator(teamDecl);
+ gen.replaceableEnclosingClass = teamDecl.binding;
+
// public void _OT$callBefore (IBoundBase2 base, int boundMethodId, int callinId, Object[] args)
// public void _OT$callAfter (IBoundBase2 base, int boundMethodId, int callinId, Object[] args, Object result)
@@ -387,7 +390,6 @@
boolean isStaticRoleMethod = callinDecl.getRoleMethod().isStatic();
ReferenceBinding roleType = callinDecl.scope.enclosingReceiverType();
MethodBinding roleMethodBinding = callinDecl.getRoleMethod();
-
boolean needLiftedRoleVar = !isStaticRoleMethod
&& roleType.isCompatibleWith(roleMethodBinding.declaringClass);
@@ -615,7 +617,7 @@
// who is responsible for lowering: the team or the current role?
Expression lowerReceiver = (isRoleOfCurrentRole(roleType, returnTypes[0]))
? gen.singleNameReference(roleVar)
- : gen.thisReference();
+ : genTeamThis(gen, returnTypes[0]);
result = new Lowering().lowerExpression(methodDecl.scope, result, returnTypes[0], returnTypes[1],
lowerReceiver, true/*needNullCheck*/, true/*delayedResolve*/);
}
@@ -904,7 +906,7 @@
gen.castExpression(gen.singleNameReference(IOTConstants.BASE), gen.typeReference(currentRole.baseclass()), CastExpression.RAW),
currentRole.baseclass(), currentRole, false)
// TODO: might want to extend the signature of callNext to pass the current role to avoid this lifting?
- : gen.thisReference();
+ : genTeamThis(gen, returnTypes[0]);
result = Lifting.liftCall(mapping.scope,
liftReceiver,
gen.castExpression(result,
@@ -933,6 +935,16 @@
AstEdit.addMethod(teamDecl, decl);
}
+ Reference genTeamThis(AstGenerator gen, TypeBinding type) {
+ TypeBinding leaf = type.leafComponentType();
+ if (leaf instanceof ReferenceBinding) {
+ ReferenceBinding teamBinding = ((ReferenceBinding) leaf).enclosingType();
+ if (teamBinding != null)
+ return gen.qualifiedThisReference(teamBinding);
+ }
+ return gen.thisReference();
+ }
+
TypeBinding[] getReturnTypes(CallinMappingDeclaration mapping, int i) {
TypeBinding baseReturn = mapping.baseMethodSpecs[i].resolvedType();
TypeBinding roleReturn = mapping.roleMethodSpec.resolvedType();
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/mappings/CalloutImplementor.java b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/mappings/CalloutImplementor.java
index 2530885..af89674 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/mappings/CalloutImplementor.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/mappings/CalloutImplementor.java
@@ -68,6 +68,7 @@
import org.eclipse.objectteams.otdt.internal.core.compiler.ast.CalloutMappingDeclaration;
import org.eclipse.objectteams.otdt.internal.core.compiler.ast.FieldAccessSpec;
import org.eclipse.objectteams.otdt.internal.core.compiler.ast.MethodSpec;
+import org.eclipse.objectteams.otdt.internal.core.compiler.ast.MethodSpec.ImplementationStrategy;
import org.eclipse.objectteams.otdt.internal.core.compiler.ast.ParameterMapping;
import org.eclipse.objectteams.otdt.internal.core.compiler.ast.PotentialLowerExpression;
import org.eclipse.objectteams.otdt.internal.core.compiler.ast.PrivateRoleMethodCall;
@@ -533,7 +534,7 @@
calloutDecl,
roleMethodDeclaration,
calloutDecl.roleMethodSpec,
- (calloutDecl.baseMethodSpec instanceof FieldAccessSpec),
+ (calloutDecl.baseMethodSpec instanceof FieldAccessSpec) ? ((FieldAccessSpec)calloutDecl.baseMethodSpec) : null,
false /*hasResultArg*/);
if ( arguments == null
|| hasParamMappingProblems(calloutDecl, returnType, roleMethodDeclaration.scope.problemReporter()))
@@ -558,7 +559,9 @@
CastExpression.DO_WRAP);
Expression baseAccess = null;
- if (calloutDecl.baseMethodSpec.isPrivate() && baseType.isRole()) {
+ if (calloutDecl.baseMethodSpec.isPrivate() && baseType.isRole()
+ && calloutDecl.baseMethodSpec.implementationStrategy != ImplementationStrategy.DYN_ACCESS)
+ {
// tricky case: callout to a private role method (base-side)
// requires the indirection via two wrapper methods (privateBridgeMethod)
@@ -975,10 +978,12 @@
int l= result.length;
if (result == Binding.NO_PARAMETERS || l == 0)
return result;
- System.arraycopy(result, 0, result= new TypeBinding[l], 0, l);
TypeVariableBinding[] variables= wrapperMethod.binding.typeVariables();
- for (int i = 0; i < result.length; i++)
- result[i] = substituteVariables(result[i], variables);
+ if (variables != Binding.NO_TYPE_VARIABLES) {
+ System.arraycopy(result, 0, result= new TypeBinding[l], 0, l);
+ for (int i = 0; i < result.length; i++)
+ result[i] = substituteVariables(result[i], variables);
+ }
return result;
}
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 9d7e7e7..9dda280 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
@@ -1,7 +1,7 @@
/**********************************************************************
* This file is part of "Object Teams Development Tooling"-Software
*
- * Copyright 2011 GK Software AG and others.
+ * Copyright 2011, 2014 GK Software AG and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -48,7 +48,7 @@
Expression receiver, MethodSpec baseSpec, Expression[] arguments,
AstGenerator gen)
{
- char[] selector = ensureAccessor(scope, baseType, baseSpec.isStatic());
+ char[] selector = ensureAccessor(scope, baseType, baseSpec.isStatic()).selector;
TeamModel teamModel = roleModel.getTeamModel();
Expression accessIdArg = gen.intLiteral(baseSpec.accessId);
int opKind = 0;
@@ -65,12 +65,14 @@
return gen.createCastOrUnboxing(messageSend, baseSpec.resolvedType(), true/*baseAccess*/);
}
- private static char[] ensureAccessor(Scope scope, ReferenceBinding baseType, boolean isStatic) {
+ public static MethodBinding ensureAccessor(Scope scope, ReferenceBinding baseType, boolean isStatic) {
if (baseType.isRoleType())
baseType = baseType.getRealClass();
char[] selector = isStatic ? OT_ACCESS_STATIC : OT_ACCESS;
MethodBinding[] methods = baseType.getMethods(selector);
- if (methods == null || methods.length != 1) {
+ if (methods != null && methods.length == 1) {
+ return methods[0];
+ } else {
int modifiers = ClassFileConstants.AccPublic|ClassFileConstants.AccSynthetic;
if (isStatic)
modifiers |= ClassFileConstants.AccStatic;
@@ -86,7 +88,7 @@
Binding.NO_EXCEPTIONS,
baseType);
baseType.addMethod(method);
+ return method;
}
- return selector;
}
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/mappings/MethodMappingImplementor.java b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/mappings/MethodMappingImplementor.java
index 5518e15..297ca25 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/mappings/MethodMappingImplementor.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/mappings/MethodMappingImplementor.java
@@ -44,6 +44,7 @@
import org.eclipse.objectteams.otdt.internal.core.compiler.ast.CalloutMappingDeclaration;
import org.eclipse.objectteams.otdt.internal.core.compiler.ast.FieldAccessSpec;
import org.eclipse.objectteams.otdt.internal.core.compiler.ast.MethodSpec;
+import org.eclipse.objectteams.otdt.internal.core.compiler.ast.MethodSpec.ImplementationStrategy;
import org.eclipse.objectteams.otdt.internal.core.compiler.ast.TypeAnchorReference;
import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.ITeamAnchor;
import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.RoleTypeBinding;
@@ -109,7 +110,7 @@
AbstractMethodMappingDeclaration methodMapping,
MethodDeclaration wrapperMethodDeclaration,
MethodSpec sourceMethodSpec,
- boolean isFieldAccess,
+ FieldAccessSpec baseFieldSpec,
boolean hasResultArgument)
{
// prepare parameter mappings:
@@ -120,7 +121,7 @@
}
Argument[] wrapperMethodArguments = wrapperMethodDeclaration.arguments;
- Expression[] arguments;
+ Expression[] arguments = null;
boolean hasArgError = false;
@@ -135,25 +136,30 @@
int implementationArgLen = implParameters.length;
int expressionsOffset = 0;
- if (isFieldAccess && this._role.getWeavingScheme() == WeavingScheme.OTRE) { // OTREDyn uses non-static accessor for non-static fields
- // field access is mapped to static method with additional first parameter _OT$base (unless static):
- if (!((FieldAccessSpec)methodMapping.getBaseMethodSpecs()[0]).isStatic())
- expressionsOffset = 1;
+ if (baseFieldSpec != null) {
+ if (baseFieldSpec.implementationStrategy == ImplementationStrategy.DYN_ACCESS) {
+ // in decapsulation scenarios OTREDyn uses non-static accessor for non-static fields
+ if (!baseFieldSpec.isSetter())
+ implementationArgLen = 0; // if resolved to a 4-arg _OT$access, don't consider these args during AST gen.
+ } else if (this._role.getWeavingScheme() == WeavingScheme.OTRE
+ && !((FieldAccessSpec)methodMapping.getBaseMethodSpecs()[0]).isStatic()) {
+ // for OTRE, non-static field access is mapped to static method with additional first parameter _OT$base:
+ expressionsOffset = 1;
- ReferenceBinding baseType = methodMapping.scope.enclosingSourceType().baseclass();
- arguments = new Expression[implementationArgLen+expressionsOffset];
- if (expressionsOffset > 0) {
- // TODO(SH): generalize this and the corresponding statement in
- // CalloutImplementor.makeArguments().
- // cast needed against weakened _OT$base reference.
- MethodSpec baseSpec = ((CalloutMappingDeclaration)methodMapping).baseMethodSpec;
- AstGenerator gen = new AstGenerator(baseSpec);
- arguments[0] = new CastExpression(
+ ReferenceBinding baseType = methodMapping.scope.enclosingSourceType().baseclass();
+ arguments = new Expression[implementationArgLen+expressionsOffset];
+ // TODO(SH): generalize this and the corresponding statement in
+ // CalloutImplementor.makeArguments().
+ // cast needed against weakened _OT$base reference.
+ MethodSpec baseSpec = ((CalloutMappingDeclaration)methodMapping).baseMethodSpec;
+ AstGenerator gen = new AstGenerator(baseSpec);
+ arguments[0] = new CastExpression(
gen.singleNameReference(IOTConstants._OT_BASE),
gen.baseclassReference(baseType),
baseType.isRole() ? CastExpression.NEED_CLASS : CastExpression.RAW); // FIXME (see also CalloutImplementor.makeArguments)
- }
- } else {
+ }
+ }
+ if (arguments == null) {
arguments = new Expression[implementationArgLen];
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/model/FieldModel.java b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/model/FieldModel.java
index 16d29eb..2b717ed 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/model/FieldModel.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/model/FieldModel.java
@@ -20,10 +20,10 @@
**********************************************************************/
package org.eclipse.objectteams.otdt.internal.core.compiler.model;
+import static org.eclipse.objectteams.otdt.core.compiler.IOTConstants.ANCHOR_USAGE_RANKS;
+import static org.eclipse.objectteams.otdt.core.compiler.IOTConstants.AccSynthIfc;
import static org.eclipse.objectteams.otdt.core.compiler.IOTConstants.OT_GETFIELD;
import static org.eclipse.objectteams.otdt.core.compiler.IOTConstants.OT_SETFIELD;
-import static org.eclipse.objectteams.otdt.core.compiler.IOTConstants.AccSynthIfc;
-import static org.eclipse.objectteams.otdt.core.compiler.IOTConstants.ANCHOR_USAGE_RANKS;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
@@ -33,12 +33,15 @@
import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
+import org.eclipse.jdt.internal.compiler.lookup.Scope;
import org.eclipse.jdt.internal.compiler.lookup.TagBits;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.objectteams.otdt.core.exceptions.InternalCompilerError;
import org.eclipse.objectteams.otdt.internal.core.compiler.ast.CalloutMappingDeclaration;
+import org.eclipse.objectteams.otdt.internal.core.compiler.ast.MethodSpec.ImplementationStrategy;
import org.eclipse.objectteams.otdt.internal.core.compiler.bytecode.AnchorUsageRanksAttribute;
import org.eclipse.objectteams.otdt.internal.core.compiler.bytecode.WordValueAttribute;
+import org.eclipse.objectteams.otdt.internal.core.compiler.mappings.CalloutImplementorDyn;
import org.eclipse.objectteams.otdt.internal.core.compiler.model.MethodModel.FakeKind;
@@ -151,29 +154,36 @@
/** Create a faked method binding for a getAccessor to a given base field.
* @param isGetter select getter or setter
*/
- public static MethodBinding getDecapsulatingFieldAccessor(ReferenceBinding baseType,
+ public static MethodBinding getDecapsulatingFieldAccessor(Scope scope,
+ ReferenceBinding baseType,
FieldBinding resolvedField,
- boolean isGetter)
+ boolean isGetter,
+ ImplementationStrategy strategy)
{
FieldModel model = FieldModel.getModel(resolvedField);
MethodBinding accessor = isGetter ? model._decapsulatingGetter : model._decapsulatingSetter;
if (accessor != null)
return accessor;
- TypeBinding[] argTypes = resolvedField.isStatic()
- ? (isGetter
- ? new TypeBinding[0]
- : new TypeBinding[]{resolvedField.type})
- : (isGetter
- ? new TypeBinding[]{baseType}
- : new TypeBinding[]{baseType, resolvedField.type});
- accessor = new MethodBinding(
+ if (strategy == ImplementationStrategy.DYN_ACCESS) {
+ accessor = CalloutImplementorDyn.ensureAccessor(scope, baseType, resolvedField.isStatic());
+ } else {
+ TypeBinding[] argTypes = resolvedField.isStatic()
+ ? (isGetter
+ ? new TypeBinding[0]
+ : new TypeBinding[]{resolvedField.type})
+ : (isGetter
+ ? new TypeBinding[]{baseType}
+ : new TypeBinding[]{baseType, resolvedField.type});
+ accessor = new MethodBinding(
ClassFileConstants.AccPublic|ClassFileConstants.AccStatic,
CharOperation.concat(isGetter ? OT_GETFIELD : OT_SETFIELD, resolvedField.name),
isGetter ? resolvedField.type : TypeBinding.VOID,
argTypes,
Binding.NO_EXCEPTIONS,
baseType);
+ baseType.addMethod(accessor);
+ }
MethodModel.getModel(accessor)._fakeKind = FakeKind.BASE_FIELD_ACCESSOR;
if (isGetter)
model._decapsulatingGetter = accessor;
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/statemachine/copyinheritance/CopyInheritance.java b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/statemachine/copyinheritance/CopyInheritance.java
index 7195ef5..57366a0 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/statemachine/copyinheritance/CopyInheritance.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/statemachine/copyinheritance/CopyInheritance.java
@@ -1124,9 +1124,6 @@
}
if (method.isPrivate()) {
newMethodDecl.binding.modifiers |= ExtraCompilerModifiers.AccLocallyUsed; // don't warn unused copied method
- MethodBinding synthBinding = SyntheticRoleBridgeMethodBinding.findOuterAccessor(targetRoleDecl.scope, targetRoleDecl.binding, newMethodDecl.binding);
- if (synthBinding != null)
- synthBinding.parameters[0] = srcRole.getRealType(); // manual weakening of bridge to copy-inherited method
}
newMethodDecl.binding.copiedInContext = tgtTeam.enclosingType();
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/statemachine/transformer/ReflectionGenerator.java b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/statemachine/transformer/ReflectionGenerator.java
index 858adea..96f5eb2 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/statemachine/transformer/ReflectionGenerator.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/statemachine/transformer/ReflectionGenerator.java
@@ -23,6 +23,7 @@
import java.util.HashSet;
import org.eclipse.jdt.core.compiler.CharOperation;
+import org.eclipse.jdt.internal.compiler.ast.AND_AND_Expression;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.eclipse.jdt.internal.compiler.ast.Argument;
@@ -573,19 +574,23 @@
{
/*
* For the end of unregisterRole(Object) create:
- * if (first_cache != null) {
+ * if (first_cache != null && found_base != null) { // ensure no null problems against either variable
* first_cache.remove(_OT$base_arg);
* ((IBoundBase)found_base)._OT$removeRole(_OT$role_arg);
* }
*/
return gen.ifStatement(
- new EqualExpression(
- gen.singleNameReference(FIRST_CACHE),
- gen.nullLiteral(),
- OperatorIds.EQUAL_EQUAL
- ),
- gen.block(null),
- gen.block(new Statement[] { // "else" instead of negation
+ new AND_AND_Expression(
+ new EqualExpression(
+ gen.singleNameReference(FIRST_CACHE),
+ gen.nullLiteral(),
+ OperatorIds.NOT_EQUAL),
+ new EqualExpression(
+ gen.singleNameReference(FOUND_BASE),
+ gen.nullLiteral(),
+ OperatorIds.NOT_EQUAL),
+ OperatorIds.AND_AND),
+ gen.block(new Statement[] {
gen.messageSend(
gen.singleNameReference(FIRST_CACHE),
REMOVE,
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 de3310a..9b24699 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
@@ -274,7 +274,11 @@
EqualExpression result = new EqualExpression(
value,
nullLiteral(),
- OperatorIds.EQUAL_EQUAL);
+ OperatorIds.EQUAL_EQUAL) {
+ protected void checkNullComparison(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo, FlowInfo initsWhenTrue, FlowInfo initsWhenFalse) {
+ // nop, never warn about generated null checks
+ }
+ };
result.sourceStart = this.sourceStart;
result.sourceEnd = this.sourceEnd;
result.constant = Constant.NotAConstant;
diff --git a/org.eclipse.jdt.core/grammar/java.g b/org.eclipse.jdt.core/grammar/java.g
index 6eacdc8..8be7063 100644
--- a/org.eclipse.jdt.core/grammar/java.g
+++ b/org.eclipse.jdt.core/grammar/java.g
@@ -404,6 +404,9 @@
TypeAnnotationsopt ::= $empty
/.$putCase consumeZeroTypeAnnotations(); $break ./
TypeAnnotationsopt -> TypeAnnotations
+--{ObjectTeams: after TentativeTypeAnchor confirm that it was a *type annotation*:
+/.$putCase confirmTypeAnnotation(); $break ./
+-- SH}
/:$compliance 1.8:/
/:$readableName TypeAnnotationsopt:/
@@ -2601,12 +2604,13 @@
-- ==== No Nested Generics ====
-- case 1: it was indeed a type anchor:
TypeAnchorOrAnnotatedTypeArgument -> AnyTypeAnchor
--- case 2a: we were wrong in assuming a type anchor, now is the time to convert it into a marker type annotation:
-TypeAnchorOrAnnotatedTypeArgument -> TentativeTypeAnchor NotAnAnchor ReferenceType
+/.$putCase confirmTypeAnchor(); $break ./
+-- case 2a: we were wrong in assuming a type anchor, converted marker type annotation exists, time to clean up
+TypeAnchorOrAnnotatedTypeArgument ::= TentativeTypeAnchor NotAnAnchor ReferenceType
/.$putCase consumeTypeArgumentFromAnchor(); $break ./
/:$readableName TypeArgument:/
/:$compliance 1.5:/
--- case 2b: we were wrong in assuming a type anchor, now is the time to convert it into a marker type annotation:
+-- case 2b: we were wrong in assuming a type anchor, converted marker type annotation exists, time to clean up
TypeAnchorOrAnnotatedTypeArgument -> TentativeTypeAnchor NotAnAnchor Wildcard
/.$putCase consumeAnnotationsOnTypeArgumentFromAnchor(); $break ./
/:$readableName TypeArgument:/
@@ -2615,12 +2619,15 @@
-- ==== One Level Nested Generics ====
-- case 1: it was indeed a type anchor:
TypeAnchorOrAnnotatedTypeArgument1 -> AnyTypeAnchor '>'
--- case 2a: we were wrong in assuming a type anchor, now is the time to convert it into a marker type annotation:
+/.$putCase confirmTypeAnchor(); $break ./
+/:$readableName TypeAnchor:/
+/:$compliance 1.5:/
+-- case 2a: we were wrong in assuming a type anchor, converted marker type annotation exists, time to clean up
TypeAnchorOrAnnotatedTypeArgument1 -> TentativeTypeAnchor NotAnAnchor ReferenceType1
/.$putCase consumeAnnotationsOnTypeArgumentFromAnchor(); $break ./
/:$readableName TypeArgument:/
/:$compliance 1.5:/
--- case 2b: we were wrong in assuming a type anchor, now is the time to convert it into a marker type annotation:
+-- case 2b: we were wrong in assuming a type anchor, converted marker type annotation exists, time to clean up
TypeAnchorOrAnnotatedTypeArgument1 -> TentativeTypeAnchor NotAnAnchor Wildcard1
/.$putCase consumeAnnotationsOnTypeArgumentFromAnchor(); $break ./
/:$readableName TypeArgument:/
@@ -2629,12 +2636,15 @@
-- ==== Two Levels Nested Generics ====
-- case 1: it was indeed a type anchor:
TypeAnchorOrAnnotatedTypeArgument2 -> AnyTypeAnchor '>>'
--- case 2a: we were wrong in assuming a type anchor, now is the time to convert it into a marker type annotation:
+/.$putCase confirmTypeAnchor(); $break ./
+/:$readableName TypeAnchor:/
+/:$compliance 1.5:/
+-- case 2a: we were wrong in assuming a type anchor, converted marker type annotation exists, time to clean up
TypeAnchorOrAnnotatedTypeArgument2 -> TentativeTypeAnchor NotAnAnchor ReferenceType2
/.$putCase consumeAnnotationsOnTypeArgumentFromAnchor(); $break ./
/:$readableName TypeArgument:/
/:$compliance 1.5:/
--- case 2b: we were wrong in assuming a type anchor, now is the time to convert it into a marker type annotation:
+-- case 2b: we were wrong in assuming a type anchor, converted marker type annotation exists, time to clean up
TypeAnchorOrAnnotatedTypeArgument2 -> TentativeTypeAnchor NotAnAnchor Wildcard2
/.$putCase consumeAnnotationsOnTypeArgumentFromAnchor(); $break ./
/:$readableName TypeArgument:/
@@ -2643,19 +2653,22 @@
-- ==== Three Levels Nested Generics ====
-- case 1: it was indeed a type anchor:
TypeAnchorOrAnnotatedTypeArgument3 -> AnyTypeAnchor '>>>'
--- case 2a: we were wrong in assuming a type anchor, now is the time to convert it into a marker type annotation:
+/.$putCase confirmTypeAnchor(); $break ./
+/:$readableName TypeAnchor:/
+/:$compliance 1.5:/
+-- case 2a: we were wrong in assuming a type anchor, converted marker type annotation exists, time to clean up
TypeAnchorOrAnnotatedTypeArgument3 -> TentativeTypeAnchor NotAnAnchor ReferenceType3
/.$putCase consumeAnnotationsOnTypeArgumentFromAnchor(); $break ./
/:$readableName TypeArgument:/
/:$compliance 1.5:/
--- case 2b: we were wrong in assuming a type anchor, now is the time to convert it into a marker type annotation:
+-- case 2b: we were wrong in assuming a type anchor, converted marker type annotation exists, time to clean up
TypeAnchorOrAnnotatedTypeArgument3 -> TentativeTypeAnchor NotAnAnchor Wildcard3
/.$putCase consumeAnnotationsOnTypeArgumentFromAnchor(); $break ./
/:$readableName TypeArgument:/
/:$compliance 1.5:/
-- =====================================
--- trigger converting a mistaken type anchor into a type argument
+-- trigger converting a mistaken type anchor into a type annotation on a type argument
NotAnAnchor ::= $empty
/.$putCase convertTypeAnchor(0); $break ./
/:$readableName annotatedTypeArgument:/
diff --git a/org.eclipse.jdt.core/scripts/export-ecj.xml b/org.eclipse.jdt.core/scripts/export-ecj.xml
index d82ba60..921c94b 100644
--- a/org.eclipse.jdt.core/scripts/export-ecj.xml
+++ b/org.eclipse.jdt.core/scripts/export-ecj.xml
@@ -48,7 +48,7 @@
<property name="target.folder" value="${basedir}/bin"/>
<replace file="${target.folder}/org/eclipse/jdt/internal/compiler/batch/messages.properties" token="bundle_qualifier" value="${bundleVersionQualifer}"/>
<!-- {ObjectTeams: more replacement (strategy no longer maintained in JDT?) -->
- <replace file="${target.folder}/org/eclipse/jdt/internal/compiler/batch/messages.properties" token="bundle_version" value="${bundleVersion}"/>
+ <replace file="${target.folder}/org/eclipse/jdt/internal/compiler/batch/messages.properties" token="bundle_version" value="${bundleVersionMajor}.${bundleVersionMinor}.${bundleVersionService}"/>
<!-- SH}-->
<echo message="Extract .class file and properties for the batch compiler" />
<copy todir="${ecj-temp-folder}">
diff --git a/plugins/org.eclipse.objectteams.otdt/META-INF/MANIFEST.MF b/plugins/org.eclipse.objectteams.otdt/META-INF/MANIFEST.MF
index 357c527..8518f14 100644
--- a/plugins/org.eclipse.objectteams.otdt/META-INF/MANIFEST.MF
+++ b/plugins/org.eclipse.objectteams.otdt/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@
Bundle-ManifestVersion: 2
Bundle-Name: %pluginName
Bundle-SymbolicName: org.eclipse.objectteams.otdt;singleton:=true
-Bundle-Version: 2.3.0.qualifier
+Bundle-Version: 2.3.1.qualifier
Bundle-ClassPath: otdtcoreext.jar
Bundle-Activator: org.eclipse.objectteams.otdt.core.ext.OTDTPlugin
Bundle-Vendor: %providerName
diff --git a/plugins/org.eclipse.objectteams.otdt/about.ini b/plugins/org.eclipse.objectteams.otdt/about.ini
index 60b21f3..e883d2c 100644
--- a/plugins/org.eclipse.objectteams.otdt/about.ini
+++ b/plugins/org.eclipse.objectteams.otdt/about.ini
@@ -1,6 +1,6 @@
aboutText=Object Teams Development Tooling\n\
\n\
-Version: 2.3.0\n\
+Version: 2.3.1\n\
\n\
Part of the Eclipse Luna Simultaneous Release\n\
\n\
diff --git a/plugins/org.eclipse.objectteams.otdt/src/org/eclipse/objectteams/otdt/core/ext/OTREContainer.java b/plugins/org.eclipse.objectteams.otdt/src/org/eclipse/objectteams/otdt/core/ext/OTREContainer.java
index c1c432b..b0705c8 100644
--- a/plugins/org.eclipse.objectteams.otdt/src/org/eclipse/objectteams/otdt/core/ext/OTREContainer.java
+++ b/plugins/org.eclipse.objectteams.otdt/src/org/eclipse/objectteams/otdt/core/ext/OTREContainer.java
@@ -21,6 +21,7 @@
package org.eclipse.objectteams.otdt.core.ext;
import java.io.IOException;
+import java.net.URL;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
@@ -261,8 +262,12 @@
int i = 0;
BYTECODE_WEAVER_PATHS[asm][i++] = new Path(OTVariableInitializer.getInstallatedPath(OTDTPlugin.getDefault(), OTDRE_PLUGIN_NAME, "bin")); //$NON-NLS-1$
for (String bundleName : ASM_BUNDLE_NAMES) {
- for (Bundle bundle : packageAdmin.getBundles(bundleName, ASM_VERSION_RANGE))
- BYTECODE_WEAVER_PATHS[asm][i++] = new Path(FileLocator.toFileURL(bundle.getEntry("/")).getFile()); //$NON-NLS-1$
+ for (Bundle bundle : packageAdmin.getBundles(bundleName, ASM_VERSION_RANGE)) {
+ URL bundleEntry = bundle.getEntry("bin"); // source project?
+ if (bundleEntry == null)
+ bundleEntry = bundle.getEntry("/"); // binary bundle
+ BYTECODE_WEAVER_PATHS[asm][i++] = new Path(FileLocator.toFileURL(bundleEntry).getFile()); //$NON-NLS-1$
+ }
}
if (i == 4)
break ASM;
diff --git a/plugins/org.eclipse.objectteams.otequinox/META-INF/MANIFEST.MF b/plugins/org.eclipse.objectteams.otequinox/META-INF/MANIFEST.MF
index cd8e62d..6dc3a87 100644
--- a/plugins/org.eclipse.objectteams.otequinox/META-INF/MANIFEST.MF
+++ b/plugins/org.eclipse.objectteams.otequinox/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@
Bundle-ManifestVersion: 2
Bundle-Name: %pluginName
Bundle-SymbolicName: org.eclipse.objectteams.otequinox;singleton:=true
-Bundle-Version: 2.3.0.qualifier
+Bundle-Version: 2.3.1.qualifier
Bundle-Activator: org.eclipse.objectteams.otequinox.TransformerPlugin
Bundle-Vendor: %providerName
Bundle-Localization: plugin
diff --git a/plugins/org.eclipse.objectteams.otequinox/META-INF/p2.inf b/plugins/org.eclipse.objectteams.otequinox/META-INF/p2.inf
index 08c8c87..a086d52 100644
--- a/plugins/org.eclipse.objectteams.otequinox/META-INF/p2.inf
+++ b/plugins/org.eclipse.objectteams.otequinox/META-INF/p2.inf
@@ -4,11 +4,11 @@
units.0.id = org.eclipse.objectteams.otequinox.configuration
-units.0.version = 2.3.0.$qualifier$
+units.0.version = 2.3.1.$qualifier$
units.0.hostRequirements.0.namespace=osgi.bundle
units.0.hostRequirements.0.name=org.eclipse.objectteams.otequinox
-units.0.hostRequirements.0.range=[2.3.0,3.0.0)
+units.0.hostRequirements.0.range=[2.3.1,3.0.0)
units.0.hostRequirements.1.namespace = org.eclipse.equinox.p2.eclipse.type
units.0.hostRequirements.1.name = bundle
units.0.hostRequirements.1.range = [1.0.0,2.0.0)
@@ -17,7 +17,7 @@
units.0.properties.0.value = true
units.0.requires.0.namespace = osgi.bundle
units.0.requires.0.name = org.eclipse.objectteams.otequinox
-units.0.requires.0.range = [2.3.0,3.0.0)
+units.0.requires.0.range = [2.3.1,3.0.0)
units.0.requires.1.namespace = org.eclipse.equinox.p2.eclipse.type
units.0.requires.1.name = bundle
@@ -26,7 +26,7 @@
units.0.provides.0.namespace = org.eclipse.equinox.p2.iu
units.0.provides.0.name = org.eclipse.objectteams.otequinox.configuration
-units.0.provides.0.version = 2.3.0.$qualifier$
+units.0.provides.0.version = 2.3.1.$qualifier$
units.0.instructions.install=\
installBundle(bundle:${artifact})
diff --git a/plugins/org.eclipse.objectteams.otequinox/about.ini b/plugins/org.eclipse.objectteams.otequinox/about.ini
index f04a271..0962c8c 100644
--- a/plugins/org.eclipse.objectteams.otequinox/about.ini
+++ b/plugins/org.eclipse.objectteams.otequinox/about.ini
@@ -1,6 +1,6 @@
aboutText=Object Teams -- Equinox integration (OT/Equinox)\n\
\n\
-Version: 2.3.0\n\
+Version: 2.3.1\n\
\n\
Part of the Eclipse Luna Simultaneous Release\n\
\n\
diff --git a/plugins/org.eclipse.objectteams.otequinox/schema/aspectBindingNegotiators.exsd b/plugins/org.eclipse.objectteams.otequinox/schema/aspectBindingNegotiators.exsd
index 9d54188..2c646df 100644
--- a/plugins/org.eclipse.objectteams.otequinox/schema/aspectBindingNegotiators.exsd
+++ b/plugins/org.eclipse.objectteams.otequinox/schema/aspectBindingNegotiators.exsd
@@ -133,7 +133,7 @@
which accompanies this distribution, and is available at
<a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>
</p><p>
-Please visit <a href="http://www.objectteams.org">www.objectteams.org</a> for updates and contact.
+Please visit <a href="http://www.eclipse.org/objectteams">www.eclipse.org/objectteams</a> for updates and contact.
</p><p>
Contributors:<br>
Technical University Berlin - Initial API and implementation
diff --git a/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/internal/osgi/weaving/AspectBinding.java b/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/internal/osgi/weaving/AspectBinding.java
index 0759da8..98ea905 100644
--- a/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/internal/osgi/weaving/AspectBinding.java
+++ b/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/internal/osgi/weaving/AspectBinding.java
@@ -67,7 +67,7 @@
final List<TeamBinding> subTeams = new ArrayList<>();
Set<TeamBinding> equivalenceSet = new HashSet<>();
- ActivationKind activation;
+ private ActivationKind activation; // clients must use accessor getActivation()!
boolean hasScannedBases;
boolean hasScannedRoles;
@@ -183,6 +183,16 @@
public String toString() {
return "team "+teamName+"("+(this.activation)+") super "+superTeamName;
}
+
+ /** Get the highest activation kind from this team and its equivalents. */
+ public ActivationKind getActivation() {
+ ActivationKind activation = this.activation;
+ for (TeamBinding equiv : this.equivalenceSet) {
+ if (equiv.activation.ordinal() > activation.ordinal())
+ activation = equiv.activation;
+ }
+ return activation;
+ }
}
/**
@@ -228,7 +238,7 @@
@NonNull ActivationKind kind = ActivationKind.NONE;
try {
if (activationSpecifier != null)
- kind = ActivationKind.valueOf(activationSpecifier);
+ kind = ActivationKind.valueOf(activationSpecifier); // well-known API of all enums
} catch (IllegalArgumentException iae) {
log(iae, "Invalid activation kind "+activationSpecifier+" for team "+teamName);
}
@@ -278,7 +288,7 @@
/** If a given team requires no activation, check if its super team should be activated instead. */
public @Nullable TeamBinding getOtherTeamToActivate(TeamBinding team) {
TeamBinding superTeam = team.superTeam;
- if (superTeam != null && superTeam.activation != ActivationKind.NONE) {
+ if (superTeam != null && superTeam.getActivation() != ActivationKind.NONE) {
return superTeam;
}
// sub teams?
diff --git a/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/internal/osgi/weaving/AspectBindingRegistry.java b/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/internal/osgi/weaving/AspectBindingRegistry.java
index 925ca14..5d9da54 100644
--- a/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/internal/osgi/weaving/AspectBindingRegistry.java
+++ b/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/internal/osgi/weaving/AspectBindingRegistry.java
@@ -1,7 +1,7 @@
/**********************************************************************
* This file is part of "Object Teams Development Tooling"-Software
*
- * Copyright 2008, 2013 Technical University Berlin, Germany and others.
+ * Copyright 2008, 2014 Technical University Berlin, Germany and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -45,7 +45,6 @@
import org.eclipse.objectteams.internal.osgi.weaving.AspectBinding.TeamBinding;
import org.eclipse.objectteams.otequinox.Constants;
import org.osgi.framework.Bundle;
-import org.osgi.service.packageadmin.PackageAdmin;
/**
* An instance of this class holds the information loaded from extensions
@@ -81,7 +80,7 @@
/* Load extensions for org.eclipse.objectteams.otequinox.aspectBindings and check aspect permissions. */
public void loadAspectBindings(
IExtensionRegistry extensionRegistry,
- @SuppressWarnings("deprecation") @Nullable PackageAdmin packageAdmin,
+ @SuppressWarnings("deprecation") @Nullable org.osgi.service.packageadmin.PackageAdmin packageAdmin,
OTWeavingHook hook)
{
IConfigurationElement[] aspectBindingConfigs = extensionRegistry
@@ -166,7 +165,7 @@
@SuppressWarnings("deprecation") // multiple uses of deprecated but still recommended class PackageAdmin
private boolean checkRequiredFragments(String aspectBundleId, String baseBundleId, IConfigurationElement[] fragments,
- @Nullable PackageAdmin packageAdmin)
+ @Nullable org.osgi.service.packageadmin.PackageAdmin packageAdmin)
{
// checking only, no real action needed.
boolean hasError = false;
diff --git a/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/internal/osgi/weaving/AspectPermissionManager.java b/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/internal/osgi/weaving/AspectPermissionManager.java
new file mode 100644
index 0000000..78343ab
--- /dev/null
+++ b/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/internal/osgi/weaving/AspectPermissionManager.java
@@ -0,0 +1,644 @@
+/**********************************************************************
+ * This file is part of "Object Teams Development Tooling"-Software
+ *
+ * Copyright 2009 Germany and Technical University Berlin, Germany.
+ *
+ * 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:
+ * Technical University Berlin - Initial API and implementation
+ **********************************************************************/
+package org.eclipse.objectteams.internal.osgi.weaving;
+
+import static org.eclipse.objectteams.otequinox.AspectPermission.DENY;
+import static org.eclipse.objectteams.otequinox.AspectPermission.GRANT;
+import static org.eclipse.objectteams.otequinox.AspectPermission.UNDEFINED;
+import static org.eclipse.objectteams.otequinox.TransformerPlugin.log;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Properties;
+import java.util.Set;
+
+import org.eclipse.core.internal.runtime.InternalPlatform;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExtensionRegistry;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.objectteams.internal.osgi.weaving.AspectBinding.BaseBundle;
+import org.eclipse.objectteams.internal.osgi.weaving.AspectBinding.TeamBinding;
+import org.eclipse.objectteams.otequinox.ActivationKind;
+import org.eclipse.objectteams.otequinox.AspectBindingRequestAnswer;
+import org.eclipse.objectteams.otequinox.AspectPermission;
+import org.eclipse.objectteams.otequinox.Constants;
+import org.eclipse.objectteams.otequinox.IAspectRequestNegotiator;
+import org.eclipse.objectteams.otequinox.TransformerPlugin;
+import org.eclipse.osgi.internal.hookregistry.HookConfigurator;
+import org.eclipse.osgi.service.datalocation.Location;
+import org.objectteams.Team;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleException;
+
+/**
+ * Manage permissions of aspect bundles requesting to apply aspectBindings and forcedExports.
+ * The following pieces of information are checked:
+ * <ul>
+ * <li>properties set in installation-wide config.ini or as command line args (handled by {@link HookConfigurator} (plus internal class OTStorageHook))</li>
+ * <li>defaults set per workspace (file negotiationDefaults.txt)</li>
+ * <li>individual GRANT/DENY per workspace (files grantedForcedExports.txt, deniedForcedExports.txt)</li>
+ * <li>answers from registered negotiators (extension point org.eclipse.objectteams.otequinox.aspectBindingNegotiators, see {@link IAspectRequestNegotiator})</li>
+ * </ul>
+ *
+ * <p>
+ * The final answer for a given request is combined from all sources where the priority of any {@link #DENY} answer is highest,
+ * of {@link #UNDEFINED} is lowest.
+ * </p>
+ * <p>
+ * If a negotiator has determined a decision and its answer has the <code>persistent</code> flag set,
+ * this particular aspect permission is stored as per-workspace configuration.
+ * </p>
+ * @author stephan
+ * @since 1.2.6
+ */
+@SuppressWarnings("restriction")
+@NonNullByDefault
+public class AspectPermissionManager {
+
+ // FIXME: forced exports are unimplemented as of OT/Equinox 2.3.0!
+
+ // property names for default configuration:
+ private static final String FORCED_EXPORT_DEFAULT = "forced.export.default";
+ private static final String ASPECT_BINDING_DEFAULT = "aspect.binding.default";
+
+ // workspace files where negotiation configuration is stored:
+ private static final String NEGOTIATION_DEFAULTS_FILE = "negotiationDefaults.txt";
+ private static final String GRANTED_FORCED_EXPORTS_FILE = "grantedForcedExports.txt";
+ private static final String DENIED_FORCED_EXPORTS_FILE = "deniedForcedExports.txt";
+
+
+ // set of aspect plug-ins for which some permission has been denied:
+ private Set<String> deniedAspects = new HashSet<String>();
+ // default permission for aspect bindings:
+ private AspectPermission defaultAspectBindingPermission = GRANT;
+ // default permission for forced exports:
+ private AspectPermission defaultForcedExportPermission = UNDEFINED; // not yet granted, but open for receiving a GRANT
+ // for negotiation of aspect binding requests (incl. forced export):
+ private List<IAspectRequestNegotiator> negotiators = new ArrayList<IAspectRequestNegotiator>();
+
+
+ // collect all forced exports (denied/granted), granted should balance to an empty structure.
+ // structure is: aspect-id -> (base bundle x base package)*
+ private HashMap<String, ArrayList<String[]>> deniedForcedExportsByAspect= new HashMap<String, ArrayList<String[]>>();
+ private HashMap<String, ArrayList<String[]>> grantedForcedExportsByAspect= new HashMap<String, ArrayList<String[]>>();
+
+ // key is aspectId+"->"+baseId, value is array of team names
+ private HashMap<String, Set<String>> deniedTeamsByAspectBinding = new HashMap<String, Set<String>>();
+ private HashMap<String, Set<String>> grantedTeamsByAspectBinding = new HashMap<String, Set<String>>();
+
+ // the workspace directory for storing the state of this plugin
+ @Nullable private IPath otequinoxState;
+ // back link needed for accessing the state location:
+ private Bundle transformerBundle;
+ // helper instance needed to stop bundles by name
+ @SuppressWarnings("deprecation")
+ @Nullable private org.osgi.service.packageadmin.PackageAdmin packageAdmin;
+
+ public AspectPermissionManager(Bundle bundle,
+ @SuppressWarnings("deprecation") @Nullable org.osgi.service.packageadmin.PackageAdmin packageAdmin)
+ {
+ this.transformerBundle = bundle;
+ this.packageAdmin = packageAdmin;
+ }
+
+ /* local cache for isReady(): */
+ private boolean isWaitingForLocation = true;
+
+ /** Before using this permission manager a client must check whether we're ready (instance location set). */
+ public boolean isReady() {
+ if (!isWaitingForLocation)
+ return true;
+ try {
+ InternalPlatform platform = InternalPlatform.getDefault();
+ Location instanceLocation = platform.getInstanceLocation();
+ if (!instanceLocation.isSet())
+ return false; // not yet capable
+ this.isWaitingForLocation = false;
+ fetchAspectBindingPermssionsFromWorkspace();
+ } catch (NoClassDefFoundError ncdfe) {
+ log(IStatus.WARNING, "Optional class InternalPlatform not found, cannot access workspace location");
+ this.isWaitingForLocation = false;
+ return true;
+ }
+ if (!this.obligations.isEmpty())
+ for (Runnable job : this.obligations)
+ job.run();
+ return true;
+ }
+
+ /**
+ * Fetch stored permissions from this plugin's workspace state.
+ *
+ * @pre instance location should be set (see {@link #isReady()}),
+ * otherwise will silently return without accessing workspace settings.
+ */
+ private void fetchAspectBindingPermssionsFromWorkspace() {
+ try {
+ @SuppressWarnings("null") // known API
+ @NonNull IPath state = InternalPlatform.getDefault().getStateLocation(this.transformerBundle, true);
+ this.otequinoxState = state;
+ internalFetchAspectBindingPermssionsFromWorkspace(state);
+ } catch (NoClassDefFoundError ncdfe) {
+ log(IStatus.WARNING, "Optional class InternalPlatform not found, cannot access workspace location");
+ return;
+ }
+ }
+
+ private void internalFetchAspectBindingPermssionsFromWorkspace(IPath state) {
+ // defaults:
+ IPath configFilePath = state.append(NEGOTIATION_DEFAULTS_FILE);
+ File configFile = new File(configFilePath.toOSString());
+ if (configFile.exists()) {
+ Properties props = new Properties();
+ try {
+ try (FileInputStream inStream = new FileInputStream(configFile)) {
+ props.load(inStream);
+ }
+ String value = (String) props.get(ASPECT_BINDING_DEFAULT);
+ if (value != null)
+ try {
+ defaultAspectBindingPermission = AspectPermission.valueOf(value); // known API of all enums
+ } catch (IllegalArgumentException iae) {
+ defaultAspectBindingPermission = AspectPermission.DENY;
+ log(iae, "Cannot set default aspect permission from file "+NEGOTIATION_DEFAULTS_FILE+", assuming DENY.");
+ }
+ value = (String) props.get(FORCED_EXPORT_DEFAULT);
+ if (value != null)
+ try {
+ defaultForcedExportPermission = AspectPermission.valueOf(value); // known API of all enums
+ } catch (IllegalArgumentException iae) {
+ defaultForcedExportPermission = AspectPermission.DENY;
+ log(iae, "Cannot set default forced exports permission from file "+NEGOTIATION_DEFAULTS_FILE+", assuming DENY.");
+ }
+ } catch (IOException ioex) {
+ log(ioex, "Failed to read configuration file "+configFilePath.toOSString());
+ }
+ } else {
+ try {
+ File stateDir = new File(state.toOSString());
+ if (!stateDir.exists())
+ stateDir.mkdirs();
+ configFile.createNewFile();
+ writeNegotiationDefaults(configFile);
+ } catch (IOException ioex) {
+ log(ioex, "Failed to create configuration file "+configFilePath.toOSString());
+ }
+ }
+
+// // explicitly denied:
+// configFilePath = this.otequinoxState.append(DENIED_FORCED_EXPORTS_FILE);
+// configFile = new File(configFilePath.toOSString());
+// if (configFile.exists())
+// HookConfigurator.parseForcedExportsFile(configFile, DENY);
+
+// // explicitly granted:
+// configFilePath = this.otequinoxState.append(GRANTED_FORCED_EXPORTS_FILE);
+// configFile = new File(configFilePath.toOSString());
+// if (configFile.exists())
+// HookConfigurator.parseForcedExportsFile(configFile, GRANT);
+ }
+
+ private void writeNegotiationDefaults(File configFile)
+ throws IOException
+ {
+ try (FileWriter writer = new FileWriter(configFile)) {
+ writer.append(ASPECT_BINDING_DEFAULT+'='+defaultAspectBindingPermission.toString()+'\n');
+ writer.append(FORCED_EXPORT_DEFAULT+'='+defaultForcedExportPermission.toString()+'\n');
+ writer.flush();
+ }
+ log(IStatus.INFO, "Created aspect binding defaults file "+configFile.getCanonicalPath());
+ }
+
+
+ /** Load extensions for EP org.eclipse.objectteams.otequinox.aspectBindingNegotiators. */
+ public void loadAspectBindingNegotiators(IExtensionRegistry extensionRegistry) {
+ IConfigurationElement[] aspectBindingNegotiatorsConfigs = extensionRegistry.getConfigurationElementsFor(
+ Constants.TRANSFORMER_PLUGIN_ID, Constants.ASPECT_NEGOTIATOR_EXTPOINT_ID);
+ for (int i = 0; i < aspectBindingNegotiatorsConfigs.length; i++) {
+ IConfigurationElement currentNegotiatorConfig = aspectBindingNegotiatorsConfigs[i];
+ try {
+ Object negotiator = currentNegotiatorConfig.createExecutableExtension("class");
+ if (negotiator != null)
+ this.negotiators.add(((IAspectRequestNegotiator)negotiator));
+ } catch (CoreException e) {
+ log(e, "Failed to instantiate extension "+currentNegotiatorConfig);
+ }
+ }
+ }
+
+ /** Delegatee of internal API {@link TransformerPlugin#isDeniedAspectPlugin(String)}. */
+ public boolean isDeniedAspectPlugin(String symbolicName) {
+ return this.deniedAspects.contains(symbolicName);
+ }
+
+
+ /**
+ * Check whether a given aspect requests forced exports from base,
+ * and whether these requests are granted/denied by checking all available sources.
+ *
+ * Clients should ask {@link #isReady()} (ie., instance location is set) before calling this method,
+ * otherwise workspace settings have to be silently ignored (any error should be signaled by client).
+ *
+ * @param aspectId symbolic name of the aspect bundle
+ * @param baseBundleId symbolic name of the bound base bundle
+ * @param forcedExports any forced exports requested in this aspect binding.
+ * @return whether all requests (if any) have been granted
+ */
+ public boolean checkForcedExports(String aspectId, String baseBundleId, @Nullable IConfigurationElement[] forcedExports)
+ {
+ if (forcedExports == null || forcedExports.length == 0)
+ return true;
+
+ ArrayList<String[]> deniedForcedExports = getConfiguredForcedExports(aspectId, DENY, deniedForcedExportsByAspect);
+ ArrayList<String[]> grantedForcedExports= getConfiguredForcedExports(aspectId, GRANT, grantedForcedExportsByAspect);
+
+ // iterate all requested forcedExports to search for a matching permission:
+ for (IConfigurationElement forcedExport : forcedExports) { // [0..1] (as defined in the schema)
+ String forcedExportsRequest = forcedExport.getValue();
+ if (forcedExportsRequest == null)
+ continue;
+ for (@NonNull String singleForcedExportRequest : forcedExportsRequest.split(",")) // well-known API
+ {
+ singleForcedExportRequest = singleForcedExportRequest.trim(); // well-known API
+
+ String[] listEntry;
+ boolean grantReported = false;
+ AspectPermission negotiatedPermission = this.defaultForcedExportPermission;
+
+ // DENY by default?
+ if (negotiatedPermission == DENY) {
+ log(IStatus.ERROR, "Default denial of forced export regarding package "+singleForcedExportRequest+
+ " from bundle "+baseBundleId+" as requested by bundle "+aspectId+"; bundle not activated");
+ this.deniedAspects.add(aspectId); // keep for answering the TransformerHook.
+ return false; // NOPE!
+ }
+
+ // DENY from configuration?
+ listEntry = findRequestInList(baseBundleId, singleForcedExportRequest, deniedForcedExports);
+ if (listEntry != null) {
+ log(IStatus.ERROR, "Explicit denial of forced export regarding package "+singleForcedExportRequest+
+ " from bundle "+baseBundleId+" as requested by bundle "+aspectId+"; bundle not activated");
+ this.deniedAspects.add(aspectId); // keep for answering the TransformerHook.
+ return false; // NOPE!
+ }
+
+ // GRANT from configuration?
+ listEntry = findRequestInList(baseBundleId, singleForcedExportRequest, grantedForcedExports);
+ if (listEntry != null) {
+ log(IStatus.INFO, "Forced export granted for "+aspectId+": "+singleForcedExportRequest+" (from bundle "+baseBundleId+")");
+ grantReported = true;
+ grantedForcedExports.remove(listEntry);
+ negotiatedPermission = GRANT;
+ }
+
+ // default and persistent configuration did not DENY, proceed to the negotiators:
+ boolean shouldPersist = false;
+ for (IAspectRequestNegotiator negotiator : this.negotiators) {
+ AspectBindingRequestAnswer answer = negotiator.checkForcedExport(aspectId, baseBundleId, singleForcedExportRequest, negotiatedPermission);
+ if (answer.permission.compareTo(negotiatedPermission) > 0) // increasing priority of answer?
+ {
+ shouldPersist = answer.persistent;
+ negotiatedPermission = answer.permission;
+ // locally store as default for subsequent requests (not persistent, see below):
+ if (answer.allRequests)
+ this.defaultForcedExportPermission = negotiatedPermission;
+ if (negotiatedPermission == DENY)
+ break; // end of discussion.
+ }
+ }
+
+ // make decision persistent?
+ if (shouldPersist && negotiatedPermission != UNDEFINED)
+ // FIXME(SH): handle "allRequests":
+ persistForcedExportsAnswer(aspectId, baseBundleId, singleForcedExportRequest, negotiatedPermission);
+
+ // report:
+ if (negotiatedPermission == GRANT) {
+ if (!grantReported)
+ log(IStatus.INFO, "Negotiation granted forced export for "+aspectId+
+ ": "+singleForcedExportRequest+" (from bundle "+baseBundleId+')');
+ } else {
+ String verb = "did not grant";
+ if (negotiatedPermission == DENY)
+ verb = "denied";
+ log(IStatus.ERROR, "Negotiation "+verb+" forced export for "+aspectId+
+ ": "+singleForcedExportRequest+" (from bundle "+baseBundleId+")"+
+ ". Aspect is not activated.");
+ this.deniedAspects.add(aspectId); // keep for answering the TransformerHook.
+ return false; // don't install illegal aspect
+ }
+ }
+ }
+ if (!grantedForcedExports.isEmpty())
+ reportUnmatchForcedExports(aspectId, grantedForcedExports);
+ return true;
+ }
+
+ /**
+ * Get the forced exports configured for a given aspect bundle with permission <code>perm</code>.
+ * Consult {@link HookConfigurator} and store the result in <code>map</code>.
+ *
+ * @param aspectId symbolic name of the aspect in focus
+ * @param perm are we asking about DENY or GRANT?
+ * @param map in/out param for storing results from OTStorageHook
+ * @return list of pairs (base bundle x base package)
+ */
+ private ArrayList<String[]> getConfiguredForcedExports(String aspectId,
+ AspectPermission perm,
+ HashMap<String, ArrayList<String[]>> map)
+ {
+ ArrayList<String[]> forcedExports= map.get(aspectId);
+ if (forcedExports == null) {
+ // fetch declarations from config.ini or other locations.
+// FIXME
+// forcedExports= HookConfigurator.getForcedExportsByAspect(aspectId, perm);
+// map.put(aspectId, forcedExports);
+ forcedExports = new ArrayList<String[]>();
+ }
+ return forcedExports;
+ }
+
+ private @Nullable String[] findRequestInList(String baseBundleId, String basePackage, @Nullable ArrayList<String[]> list) {
+ if (list != null)
+ for (String[] singleExport : list)
+ if ( singleExport[0].equals(baseBundleId)
+ && singleExport[1].equals(basePackage))
+ {
+ return singleExport;
+ }
+ return null;
+ }
+
+ /**
+ * If the structure of grantedForcedExports is not empty we have mismatches between forced-export declarations.
+ * Report these mismatches as warnings.
+ */
+ void reportUnmatchForcedExports(String aspectId, ArrayList<String[]> unmatchedForcedExports)
+ {
+ for (String[] export: unmatchedForcedExports) {
+ String baseId = export[0];
+ String pack = export[1];
+ log(IStatus.WARNING, "Aspect "+aspectId+
+ " does not declare forced export of package "+
+ pack+" from bundle "+baseId+
+ " as declared in config.ini (or system property)");
+ }
+ }
+
+ /* Simple strategy to append a forced export to a file (existing or to be created). */
+ private void persistForcedExportsAnswer(String aspectId, String baseBundleId, String basePackage, AspectPermission negotiatedPermission)
+ {
+ IPath state = this.otequinoxState;
+ if (state == null) {
+ log(IStatus.ERROR, "Can't persist forcedExports permission, no workspace location accessable.");
+ return;
+ }
+ try {
+ String fileName = (negotiatedPermission == DENY) ? DENIED_FORCED_EXPORTS_FILE : GRANTED_FORCED_EXPORTS_FILE;
+ IPath forcedExportsPath = state.append(fileName);
+ File forcedExportsFile = new File(forcedExportsPath.toOSString());
+ if (!forcedExportsFile.exists())
+ forcedExportsFile.createNewFile();
+ try (FileWriter writer = new FileWriter(forcedExportsFile, true)) { // FIXME(SH): consider merge (after decision about file format)
+ writer.append('\n');
+ writer.append(baseBundleId);
+ writer.append("\n[\n\t");
+ writer.append(basePackage);
+ writer.append(";x-friends:=\"");
+ writer.append(aspectId);
+ writer.append("\"\n]\n");
+ writer.flush();
+ }
+ } catch (IOException ioe) {
+ log(ioe, "Failed to persist negotiation result");
+ }
+ }
+
+ /**
+ * Check the permissions for all given teams.
+ * @param aspectBundle the bundle containing the given teams
+ * @param aspectBinding the binding mentioning the given teams
+ * @param teamsForBase the teams to check
+ * @return true if at least one binding was denied.
+ */
+ boolean checkAspectPermissionDenial(Bundle aspectBundle, AspectBinding aspectBinding, Collection<TeamBinding> teamsForBase)
+ {
+ boolean hasDenial = false;
+ String aspectBundleName = aspectBundle.getSymbolicName();
+ if (aspectBundleName == null) {
+ log(IStatus.ERROR, "Cannot handle unnamed bundle "+aspectBundle);
+ } else {
+ for (TeamBinding teamForBase : teamsForBase)
+ if (!checkTeamBinding(aspectBundleName, aspectBinding.basePluginName, teamForBase.teamName)) {
+ hasDenial = true;
+ try {
+ aspectBundle.stop();
+ log(IStatus.ERROR, "Stopped bundle "+aspectBundleName+" which requests unconfirmed aspect binding(s).");
+ } catch (Throwable t) { // don't let the aspect bundle get by by throwing an unexpected exception!
+ log(t, "Failed to stop bundle "+aspectBundleName+" which requests unconfirmed aspect binding(s).");
+ }
+ }
+ }
+ return hasDenial;
+ }
+
+ /**
+ * Check permission for the aspect binding of one specific team.
+ *
+ * Clients should ask {@link #isReady()} (ie., instance location is set) before calling this method,
+ * otherwise workspace settings have to be silently ignored (any error should be signaled by client).
+ *
+ * @param aspectBundleId
+ * @param baseBundleId
+ * @param teamClass
+ * @return whether this team is permitted to adapt classes from the given base bundle.
+ */
+ boolean checkTeamBinding(String aspectBundleId, String baseBundleId, String teamClass)
+ {
+ boolean shouldReportGrant = false; // grant by default should not be reported
+ AspectPermission negotiatedPermission = this.defaultAspectBindingPermission;
+
+ // DENY by default?
+ if (negotiatedPermission == DENY) {
+ log(IStatus.ERROR, "Default denial of aspect binding regarding base bundle "+baseBundleId+
+ " as requested by bundle "+aspectBundleId+"; bundle not activated");
+ this.deniedAspects.add(aspectBundleId); // keep for answering the TransformerHook.
+ return false; // NOPE!
+ }
+
+
+ String key = aspectBundleId+"->"+baseBundleId;
+
+ // denied from configuration?
+ Set<String> deniedTeams = deniedTeamsByAspectBinding.get(key);
+ if (deniedTeams != null && !deniedTeams.isEmpty()) {
+ if (deniedTeams.contains(teamClass)) {
+ deniedAspects.add(aspectBundleId);
+ return false;
+ }
+ }
+
+ // granted from configuration?
+ Set<String> grantedTeams = grantedTeamsByAspectBinding.get(key);
+ if (grantedTeams != null && grantedTeams.contains(teamClass)) {
+ negotiatedPermission = GRANT;
+ shouldReportGrant = true;
+ }
+
+ // default and persistent configuration did not DENY, proceed to the negotiators:
+ boolean shouldPersist = false;
+ String denyingNegotiator = null;
+ for (IAspectRequestNegotiator negotiator : this.negotiators) {
+ AspectBindingRequestAnswer answer = negotiator.checkAspectBinding(aspectBundleId, baseBundleId, teamClass, negotiatedPermission);
+ if (answer.permission.compareTo(negotiatedPermission) > 0) // increasing priority of answer?
+ {
+ shouldPersist = answer.persistent;
+ negotiatedPermission = answer.permission;
+ shouldReportGrant = negotiatedPermission == GRANT;
+ // locally store as default for subsequent requests:
+ if (answer.allRequests)
+ this.defaultAspectBindingPermission = negotiatedPermission;
+
+ if (negotiatedPermission == DENY) {
+ denyingNegotiator = negotiator.getClass().getName();
+ break; // end of discussion.
+ }
+ }
+ }
+
+ // make decision persistent?
+ if (shouldPersist && negotiatedPermission != UNDEFINED)
+ persistTeamBindingAnswer(aspectBundleId, baseBundleId, teamClass, negotiatedPermission);
+
+ // report:
+ if (negotiatedPermission == GRANT) {
+ if (shouldReportGrant)
+ log(IStatus.INFO, "Negotiation granted aspect binding for "+aspectBundleId+
+ " to base bundle "+baseBundleId+" by means of team "+teamClass+'.');
+ } else {
+ String front = (negotiatedPermission == DENY)
+ ? "Negotiator "+denyingNegotiator + " denied"
+ : "Negotiation did not grant";
+ log(IStatus.ERROR, front+" aspect binding for "+aspectBundleId+
+ " to base bundle "+baseBundleId+" by means of team "+teamClass+
+ ". Aspect is not activated.");
+ this.deniedAspects.add(aspectBundleId); // keep for answering the TransformerHook.
+ return false; // don't install illegal aspect
+ }
+ return true;
+ }
+
+ private void persistTeamBindingAnswer(String aspectBundleId, String baseBundleId, String teamClass, AspectPermission negotiatedPermission)
+ {
+ // FIXME(SH): implement persisting these!
+ }
+
+ List<Runnable> obligations = new ArrayList<Runnable>();
+ public void addBaseBundleObligations(final List<Team> teamInstances, final Collection<TeamBinding> teamClasses, final Bundle aspectBundle, final BaseBundle baseBundle) {
+ schedule(new Runnable() {
+ public void run() {
+ List<TeamBinding> teamsToRevert = new ArrayList<TeamBinding>();
+ // aspect bindings:
+ String aspectBundleName = aspectBundle.getSymbolicName();
+ if (aspectBundleName == null) {
+ log(IStatus.ERROR, "Cannot handle unnamed aspect bundle "+aspectBundle);
+ } else {
+ for (TeamBinding teamClass : teamClasses)
+ if (!checkTeamBinding(aspectBundleName, baseBundle.bundleName, teamClass.teamName))
+ teamsToRevert.add(teamClass);
+ }
+ if (!teamsToRevert.isEmpty())
+ revert(teamsToRevert);
+ }
+ void revert(List<TeamBinding> teamsToRevert) {
+ try {
+ Set<Bundle> bundlesToStop = new HashSet<Bundle>();
+ for (TeamBinding teamClass : teamClasses) {
+ if (teamClass.getActivation() != ActivationKind.NONE) {
+ for (Team teamInstance : teamInstances)
+ if (teamInstance.getClass() == teamClass.teamClass)
+ teamInstance.deactivate(Team.ALL_THREADS);
+ // could also check if roles are present already ...
+ }
+ bundlesToStop.add(aspectBundle);
+ }
+ for (Bundle bundle : bundlesToStop) {
+ log(IStatus.ERROR, "Stopping aspect bundle "+bundle.getSymbolicName()+" with denied aspect binding(s)");
+ bundle.stop();
+ }
+ } catch (Exception e) {
+ log(e, "Failed to revert aspect bundle with denied aspect bindings.");
+ }
+ }
+ });
+ }
+
+ public void addForcedExportsObligations(final List<AspectBinding> aspects, final Bundle baseBundle) {
+ final String baseBundleName = baseBundle.getSymbolicName();
+ if (baseBundleName == null) {
+ log(IStatus.ERROR, "Cannot handle unnamed base bundle "+baseBundle);
+ } else {
+ schedule(new Runnable () {
+ public void run() {
+ for (AspectBinding aspectBinding : aspects)
+ if (!checkForcedExports(aspectBinding.aspectPlugin, baseBundleName, aspectBinding.forcedExports))
+ stopIllegalBundle(aspectBinding.aspectPlugin);
+ }
+ });
+ }
+ }
+
+ void schedule(Runnable job) {
+ if (isReady()) // became ready since last query?
+ job.run();
+ else
+ synchronized(obligations) {
+ obligations.add(job);
+ }
+ }
+
+ void stopIllegalBundle(String symbolicName) {
+ String msgCore = "stop bundle "+symbolicName+" whose requests for forced exports have been denied";
+ @SuppressWarnings("deprecation")
+ org.osgi.service.packageadmin.PackageAdmin packAdmin = this.packageAdmin;
+ if (packAdmin == null) {
+ log(IStatus.ERROR, "Needing to "+msgCore+" but package admin is not available");
+ } else {
+ @SuppressWarnings("deprecation")
+ Bundle[] bundles = packAdmin.getBundles(symbolicName, null);
+ if (bundles == null)
+ log(IStatus.ERROR, "Needing to "+msgCore+" but bundle cannot be retrieved");
+ else
+ try {
+ bundles[0].stop();
+ } catch (BundleException e) {
+ log(e, "Failed to " + msgCore);
+ }
+ }
+ }
+}
diff --git a/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/internal/osgi/weaving/BaseBundleLoadTrigger.java b/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/internal/osgi/weaving/BaseBundleLoadTrigger.java
index 8dba1fb..c3351d2 100644
--- a/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/internal/osgi/weaving/BaseBundleLoadTrigger.java
+++ b/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/internal/osgi/weaving/BaseBundleLoadTrigger.java
@@ -27,7 +27,6 @@
import org.eclipse.objectteams.internal.osgi.weaving.AspectBinding.BaseBundle;
import org.osgi.framework.Bundle;
import org.osgi.framework.hooks.weaving.WovenClass;
-import org.osgi.service.packageadmin.PackageAdmin;
/**
* Each instance of this class represents the fact that a given base bundle has aspect bindings,
@@ -38,7 +37,7 @@
private AspectBindingRegistry aspectBindingRegistry;
@SuppressWarnings("deprecation")
- private @Nullable PackageAdmin admin;
+ private @Nullable org.osgi.service.packageadmin.PackageAdmin admin;
private String baseBundleName;
@Nullable private BaseBundle baseBundle; // null when representing an aspectBundle with SELF-adapting teams
@@ -46,7 +45,7 @@
private List<AspectBinding> aspectBindings = new ArrayList<>();
public BaseBundleLoadTrigger(String bundleSymbolicName, @Nullable BaseBundle baseBundle, AspectBindingRegistry aspectBindingRegistry,
- @SuppressWarnings("deprecation") @Nullable PackageAdmin admin)
+ @SuppressWarnings("deprecation") @Nullable org.osgi.service.packageadmin.PackageAdmin admin)
{
this.baseBundleName = bundleSymbolicName;
this.baseBundle = baseBundle;
@@ -107,7 +106,7 @@
// (4) try optional steps:
TeamLoader loading = new TeamLoader(deferredTeamClasses, beingDefined, useDynamicWeaver);
- loading.loadTeamsForBase(aspectBundle, aspectBinding, baseClass);
+ loading.loadTeamsForBase(aspectBundle, aspectBinding, baseClass, hook.getAspectPermissionManager());
}
}
diff --git a/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/internal/osgi/weaving/OTWeavingHook.java b/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/internal/osgi/weaving/OTWeavingHook.java
index 8ffe2c7..f5a26a6 100644
--- a/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/internal/osgi/weaving/OTWeavingHook.java
+++ b/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/internal/osgi/weaving/OTWeavingHook.java
@@ -20,7 +20,6 @@
import java.io.IOException;
import java.io.InputStream;
import java.lang.instrument.IllegalClassFormatException;
-import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -37,6 +36,7 @@
import org.eclipse.objectteams.internal.osgi.weaving.AspectBinding.BaseBundle;
import org.eclipse.objectteams.internal.osgi.weaving.AspectBinding.TeamBinding;
import org.eclipse.objectteams.internal.osgi.weaving.Util.ProfileKind;
+import org.eclipse.objectteams.internal.osgi.weaving.AspectPermissionManager;
import org.eclipse.objectteams.otequinox.Constants;
import org.eclipse.objectteams.otequinox.TransformerPlugin;
import org.osgi.framework.Bundle;
@@ -48,8 +48,6 @@
import org.osgi.framework.namespace.PackageNamespace;
import org.osgi.framework.wiring.BundleWiring;
import org.osgi.resource.Wire;
-import org.osgi.service.packageadmin.PackageAdmin;
-
/**
* This class integrates the OT/J weaver into OSGi using the standard API {@link WeavingHook}.
@@ -90,13 +88,17 @@
private @NonNull ASMByteCodeAnalyzer byteCodeAnalyzer = new ASMByteCodeAnalyzer();
+ private AspectPermissionManager permissionManager;
+
/** Call-back once the extension registry is up and running. */
public void activate(BundleContext bundleContext, ServiceReference<IExtensionRegistry> serviceReference) {
loadAspectBindingRegistry(bundleContext, serviceReference);
- TransformerPlugin.getDefault().registerAspectBindingRegistry(this.aspectBindingRegistry);
+ TransformerPlugin activator = TransformerPlugin.getDefault();
+ activator.registerAspectBindingRegistry(this.aspectBindingRegistry);
+ activator.registerAspectPermissionManager(this.permissionManager);
}
- // ====== Aspect Binding: ======
+ // ====== Aspect Bindings & Permissions: ======
@SuppressWarnings("deprecation")
private void loadAspectBindingRegistry(BundleContext context, ServiceReference<IExtensionRegistry> serviceReference) {
@@ -107,12 +109,24 @@
packageAdmin = (org.osgi.service.packageadmin.PackageAdmin)context.getService(ref);
else
log(IStatus.ERROR, "Failed to load PackageAdmin service. Will not be able to handle fragments.");
+
IExtensionRegistry extensionRegistry = context.getService(serviceReference);
- if (extensionRegistry == null)
+ if (extensionRegistry == null) {
log(IStatus.ERROR, "Failed to acquire ExtensionRegistry service, cannot load aspect bindings.");
- else
+ } else {
+ permissionManager = new AspectPermissionManager(context.getBundle(), packageAdmin); // known API
+ permissionManager.loadAspectBindingNegotiators(extensionRegistry);
+
aspectBindingRegistry.loadAspectBindings(extensionRegistry, packageAdmin, this);
+ }
+ }
+
+ public @NonNull AspectPermissionManager getAspectPermissionManager() {
+ AspectPermissionManager manager = this.permissionManager;
+ if (manager == null)
+ throw new NullPointerException("Missing AspectPermissionManager");
+ return manager;
}
// ====== Base Bundle Trip Wires: ======
@@ -121,7 +135,8 @@
* Callback during AspectBindingRegistry#loadAspectBindings():
* Set-up a trip wire to fire when the mentioned base bundle is loaded.
*/
- void setBaseTripWire(@SuppressWarnings("deprecation") @Nullable PackageAdmin packageAdmin, @NonNull String baseBundleId, BaseBundle baseBundle)
+ void setBaseTripWire(@SuppressWarnings("deprecation") @Nullable org.osgi.service.packageadmin.PackageAdmin packageAdmin,
+ @NonNull String baseBundleId, BaseBundle baseBundle)
{
if (!baseTripWires.containsKey(baseBundleId))
baseTripWires.put(baseBundleId, new BaseBundleLoadTrigger(baseBundleId, baseBundle, aspectBindingRegistry, packageAdmin));
@@ -380,5 +395,5 @@
}
return false;
}
-
+
}
diff --git a/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/internal/osgi/weaving/TeamLoader.java b/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/internal/osgi/weaving/TeamLoader.java
index 8805c8d..8f8512a 100644
--- a/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/internal/osgi/weaving/TeamLoader.java
+++ b/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/internal/osgi/weaving/TeamLoader.java
@@ -70,11 +70,22 @@
* Team loading, 1st attempt before the base class is even loaded
* Trying to do these phases: load (now) instantiate/activate (if ready),
*/
- public void loadTeamsForBase(Bundle aspectBundle, AspectBinding aspectBinding, WovenClass baseClass) {
+ public void loadTeamsForBase(Bundle aspectBundle, AspectBinding aspectBinding, WovenClass baseClass, AspectPermissionManager permissionManager) {
@SuppressWarnings("null")@NonNull String className = baseClass.getClassName();
Collection<TeamBinding> teamsForBase = aspectBinding.getTeamsForBase(className);
if (teamsForBase == null)
return; // not done
+
+ // permission checking can be performed now or later, depending on readiness:
+ boolean permissionManagerReady = permissionManager.isReady();
+
+ // ==== check permissions before we start activating:
+ if (permissionManagerReady) { // otherwise we will register pending obligations below.
+ if (permissionManager.checkAspectPermissionDenial(aspectBundle, aspectBinding, teamsForBase))
+ return;
+ }
+
+ List<Team> teamInstances = new ArrayList<>();
for (TeamBinding teamForBase : teamsForBase) {
if (teamForBase.isActivated) continue;
// Load:
@@ -85,12 +96,12 @@
continue;
}
// Try to instantiate & activate, failures are recorded in deferredTeams
- ActivationKind activationKind = teamForBase.activation;
+ ActivationKind activationKind = teamForBase.getActivation();
if (activationKind == ActivationKind.NONE) {
teamForBase = aspectBinding.getOtherTeamToActivate(teamForBase);
if (teamForBase != null) {
if (teamForBase.isActivated) continue;
- activationKind = teamForBase.activation;
+ activationKind = teamForBase.getActivation();
teamClass = teamForBase.loadTeamClass(aspectBundle);
if (teamClass == null) {
log(new ClassNotFoundException("Not found: "+teamForBase.teamName+" in bundle "+aspectBundle.getSymbolicName()), "Failed to load team "+teamForBase);
@@ -102,8 +113,13 @@
}
if (activationKind == ActivationKind.NONE)
continue;
- instantiateAndActivate(aspectBinding, teamForBase, activationKind);
+ Team instance = instantiateAndActivate(aspectBinding, teamForBase, activationKind);
+ if (instance != null)
+ teamInstances.add(instance);
}
+
+ if (!permissionManagerReady)
+ permissionManager.addBaseBundleObligations(teamInstances, teamsForBase, aspectBundle, aspectBinding.baseBundle);
}
public static @Nullable Pair<URL,String> findTeamClassResource(String className, Bundle bundle) {
@@ -153,7 +169,7 @@
/**
* Check if the given team is ready. If so instantiate it and if activationKind requires also activate it.
*/
- void instantiateAndActivate(AspectBinding aspectBinding, TeamBinding team, ActivationKind activationKind)
+ @Nullable Team instantiateAndActivate(AspectBinding aspectBinding, TeamBinding team, ActivationKind activationKind)
{
String teamName = team.teamName;
// don't try to instantiate before all base classes successfully loaded.
@@ -161,7 +177,7 @@
if (!isReadyToLoad(aspectBinding, team, teamName, activationKind)) {
if (this.useDynamicWeaving)
TeamManager.prepareTeamActivation(team.teamClass);
- return;
+ return null;
}
for (TeamBinding equivalent : team.equivalenceSet)
equivalent.isActivated = true;
@@ -194,10 +210,12 @@
// application errors during activation
log(t, "Failed to activate team "+teamName);
}
+ return instance;
} catch (Throwable e) {
// application error during constructor execution?
log(e, "Failed to instantiate team "+teamName);
}
+ return null;
}
private boolean isReadyToLoad(AspectBinding aspectBinding,
diff --git a/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/otequinox/AspectBindingRequestAnswer.java b/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/otequinox/AspectBindingRequestAnswer.java
new file mode 100644
index 0000000..e1be2d3
--- /dev/null
+++ b/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/otequinox/AspectBindingRequestAnswer.java
@@ -0,0 +1,48 @@
+/**********************************************************************
+ * This file is part of "Object Teams Development Tooling"-Software
+ *
+ * Copyright 2009, 2014 Germany and Technical University Berlin, Germany.
+ *
+ * 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:
+ * Technical University Berlin - Initial API and implementation
+ **********************************************************************/
+package org.eclipse.objectteams.otequinox;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+
+/**
+ * Answer for an aspect binding request. See extension point org.eclipse.objectteams.otequinox.aspectBindingNegotiators.
+ *
+ * @author stephan
+ * @since 1.2.6
+ */
+@NonNullByDefault
+public class AspectBindingRequestAnswer
+{
+ /** Should this answer be remembered persistently? */
+ public boolean persistent;
+ /** Should this answer be applied to all subsequent requests? */
+ public boolean allRequests;
+ /** The actual answer. */
+ public AspectPermission permission;
+
+ /**
+ * @param persistent Should this answer be remembered persistently?
+ * @param allRequests Should this answer be applied to all subsequent requests?
+ * @param permission One of DENY, GRANT, UNDEFINED (let others decide).
+ */
+ public AspectBindingRequestAnswer(boolean persistent, boolean allRequests, AspectPermission permission) {
+ this.persistent = persistent;
+ this.allRequests = allRequests;
+ this.permission = permission;
+ }
+
+}
diff --git a/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/otequinox/AspectPermission.java b/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/otequinox/AspectPermission.java
new file mode 100644
index 0000000..a0ad44e
--- /dev/null
+++ b/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/otequinox/AspectPermission.java
@@ -0,0 +1,32 @@
+/**********************************************************************
+ * This file is part of "Object Teams Development Tooling"-Software
+ *
+ * Copyright 2009, 2014 Technical University Berlin, Germany.
+ *
+ * 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:
+ * Technical University Berlin - Initial API and implementation
+ **********************************************************************/
+package org.eclipse.objectteams.otequinox;
+
+/**
+ * Possible values while negotiating aspect access (aspectBinding and forcedExport).
+ * Note that order is relevant in this enum: higher index means higher priority.
+ *
+ * @author stephan
+ * @since 1.2.6
+ */
+public enum AspectPermission {
+ /** Not influencing negotiation between other parties. */
+ UNDEFINED,
+ /** A permission is granted unless someone else denies it. */
+ GRANT,
+ /** A permission is explicitly denied. Cannot be overridden. */
+ DENY;
+}
diff --git a/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/otequinox/IAspectRegistry.java b/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/otequinox/IAspectRegistry.java
index 0fc118a..3735251 100644
--- a/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/otequinox/IAspectRegistry.java
+++ b/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/otequinox/IAspectRegistry.java
@@ -63,5 +63,5 @@
* @param symbolicName
* @return
*/
- public boolean isDeniedAspectPlugin(String symbolicName);
+ public boolean isDeniedAspectPlugin(@NonNull String symbolicName);
}
\ No newline at end of file
diff --git a/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/otequinox/IAspectRequestNegotiator.java b/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/otequinox/IAspectRequestNegotiator.java
new file mode 100644
index 0000000..04024a1
--- /dev/null
+++ b/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/otequinox/IAspectRequestNegotiator.java
@@ -0,0 +1,49 @@
+/**********************************************************************
+ * This file is part of "Object Teams Development Tooling"-Software
+ *
+ * Copyright 2009, 2014 Germany and Technical University Berlin, Germany.
+ *
+ * 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:
+ * Technical University Berlin - Initial API and implementation
+ **********************************************************************/
+package org.eclipse.objectteams.otequinox;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * Interface for extenders wishing to participate in negotiation about aspect binding requests
+ * including forced exports.
+ *
+ * @author stephan
+ * @since 1.2.6
+ */
+@NonNullByDefault
+public interface IAspectRequestNegotiator {
+
+ /**
+ * Check whether a request for forced exports should be granted.
+ * @param aspectBundleSymbolicName the aspect issuing the request
+ * @param baseBundleSymbolicName the affected base bundle
+ * @param basePackage the affected base package
+ * @param previousNegotiation the result of negotations up-to this point
+ * @return a structure holding the answer
+ */
+ AspectBindingRequestAnswer checkForcedExport(String aspectBundleSymbolicName, String baseBundleSymbolicName, String basePackage, AspectPermission previousNegotiation);
+
+ /**
+ * Check whether a request for an aspect binding should be granted.
+ * @param aspectBundleSymbolicName the aspect issuing the request
+ * @param baseBundleSymbolicName the affected base bundle
+ * @param teamClass an affecting team class involved in this aspect binding
+ * @param previousNegotiation the result of negotations up-to this point
+ * @return a structure holding the answer
+ */
+ AspectBindingRequestAnswer checkAspectBinding(String aspectBundleSymbolicName, String baseBundleSymbolicName, String teamClass, AspectPermission previousNegotiation);
+}
diff --git a/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/otequinox/TransformerPlugin.java b/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/otequinox/TransformerPlugin.java
index 690f558..d3c8aa6 100644
--- a/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/otequinox/TransformerPlugin.java
+++ b/plugins/org.eclipse.objectteams.otequinox/src/org/eclipse/objectteams/otequinox/TransformerPlugin.java
@@ -27,6 +27,7 @@
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.objectteams.internal.osgi.weaving.AspectBinding;
import org.eclipse.objectteams.internal.osgi.weaving.AspectBindingRegistry;
+import org.eclipse.objectteams.internal.osgi.weaving.AspectPermissionManager;
import org.eclipse.objectteams.internal.osgi.weaving.OTWeavingHook;
import org.eclipse.objectteams.otre.ClassLoaderAccess;
import org.objectteams.Team;
@@ -54,6 +55,7 @@
private AspectBindingRegistry aspectBindingRegistry;
private List<Team> teamInstances = new ArrayList<>();
+ private AspectPermissionManager aspectPermissionManager;
/*
* (non-Javadoc)
@@ -192,6 +194,10 @@
this.aspectBindingRegistry = aspectBindingRegistry;
}
+ public void registerAspectPermissionManager(AspectPermissionManager permissionManager) {
+ this.aspectPermissionManager = permissionManager;
+ }
+
/**
* public API:
* {@link IAspectRegistry#getAdaptingAspectPlugins(Bundle)}
@@ -222,8 +228,7 @@
@Override
public boolean isOTDT() {
- // TODO Auto-generated method stub
- return false;
+ return this.aspectBindingRegistry.isOTDT();
}
@Override
@@ -233,8 +238,7 @@
@Override
public String[] getAdaptedBasePlugins(Bundle aspectBundle) {
- // TODO Auto-generated method stub
- return null;
+ return this.aspectBindingRegistry.getAdaptedBasePlugins(aspectBundle);
}
@Override
@@ -244,8 +248,7 @@
}
@Override
- public boolean isDeniedAspectPlugin(String symbolicName) {
- // TODO Auto-generated method stub
- return false;
+ public boolean isDeniedAspectPlugin(@NonNull String symbolicName) {
+ return aspectPermissionManager.isDeniedAspectPlugin(symbolicName);
}
}
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 2d068bd..cf22779 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
@@ -160,7 +160,7 @@
private AbstractBoundClass enclosingClass;
private Map<String, Method> methods;
private Map<String, Field> fields;
- private Map<AbstractBoundClass, Object> subclasses;
+ protected Map<AbstractBoundClass, Object> subclasses;
// Is the java class, that was represented by the AbstractBoundClass
// already loaded by a class loader or not
diff --git a/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/AbstractTeam.java b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/AbstractTeam.java
index 0de7c51..6f959e7 100644
--- a/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/AbstractTeam.java
+++ b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/AbstractTeam.java
@@ -16,12 +16,18 @@
**********************************************************************/
package org.eclipse.objectteams.otredyn.bytecode;
+import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import org.eclipse.objectteams.otredyn.runtime.IBinding;
+import org.eclipse.objectteams.otredyn.runtime.IBoundClass;
import org.eclipse.objectteams.otredyn.runtime.IBoundTeam;
+import org.eclipse.objectteams.otredyn.runtime.IClassIdentifierProvider;
+import org.eclipse.objectteams.otredyn.runtime.IClassRepository;
/**
* Represents a team class and stores the bindings this team has got.
@@ -77,4 +83,33 @@
public int getHighestAccessId() {
return highestAccessId;
}
+
+ /** Answer known tsub-versions of the given role. */
+ private List<String> getTSubRoles(String simpleRoleName) {
+ List<String> result = new ArrayList<String>();
+ for (AbstractBoundClass subTeam : this.subclasses.keySet()) {
+ if (!subTeam.isAnonymous())
+ result.add(subTeam.getName()+'$'+simpleRoleName);
+ }
+ return result;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public List<IBoundClass> getTSubsOfThis(IClassRepository classRepository, IClassIdentifierProvider idProvider) {
+ String name = this.getName();
+ int dollar = name.lastIndexOf('$');
+ if (dollar == -1)
+ return Collections.emptyList();
+ String teamName = name.substring(0, dollar);
+ // FIXME: use the idProvider, but: we don't yet have that team class :(
+ IBoundTeam myTeam = classRepository.getTeam(teamName.replace('/', '.'), teamName.replace('.', '/'), this.loader);
+ List<String> tsubRoleNames = ((AbstractTeam)myTeam).getTSubRoles(name.substring(dollar+1));
+ List<IBoundClass> tsubBases = new ArrayList<IBoundClass>();
+ for (String tsubRoleName : tsubRoleNames)
+ tsubBases.add(classRepository.getBoundClass(tsubRoleName.replace('/', '.'), tsubRoleName.replace('.', '/'), this.loader));
+ return tsubBases;
+ }
}
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/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/transformer/jplis/otreAgent.java b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/transformer/jplis/otreAgent.java
index 48ec34d..61f5d28 100644
--- a/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/transformer/jplis/otreAgent.java
+++ b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/transformer/jplis/otreAgent.java
@@ -17,6 +17,10 @@
import java.lang.instrument.Instrumentation;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.commons.AdviceAdapter;
+import org.objectweb.asm.tree.InsnNode;
+
/**
* @author Christine Hundt
@@ -29,11 +33,17 @@
public static void premain(String options, Instrumentation inst) {
instCopy = inst;
-
+ checkASM();
otTransformer = new ObjectTeamsTransformer();
instCopy.addTransformer(otTransformer);
}
+ private static void checkASM() {
+ ClassVisitor.class.getName(); // asm
+ AdviceAdapter.class.getName(); // asm.commons
+ InsnNode.class.getName(); // asm.tree
+ }
+
public static Instrumentation getInstrumentation() {
return instCopy;
}
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 3e7ee52..c09aaff 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
@@ -15,6 +15,8 @@
**********************************************************************/
package org.eclipse.objectteams.otredyn.runtime;
+import java.util.List;
+
/**
* Interface through which the {@link TeamManager} reaches into the OTREDyn.
* Representation of a class that participates in a binding.
@@ -81,4 +83,9 @@
*/
void addWiringTask(ISubclassWiringTask subclassWiringTask);
+ /**
+ * Assuming this instance represents a role, answer all tsub roles in any teams known at this point.
+ */
+ List<IBoundClass> getTSubsOfThis(IClassRepository classRepository, IClassIdentifierProvider idProvider);
+
}
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 fb69891..df63479 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
@@ -115,7 +115,16 @@
* @return
*/
public static int getMemberId(int accessId, Class<? extends ITeam> teamClass) {
- Integer id = accessIdMap.get(teamClass).get(accessId);
+ 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?
+ 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;
}
@@ -172,25 +181,33 @@
String boundClassIdentifier = provider.getBoundClassIdentifier(teamClass, boundClassName);
// FIXME(SH): the following may need adaptation for OT/Equinox or other multi-classloader settings:
IBoundClass boundClass = classRepository.getBoundClass(boundClassName.replace('/', '.'), boundClassIdentifier, teamClass.getClassLoader());
- // FIXME(SH): if boundClass is a role we need to find tsub roles, too!
switch (binding.getType()) {
case CALLIN_BINDING:
- IMethod method = boundClass.getMethod(binding.getMemberName(), binding.getMemberSignature(), binding.getBaseFlags(), binding.isHandleCovariantReturn());
- int joinpointId = getJoinpointId(boundClass
- .getMethodIdentifier(method));
- synchronized (method) {
- changeTeamsForJoinpoint(t, binding.getPerTeamId(), joinpointId, stateChange);
- List<Integer> subJoinpoints = joinpointToSubJoinpoints.get(joinpointId);
- if (subJoinpoints != null)
- for (Integer subJoinpoint : subJoinpoints)
- changeTeamsForJoinpoint(t, binding.getPerTeamId(), subJoinpoint, stateChange);
- }
- boundClass.handleAddingOfBinding(binding); // TODO: do we want/need to group all bindings into one action?
+ handleBindingForBase(t, stateChange, binding, boundClass, provider);
break;
- //$CASES-OMITTED$ // TODO: this marker-comment has not effect?
+ default:
+ // no further action for *ACCESS bindings
}
}
}
+
+ private void handleBindingForBase(ITeam t, ITeamManager.TeamStateChange stateChange, IBinding binding, IBoundClass boundClass, IClassIdentifierProvider provider) {
+ IMethod method = boundClass.getMethod(binding.getMemberName(), binding.getMemberSignature(), binding.getBaseFlags(), binding.isHandleCovariantReturn());
+ int joinpointId = getJoinpointId(boundClass
+ .getMethodIdentifier(method));
+ synchronized (method) {
+ changeTeamsForJoinpoint(t, binding.getPerTeamId(), joinpointId, stateChange);
+ List<Integer> subJoinpoints = joinpointToSubJoinpoints.get(joinpointId);
+ if (subJoinpoints != null)
+ for (Integer subJoinpoint : subJoinpoints)
+ changeTeamsForJoinpoint(t, binding.getPerTeamId(), subJoinpoint, stateChange);
+ }
+ boundClass.handleAddingOfBinding(binding); // TODO: do we want/need to group all bindings into one action?
+
+ for (IBoundClass tsubBase : boundClass.getTSubsOfThis(classRepository, provider)) {
+ handleBindingForBase(t, stateChange, binding, tsubBase, provider);
+ }
+ }
/**
* When a team is about to be activated propagate its bindings to all base classes,
@@ -208,13 +225,19 @@
String boundClassIdentifier = provider.getBoundClassIdentifier(teamClass, boundClassName);
// FIXME(SH): the following may need adaptation for OT/Equinox or other multi-classloader settings:
IBoundClass boundClass = classRepository.getBoundClass(boundClassName.replace('/', '.'), boundClassIdentifier, teamClass.getClassLoader());
- // FIXME(SH): if boundClass is a role we need to find tsub roles, too!
switch (binding.getType()) {
case CALLIN_BINDING:
- boundClass.handleAddingOfBinding(binding);
+ prepareBindingForBase(binding, boundClass, provider);
break;
+ default: // no further action for *ACCESS bindings
}
- }
+ }
+ }
+
+ private static void prepareBindingForBase(IBinding binding, IBoundClass boundClass, IClassIdentifierProvider idProvider) {
+ boundClass.handleAddingOfBinding(binding);
+ for (IBoundClass tsubBase : boundClass.getTSubsOfThis(classRepository, idProvider))
+ prepareBindingForBase(binding, tsubBase, idProvider);
}
public static void handleTeamLoaded(Class<? extends ITeam> teamClass) {
@@ -250,6 +273,7 @@
}
boundClass.handleAddingOfBinding(binding);
break;
+ default: // no action for CALLIN_BINDING here
}
}
}
@@ -355,8 +379,11 @@
teams.add(d, srcTeams.get(s));
callinIds.add(0, srcCallins.get(s));
}
- _teams.set(destJoinpointId, teams);
- _callinIds.set(destJoinpointId, callinIds);
+ // transitively pass the new information down the tree of subJoinpoints:
+ List<Integer> destDests = joinpointToSubJoinpoints.get(destJoinpointId);
+ if (destDests != null && !destDests.isEmpty())
+ for (Integer destDest : destDests)
+ applyJoinpointMerge(destJoinpointId, destDest);
}
/**
diff --git a/testplugins/org.eclipse.objectteams.otdt.tests/otjld/org/eclipse/objectteams/otdt/tests/otjld/AbstractOTJLDNullAnnotationTest.java b/testplugins/org.eclipse.objectteams.otdt.tests/otjld/org/eclipse/objectteams/otdt/tests/otjld/AbstractOTJLDNullAnnotationTest.java
new file mode 100644
index 0000000..6c45705
--- /dev/null
+++ b/testplugins/org.eclipse.objectteams.otdt.tests/otjld/org/eclipse/objectteams/otdt/tests/otjld/AbstractOTJLDNullAnnotationTest.java
@@ -0,0 +1,381 @@
+/**********************************************************************
+ * This file is part of "Object Teams Development Tooling"-Software
+ *
+ * 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.otdt.tests.otjld;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.util.Map;
+
+import org.eclipse.core.runtime.FileLocator;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.jdt.core.tests.compiler.regression.AbstractNullAnnotationTest;
+import org.eclipse.jdt.core.tests.compiler.regression.InMemoryNameEnvironment;
+import org.eclipse.jdt.core.tests.compiler.regression.RegressionTestSetup;
+import org.eclipse.jdt.core.tests.compiler.regression.Requestor;
+import org.eclipse.jdt.core.tests.util.CompilerTestSetup;
+import org.eclipse.jdt.core.tests.util.TestVerifier;
+import org.eclipse.jdt.core.tests.util.Util;
+import org.eclipse.jdt.internal.compiler.Compiler;
+import org.eclipse.jdt.internal.compiler.batch.FileSystem;
+import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
+import org.eclipse.jdt.internal.compiler.env.INameEnvironment;
+import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
+import org.eclipse.objectteams.otdt.core.ext.WeavingScheme;
+import org.eclipse.objectteams.otdt.tests.ClasspathUtil;
+
+/**
+ * Common super class for OT-tests with null annotations (mostly copied from AbstractOTJLDTest).
+ */
+public class AbstractOTJLDNullAnnotationTest extends AbstractNullAnnotationTest {
+
+ /** a test verifier that allows reusing a running vm even in the presence of (constant) vm arguments. */
+ protected class OTTestVerifier extends TestVerifier {
+ protected OTTestVerifier(boolean reuseVM) {
+ super(reuseVM);
+ this.fVMArguments = getOTVMArgs();
+ }
+ @Override
+ public boolean verifyClassFiles(String sourceFilePath, String className, String expectedOutputString, String expectedErrorStringStart,
+ String[] classpaths, String[] programArguments, String[] vmArguments) {
+ vmArguments = mergeArgs(vmArguments);
+ return super.verifyClassFiles(sourceFilePath, className, expectedOutputString, expectedErrorStringStart, classpaths, programArguments, vmArguments);
+ }
+ @Override
+ public boolean vmArgsEqual(String[] newArgs) {
+ return super.vmArgsEqual(mergeArgs(newArgs));
+ }
+ protected String[] mergeArgs(String[] newArgs) {
+ String[] OTVMArgs = getOTVMArgs();
+ if (newArgs == null)
+ return OTVMArgs;
+ else {
+ int l1 = OTVMArgs.length;
+ int l2 = newArgs.length;
+ String[] result = new String[l1+l2];
+ System.arraycopy(OTVMArgs, 0, result, 0, l1);
+ System.arraycopy(newArgs, 0, result, l1, l2);
+ return result;
+ }
+ }
+ }
+
+ private static String getOTDTJarPath(String jarName) {
+ return ClasspathUtil.OTDT_PATH + File.separator + "lib" + File.separator + jarName + ".jar";
+ }
+
+ public String[] getOTVMArgs() {
+ String OTRE_MIN_JAR_PATH, OTAGENT_JAR_PATH;
+ OTRE_MIN_JAR_PATH = getOTDTJarPath("otre_min");
+ switch (this.weavingScheme) {
+ case OTDRE:
+ OTAGENT_JAR_PATH = getOTDTJarPath("otredyn_agent");
+ break;
+ case OTRE:
+ OTAGENT_JAR_PATH = getOTDTJarPath("otre_agent");
+ break;
+ default:
+ throw new IllegalStateException("Unsupported weavingScheme "+this.weavingScheme);
+ }
+ return new String[] {
+ "-javaagent:"+OTAGENT_JAR_PATH,
+ "-Xbootclasspath/a:"+OTRE_MIN_JAR_PATH,
+ "-Dot.dump=1"
+ };
+ }
+
+ public static boolean IS_JRE_8;
+ static {
+ String javaVersion = System.getProperty("java.specification.version");
+ IS_JRE_8 = "1.8".equals(javaVersion);
+ }
+ protected String foreach(String elemType) {
+ return (IS_JRE_8 && this.complianceLevel < ClassFileConstants.JDK1_8)
+ ? "public void forEach(java.util.function.Consumer<? super "+elemType+"> element) {}\n"
+ : "";
+ }
+ protected String spliterator(String elemType) {
+ return (IS_JRE_8 && this.complianceLevel < ClassFileConstants.JDK1_8)
+ ? "public java.util.Spliterator<"+elemType+"> spliterator() { return null; }\n"
+ : "";
+ }
+ protected String spliteratorCallout() {
+ return (IS_JRE_8 && this.complianceLevel < ClassFileConstants.JDK1_8) ? "spliterator -> spliterator;\n" : "";
+ }
+ // ===
+
+ protected static final JavacTestOptions DEFAULT_TEST_OPTIONS = new JavacTestOptions();
+
+ // shall compiler output be matched exactly or using some matching?
+ boolean errorMatching = false;
+
+ // each subarray defines a set of classes to be compiled together:
+ protected String[][] compileOrder;
+
+ protected WeavingScheme weavingScheme = WeavingScheme.OTRE;
+
+ public AbstractOTJLDNullAnnotationTest(String name) {
+ super(name);
+ }
+
+ /** Add otre/otdre and (bcel or asm) to the class path. */
+ @Override
+ protected String[] getDefaultClassPaths() {
+ String[] defaults = super.getDefaultClassPaths();
+ int len = defaults.length;
+ IPath[] bytecodeLibJarPath = ClasspathUtil.getWeaverPaths(this.weavingScheme);
+ int len2 = bytecodeLibJarPath.length;
+ System.arraycopy(defaults, 0, defaults=new String[len+1+len2], 0, len);
+ defaults[len] = new Path(ClasspathUtil.getOTREPath(this.weavingScheme)).toString();
+ for (int i=0; i<len2; i++)
+ defaults[len+1+i] = bytecodeLibJarPath[i].toString();
+ return defaults;
+ }
+
+ @Override
+ public void initialize(CompilerTestSetup setUp) {
+ super.initialize(setUp);
+ if ("dynamic".equals(System.getProperty("ot.weaving"))
+ ||"otdre".equals(System.getProperty("test.ot.weaving")))
+ weavingScheme = WeavingScheme.OTDRE;
+ if (setUp instanceof RegressionTestSetup) {
+ RegressionTestSetup regressionSetTup = (RegressionTestSetup) setUp;
+ if (!(regressionSetTup.verifier instanceof OTTestVerifier)) {
+ // overwrite plain TestVerifier:
+ regressionSetTup.verifier = this.verifier = new OTTestVerifier(true/*reuseVM*/);
+ }
+ }
+ }
+
+ @Override
+ protected TestVerifier getTestVerifier(boolean reuseVM) {
+ return new OTTestVerifier(reuseVM);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ this.compileOrder = null;
+ super.tearDown();
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ protected Map getCompilerOptions() {
+ Map options = super.getCompilerOptions();
+ options.put(CompilerOptions.OPTION_ReportUnnecessaryElse, CompilerOptions.IGNORE);
+ options.put(CompilerOptions.OPTION_ReportSyntheticAccessEmulation, CompilerOptions.IGNORE);
+ options.put(CompilerOptions.OPTION_ReportUnusedWarningToken, CompilerOptions.ERROR);
+ options.put(CompilerOptions.OPTION_WeavingScheme, this.weavingScheme.toString());
+ // FIXME: consider setting to warning:
+ options.put(CompilerOptions.OPTION_ReportOtreWeavingIntoJava8, CompilerOptions.IGNORE);
+ return options;
+ }
+
+ protected void runNegativeTestMatching(String[] testFiles, String expectedCompilerLog) {
+ this.errorMatching = true;
+ runTest(
+ // test directory preparation
+ true /* flush output directory */,
+ testFiles /* test files */,
+ // compiler options
+ null /* no class libraries */,
+ null /* no custom options */,
+ false /* do not perform statements recovery */,
+ null /* no custom requestor */,
+ // compiler results
+ true /* expecting compiler errors */,
+ expectedCompilerLog /* expected compiler log */,
+ // runtime options
+ false /* do not force execution */,
+ null /* no vm arguments */,
+ // runtime results
+ null /* do not check output string */,
+ null /* do not check error string */,
+ // javac options
+ DEFAULT_TEST_OPTIONS /* javac test options */);
+ this.errorMatching = false;
+ }
+
+ // inaccessible helper from super
+ void logTestFiles(boolean logTitle, String[] testFiles) {
+ if (logTitle) {
+ logTestTitle();
+ }
+ for (int i = 0; i < testFiles.length; i += 2) {
+ System.out.print(testFiles[i]);
+ System.out.println(" ["); //$NON-NLS-1$
+ System.out.println(testFiles[i + 1]);
+ System.out.println("]"); //$NON-NLS-1$
+ }
+ }
+ // inaccessible helper from super
+ void logTestTitle() {
+ System.out.println(getClass().getName() + '#' + getName());
+ }
+
+ // support explicit compile order in several steps:
+ @Override
+ protected void compileTestFiles(Compiler batchCompiler, String[] testFiles) {
+ if (this.compileOrder == null) {
+ super.compileTestFiles(batchCompiler, testFiles);
+ } else {
+ for (String[] bunch : this.compileOrder) {
+ String[] bunchFiles = new String[bunch.length * 2];
+ int b = 0;
+ for (int i = 0; i < bunch.length; i++) {
+ int b0 = b;
+ for (int j = 0; j < testFiles.length; j+=2) {
+ if (bunch[i].equals(testFiles[j])) {
+ bunchFiles[b++] = testFiles[j];
+ bunchFiles[b++] = testFiles[j+1];
+ }
+ }
+ assertTrue("Unmatched filename: "+bunch[i], b == b0+2);
+ }
+ super.compileTestFiles(batchCompiler, bunchFiles);
+ batchCompiler.lookupEnvironment.nameEnvironment.cleanup(); // don't use chached info from previous runs
+ }
+ }
+ }
+
+ protected INameEnvironment getNameEnvironment(final String[] testFiles, String[] classPaths) {
+ this.classpaths = classPaths == null ? getDefaultClassPaths() : classPaths;
+ // make cleanup weaker:
+ return new InMemoryNameEnvironment(testFiles, getClassLibs(false)) {
+ @Override
+ public void cleanup() {
+ for (int i = 0, max = this.classLibs.length; i < max; i++)
+ if (this.classLibs[i] instanceof FileSystem)
+ ((FileSystem) this.classLibs[i]).softReset();
+ }
+ };
+ }
+
+ /** Additional entry for tests expecting a compiler warning and don't run. */
+ protected void runTestExpectingWarnings(String[] files, String expectedWarnings) {
+ Map options = getCompilerOptions();
+ runConformTest(
+ // test directory preparation
+ true /* flush output directory */,
+ files,
+ // compiler options
+ null /* no class libraries */,
+ options /* custom options - happen to be the default not changed by the test suite */,
+ // compiler results
+ expectedWarnings,
+ // runtime results
+ null /* do not check output string */,
+ null /* do not check error string */,
+ // javac options
+ JavacTestOptions.Excuse.EclipseHasSomeMoreWarnings /* javac test options */);
+ }
+
+ /** Additional entry for tests expecting a compiler warning and don't run. */
+ protected void runTestExpectingWarnings(String[] files, String expectedWarnings, Map options) {
+ runConformTest(
+ // test directory preparation
+ true /* flush output directory */,
+ files,
+ // compiler options
+ null /* no class libraries */,
+ options /* custom options - happen to be the default not changed by the test suite */,
+ // compiler results
+ expectedWarnings,
+ // runtime results
+ null /* do not check output string */,
+ null /* do not check error string */,
+ // javac options
+ JavacTestOptions.Excuse.EclipseHasSomeMoreWarnings /* javac test options */);
+ }
+
+ /** Additional entry for tests expecting a compiler warning and don't run. */
+ protected void runTestExpectingWarnings(String[] files, String expectedWarnings, boolean flushOutputDirectory) {
+ Map options = getCompilerOptions();
+ runConformTest(
+ // test directory preparation
+ flushOutputDirectory,
+ files,
+ // compiler options
+ null /* no class libraries */,
+ options /* custom options - happen to be the default not changed by the test suite */,
+ // compiler results
+ expectedWarnings,
+ // runtime results
+ null /* do not check output string */,
+ null /* do not check error string */,
+ // javac options
+ JavacTestOptions.Excuse.EclipseHasSomeMoreWarnings /* javac test options */);
+ }
+ /** Additional entry for tests expecting a compiler warning and run. */
+ protected void runTestExpectingWarnings(String[] files, String expectedWarnings, String expectedOutput) {
+ Map options = getCompilerOptions();
+ runConformTest(
+ // test directory preparation
+ true/*flushOutputDirectory*/,
+ files,
+ // compiler options
+ null /* no class libraries */,
+ options /* custom options - happen to be the default not changed by the test suite */,
+ // compiler results
+ expectedWarnings,
+ // runtime results
+ expectedOutput,
+ null /* do not check error string */,
+ // javac options
+ JavacTestOptions.Excuse.EclipseHasSomeMoreWarnings /* javac test options */);
+ }
+
+ protected void myWriteFiles(String[] testFiles) {
+ // force the directory to comply with the infrastructure from AbstractRegressionTest:
+ String testName = null;
+ try {
+ testName = getName();
+ setName("regression");
+ // Write files in dir
+ writeFiles(testFiles);
+ } finally {
+ setName(testName);
+ }
+ }
+
+ protected String getTestResourcePath(String filename) {
+ try
+ {
+ URL platformURL = Platform
+ .getBundle("org.eclipse.objectteams.otdt.tests")
+ .getEntry("/testresources/"+filename);
+ return new File(FileLocator.toFileURL(platformURL).getFile())
+ .getAbsolutePath();
+ }
+ catch (IOException ex)
+ {
+ ex.printStackTrace();
+ }
+ return null;
+ }
+ /** Create a vm arg for team activation and reset the verifier (avoid reuse of a running VM). */
+ protected String[] getTeamActivationVMArgs(String relativeFilePath) {
+ if (this.verifier != null)
+ this.verifier.shutDown();
+ this.verifier = getTestVerifier(false);
+ this.createdVerifier = true;
+ return new String[] {
+ "-Dot.teamconfig="+OUTPUT_DIR+'/'+relativeFilePath
+ };
+ }
+}
diff --git a/testplugins/org.eclipse.objectteams.otdt.tests/otjld/org/eclipse/objectteams/otdt/tests/otjld/AllTests.java b/testplugins/org.eclipse.objectteams.otdt.tests/otjld/org/eclipse/objectteams/otdt/tests/otjld/AllTests.java
index 9bfe2c8..c4c8328 100644
--- a/testplugins/org.eclipse.objectteams.otdt.tests/otjld/org/eclipse/objectteams/otdt/tests/otjld/AllTests.java
+++ b/testplugins/org.eclipse.objectteams.otdt.tests/otjld/org/eclipse/objectteams/otdt/tests/otjld/AllTests.java
@@ -39,6 +39,7 @@
import org.eclipse.objectteams.otdt.tests.otjld.other.Java8;
import org.eclipse.objectteams.otdt.tests.otjld.other.Misc;
import org.eclipse.objectteams.otdt.tests.otjld.other.Modifiers;
+import org.eclipse.objectteams.otdt.tests.otjld.other.OTNullTypeAnnotationTest;
import org.eclipse.objectteams.otdt.tests.otjld.regression.CompilationOrder;
import org.eclipse.objectteams.otdt.tests.otjld.regression.ComplexStructures;
import org.eclipse.objectteams.otdt.tests.otjld.regression.DevelopmentExamples;
@@ -135,6 +136,7 @@
/*A.1*/addComplianceSuite(suite, Java5.testClass());
/*A.2*/addComplianceSuite(suite, Java7.testClass(), AbstractCompilerTest.F_1_7);
/*---*/addComplianceSuite(suite, Java8.testClass(), AbstractCompilerTest.F_1_8);
+ /*---*/addComplianceSuite(suite, OTNullTypeAnnotationTest.testClass(), AbstractCompilerTest.F_1_8);
/*0.m*/addComplianceSuite(suite, Misc.testClass());
// regression:
diff --git a/testplugins/org.eclipse.objectteams.otdt.tests/otjld/org/eclipse/objectteams/otdt/tests/otjld/other/Java5.java b/testplugins/org.eclipse.objectteams.otdt.tests/otjld/org/eclipse/objectteams/otdt/tests/otjld/other/Java5.java
index 9c6df41..993dd47 100644
--- a/testplugins/org.eclipse.objectteams.otdt.tests/otjld/org/eclipse/objectteams/otdt/tests/otjld/other/Java5.java
+++ b/testplugins/org.eclipse.objectteams.otdt.tests/otjld/org/eclipse/objectteams/otdt/tests/otjld/other/Java5.java
@@ -1322,22 +1322,22 @@
public void testA12_genericRoleFeature16a() {
runConformTest(
new String[] {
- "TeamA12grf16_3.java",
- "public team class TeamA12grf16_3 extends TeamA12grf16_2<String> {\n" +
+ "TeamA12grf16a_3.java",
+ "public team class TeamA12grf16a_3 extends TeamA12grf16a_2<String> {\n" +
" @Override\n" +
- " public class R playedBy TA12grf16 {\n" +
+ " public class R playedBy TA12grf16a {\n" +
" test <- before test;\n" +
" }\n" +
" public static void main(String... args) {\n" +
- " new TeamA12grf16_3().activate();\n" +
- " System.out.print(new TA12grf16().test(\"K\"));" +
+ " new TeamA12grf16a_3().activate();\n" +
+ " System.out.print(new TA12grf16a().test(\"K\"));" +
" }\n" +
"}\n",
- "TeamA12grf16_2.java",
- "public team class TeamA12grf16_2<U> extends TeamA12grf16_1<U> {\n" +
+ "TeamA12grf16a_2.java",
+ "public team class TeamA12grf16a_2<U> extends TeamA12grf16a_1<U> {\n" +
"}\n",
- "TeamA12grf16_1.java",
- "public team class TeamA12grf16_1<U> {\n" +
+ "TeamA12grf16a_1.java",
+ "public team class TeamA12grf16a_1<U> {\n" +
" public class R {\n" +
" private U test(U u) {\n" +
" System.out.print(\"O\");\n" +
@@ -1345,8 +1345,8 @@
" }\n" +
" }\n" +
"}\n",
- "TA12grf16.java",
- "public class TA12grf16 {\n" +
+ "TA12grf16a.java",
+ "public class TA12grf16a {\n" +
" protected String test(String u){ return u;}" +
"}\n"
},
@@ -1359,40 +1359,40 @@
public void testA12_genericRoleFeature16f() {
runNegativeTest(
new String[] {
- "TeamA12grf16_3.java",
- "public team class TeamA12grf16_3 extends TeamA12grf16_2<String> {\n" +
+ "TeamA12grf16f_3.java",
+ "public team class TeamA12grf16f_3 extends TeamA12grf16f_2<String> {\n" +
" public static void main(String... args) {\n" +
- " new TeamA12grf16_3().activate();\n" +
- " System.out.print(new TA12grf16().test(\"K\"));" +
+ " new TeamA12grf16f_3().activate();\n" +
+ " System.out.print(new TA12grf16f().test(\"K\"));" +
" }\n" +
"}\n",
- "TeamA12grf16_2.java",
- "public team class TeamA12grf16_2<U> extends TeamA12grf16_1<U> {\n" +
+ "TeamA12grf16f_2.java",
+ "public team class TeamA12grf16f_2<U> extends TeamA12grf16f_1<U> {\n" +
" @Override\n" +
- " public class R playedBy TA12grf16 {\n" +
+ " public class R playedBy TA12grf16f {\n" +
" test <- before test;\n" +
" }\n" +
"}\n",
- "TeamA12grf16_1.java",
- "public team class TeamA12grf16_1<U> {\n" +
+ "TeamA12grf16f_1.java",
+ "public team class TeamA12grf16f_1<U> {\n" +
" public class R {\n" +
" private U test(U u) {\n" +
" return u;\n" +
" }\n" +
" }\n" +
"}\n",
- "TA12grf16.java",
- "public class TA12grf16 {\n" +
+ "TA12grf16f.java",
+ "public class TA12grf16f {\n" +
" protected String test(String u){ return u;}" +
"}\n"
},
"----------\n" +
- "1. ERROR in TeamA12grf16_2.java (at line 4)\n" +
+ "1. ERROR in TeamA12grf16f_2.java (at line 4)\n" +
" test <- before test;\n" +
" ^^^^\n" +
(this.weavingScheme == WeavingScheme.OTRE
? "Type mismatch: cannot convert from String to U\n"
- : "The method test(U) in the type TeamA12grf16_2<U>.R is not applicable for the arguments (String)\n"
+ : "The method test(U) in the type TeamA12grf16f_2<U>.R is not applicable for the arguments (String)\n"
) +
"----------\n");
}
@@ -1457,7 +1457,7 @@
"OK",
null,
false/*shouldFlushOutputDirectory*/,
- null);
+ new String[] {"-DA12grf18_2=2"}); // force new vm launch to accept the new class version
}
// a role does not use the type parameter of its enclosing team, has callin binding, copied as phantom role
@@ -1496,7 +1496,7 @@
"OK",
null,
false/*shouldFlushOutputDirectory*/,
- null);
+ new String[] {"-DA12grf19_2=2"}); // force new vm launch to accept the new class version
}
// a parameter of a callout binding requires autoboxing - explicit mapping
diff --git a/testplugins/org.eclipse.objectteams.otdt.tests/otjld/org/eclipse/objectteams/otdt/tests/otjld/other/Java8.java b/testplugins/org.eclipse.objectteams.otdt.tests/otjld/org/eclipse/objectteams/otdt/tests/otjld/other/Java8.java
index 8a43d15..ab5cd0e 100644
--- a/testplugins/org.eclipse.objectteams.otdt.tests/otjld/org/eclipse/objectteams/otdt/tests/otjld/other/Java8.java
+++ b/testplugins/org.eclipse.objectteams.otdt.tests/otjld/org/eclipse/objectteams/otdt/tests/otjld/other/Java8.java
@@ -264,7 +264,7 @@
" public class R {}\n" +
"}\n",
"C1.java",
- "public abstract class C1 {\n" +
+ "public abstract team class C1 {\n" +
" final T1 t = new T1();\n" +
" abstract void test1(final C1 a, R<@a.t> c);\n" +
" abstract void test2(final Object o, C2<java.lang.@p1.Marker Object> c);\n" + // bug, should be java.lang. @p1.Marker Object
diff --git a/testplugins/org.eclipse.objectteams.otdt.tests/otjld/org/eclipse/objectteams/otdt/tests/otjld/other/OTNullTypeAnnotationTest.java b/testplugins/org.eclipse.objectteams.otdt.tests/otjld/org/eclipse/objectteams/otdt/tests/otjld/other/OTNullTypeAnnotationTest.java
new file mode 100644
index 0000000..b4dd521
--- /dev/null
+++ b/testplugins/org.eclipse.objectteams.otdt.tests/otjld/org/eclipse/objectteams/otdt/tests/otjld/other/OTNullTypeAnnotationTest.java
@@ -0,0 +1,188 @@
+/**********************************************************************
+ * This file is part of "Object Teams Development Tooling"-Software
+ *
+ * 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.otdt.tests.otjld.other;
+
+import junit.framework.Test;
+
+import org.eclipse.jdt.core.tests.util.CompilerTestSetup;
+import org.eclipse.objectteams.otdt.core.ext.WeavingScheme;
+import org.eclipse.objectteams.otdt.tests.otjld.AbstractOTJLDNullAnnotationTest;
+
+/** Test combination of OT-types with null type annotations. */
+public class OTNullTypeAnnotationTest extends AbstractOTJLDNullAnnotationTest {
+
+ public OTNullTypeAnnotationTest(String name) {
+ super(name);
+ }
+
+ // Static initializer to specify tests subset using TESTS_* static variables
+ // All specified tests which do not belong to the class are skipped...
+ static {
+// TESTS_NAMES = new String[] { "testExplicitTeamAnchor1" };
+// TESTS_NUMBERS = new int[] { 561 };
+// TESTS_RANGE = new int[] { 1, 2049 };
+ }
+
+ public static Test suite() {
+ return buildMinimalComplianceTestSuite(testClass(), F_1_8);
+ }
+
+ public static Class testClass() {
+ return OTNullTypeAnnotationTest.class;
+ }
+
+ @Override
+ public void initialize(CompilerTestSetup setUp) {
+ super.initialize(setUp);
+ if (this.weavingScheme == WeavingScheme.OTRE)
+ System.err.println("Running Java8 tests for OTRE weaver will skip most tests");
+ }
+
+ // Bug 437767 - [java8][null] semantically integrate null type annotations with anchored role types
+ // see comment 1
+ public void testImplicitTeamAnchor1() {
+ if (this.weavingScheme == WeavingScheme.OTRE) return;
+ runConformTestWithLibs(
+ new String[] {
+ "MyTeam.java",
+ "import org.eclipse.jdt.annotation.*;\n" +
+ "import java.util.*;\n" +
+ "public team class MyTeam {\n" +
+ " protected class MyRole {\n" +
+ " protected void print() {}\n" +
+ " }\n" +
+ " void test() {\n" +
+ " List<@NonNull MyRole> someRoles = new ArrayList<>();\n" +
+ " @NonNull MyRole role = someRoles.get(0);\n" +
+ " role.print();\n" +
+ " }\n" +
+ "}\n"
+ },
+ getCompilerOptions(),
+ "");
+ }
+
+ public void testImplicitTeamAnchor2() {
+ if (this.weavingScheme == WeavingScheme.OTRE) return;
+ runNegativeTestWithLibs(
+ new String[] {
+ "MyTeam.java",
+ "import org.eclipse.jdt.annotation.*;\n" +
+ "import java.util.*;\n" +
+ "public team class MyTeam {\n" +
+ " protected class MyRole {\n" +
+ " protected void print() {}\n" +
+ " }\n" +
+ " void test() {\n" +
+ " List<@Nullable MyRole> someRoles = new ArrayList<>();\n" +
+ " @NonNull MyRole role = someRoles.get(0);\n" +
+ " role.print();\n" +
+ " }\n" +
+ "}\n"
+ },
+ getCompilerOptions(),
+ "----------\n" +
+ "1. ERROR in MyTeam.java (at line 9)\n" +
+ " @NonNull MyRole role = someRoles.get(0);\n" +
+ " ^^^^^^^^^^^^^^^^\n" +
+ "Null type mismatch (type annotations): required \'MyTeam.@NonNull MyRole\' but this expression has type \'MyTeam.@Nullable MyRole\'\n" +
+ "----------\n");
+ }
+
+ public void testExplicitTeamAnchor1() {
+ if (this.weavingScheme == WeavingScheme.OTRE) return;
+ runNegativeTestWithLibs(
+ new String[] {
+ "Main.java",
+ "import org.eclipse.jdt.annotation.*;\n" +
+ "import java.util.*;\n" +
+ "public class Main {\n" +
+ " void test(final MyTeam t, List<@Nullable MyRole<@t>> someRoles) {\n" +
+ " @NonNull MyRole<@t> role = someRoles.get(0);\n" +
+ " role.print();\n" +
+ " }\n" +
+ "}\n",
+ "MyTeam.java",
+ "public team class MyTeam {\n" +
+ " public class MyRole {\n" +
+ " public void print() {}\n" +
+ " }\n" +
+ "}\n"
+ },
+ getCompilerOptions(),
+ "----------\n" +
+ "1. ERROR in Main.java (at line 5)\n" +
+ " @NonNull MyRole<@t> role = someRoles.get(0);\n" +
+ " ^^^^^^^^^^^^^^^^\n" +
+ "Null type mismatch (type annotations): required \'MyTeam.@NonNull MyRole\' but this expression has type \'MyTeam.@Nullable MyRole\'\n" +
+ "----------\n");
+ }
+
+ public void testLiftingType1() {
+ if (this.weavingScheme == WeavingScheme.OTRE) return;
+ runNegativeTestWithLibs(
+ new String[] {
+ "MyTeam.java",
+ "import org.eclipse.jdt.annotation.*;\n" +
+ "public team class MyTeam {\n" +
+ " protected class MyRole playedBy MyBase {\n" +
+ " protected void print() {}\n" +
+ " }\n" +
+ " void test(@Nullable MyBase as @NonNull MyRole r) {}\n" +
+ "}\n",
+ "MyBase.java",
+ "public class MyBase {}\n"
+ },
+ getCompilerOptions(),
+ "----------\n" +
+ "1. ERROR in MyTeam.java (at line 6)\n" +
+ " void test(@Nullable MyBase as @NonNull MyRole r) {}\n" +
+ " ^^^^^^^^^^^^^^^^^^^^^^^^^\n" +
+ "Null type mismatch (type annotations): required \'MyTeam.@NonNull MyRole\' but this expression has type \'@Nullable MyBase\'\n" +
+ "----------\n");
+ }
+
+ public void testLiftingType2() {
+ if (this.weavingScheme == WeavingScheme.OTRE) return;
+ runNegativeTestWithLibs(
+ new String[] {
+ "MyTeam.java",
+ "import org.eclipse.jdt.annotation.*;\n" +
+ "public team class MyTeam {\n" +
+ " protected class MyRole playedBy MyBase {\n" +
+ " protected void print() {}\n" +
+ " }\n" +
+ " void test1(MyBase @Nullable[] as MyRole @NonNull[] r) {}\n" +
+ " void test2(@Nullable MyBase @NonNull[] as @NonNull MyRole @NonNull[] r) {}\n" +
+ "}\n",
+ "MyBase.java",
+ "public class MyBase {}\n"
+ },
+ getCompilerOptions(),
+ "----------\n" +
+ "1. ERROR in MyTeam.java (at line 6)\n" +
+ " void test1(MyBase @Nullable[] as MyRole @NonNull[] r) {}\n" +
+ " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" +
+ "Null type mismatch (type annotations): required \'MyTeam.MyRole @NonNull[]\' but this expression has type \'MyBase @Nullable[]\'\n" +
+ "----------\n" +
+ "2. ERROR in MyTeam.java (at line 7)\n" +
+ " void test2(@Nullable MyBase @NonNull[] as @NonNull MyRole @NonNull[] r) {}\n" +
+ " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" +
+ "Null type mismatch (type annotations): required \'MyTeam.@NonNull MyRole @NonNull[]\' but this expression has type \'@Nullable MyBase @NonNull[]\'\n" +
+ "----------\n");
+ }
+
+}
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() {
diff --git a/testplugins/org.eclipse.objectteams.otdt.tests/otjld/org/eclipse/objectteams/otdt/tests/otjld/roleplaying/ExplicitRoleCreation.java b/testplugins/org.eclipse.objectteams.otdt.tests/otjld/org/eclipse/objectteams/otdt/tests/otjld/roleplaying/ExplicitRoleCreation.java
index 7893d72..b4eb9e5 100644
--- a/testplugins/org.eclipse.objectteams.otdt.tests/otjld/org/eclipse/objectteams/otdt/tests/otjld/roleplaying/ExplicitRoleCreation.java
+++ b/testplugins/org.eclipse.objectteams.otdt.tests/otjld/org/eclipse/objectteams/otdt/tests/otjld/roleplaying/ExplicitRoleCreation.java
@@ -3830,5 +3830,50 @@
},
"OK");
}
+
+ // a role is bound to a lower role with a non-visible constructor invoked via base()
+ // addition: one more arg (2byte)
+ public void test2335_creatingRoleOfRegularInner2b() {
+
+ runConformTest(
+ new String[] {
+ "Team2335crori2b_2.java",
+ "\n" +
+ "import b.Team2335crori2b_1;\n" +
+ "@SuppressWarnings(\"decapsulation\")\n" +
+ "public team class Team2335crori2b_2 {\n" +
+ " protected final Team2335crori2b_1 lower = new Team2335crori2b_1();\n" +
+ " protected class R playedBy Inner<@lower> {\n" +
+ " protected R(Team2335crori2b_1 outer) {\n" +
+ " outer.base(\"OK\", 1.2345d);\n" + // TODO: is outer. acceptable, shouldn't it always be lower.base()??
+ " }\n" +
+ " String getVal() -> get String val;\n" +
+ " protected void print() {\n" +
+ " System.out.print(getVal());\n" +
+ " }\n" +
+ " }\n" +
+ " void test() {\n" +
+ " new R(lower).print();\n" +
+ " }\n" +
+ " public static void main(String[] args) {\n" +
+ " new Team2335crori2b_2().test();\n" +
+ " }\n" +
+ "}\n" +
+ " \n",
+ "b/Team2335crori2b_1.java",
+ "\n" +
+ "package b;\n" +
+ "public team class Team2335crori2b_1 {\n" +
+ " protected class Inner {\n" +
+ " String val;\n" +
+ " protected Inner(String v, double d) {\n" +
+ " this.val = v+String.valueOf(d);\n" +
+ " }\n" +
+ " }\n" +
+ "}\n" +
+ " \n"
+ },
+ "OK1.2345");
+ }
// no test with private constructor: can never be called from outside (has no creation method).
}
diff --git a/testplugins/org.eclipse.objectteams.otdt.tests/otjld/org/eclipse/objectteams/otdt/tests/otjld/rolesandteams/AcquisitionAndInheritanceOfRoleClasses.java b/testplugins/org.eclipse.objectteams.otdt.tests/otjld/org/eclipse/objectteams/otdt/tests/otjld/rolesandteams/AcquisitionAndInheritanceOfRoleClasses.java
index 7cc39c6..39e6366 100644
--- a/testplugins/org.eclipse.objectteams.otdt.tests/otjld/org/eclipse/objectteams/otdt/tests/otjld/rolesandteams/AcquisitionAndInheritanceOfRoleClasses.java
+++ b/testplugins/org.eclipse.objectteams.otdt.tests/otjld/org/eclipse/objectteams/otdt/tests/otjld/rolesandteams/AcquisitionAndInheritanceOfRoleClasses.java
@@ -483,6 +483,57 @@
"OK");
}
+ // a role class accesses an overwritten feature that it indirectly inherited from its implicit super role
+ // challenge tsuper from private
+ public void _test132_roleInheritedFeatureAccess2b() {
+
+ runConformTest(
+ new String[] {
+ "T132rfa2bMain.java",
+ "\n" +
+ "public class T132rfa2bMain {\n" +
+ " public static void main(String[] args) {\n" +
+ " Team132rfa2b_3 t = new Team132rfa2b_3();\n" +
+ "\n" +
+ " System.out.print(t.getValue());\n" +
+ " }\n" +
+ "}\n" +
+ " \n",
+ "Team132rfa2b_1.java",
+ "\n" +
+ "public team class Team132rfa2b_1 {\n" +
+ " protected class Role132rfa2b {\n" +
+ " private String getValueInternal() {\n" +
+ " return \"K\";\n" +
+ " }\n" +
+ " }\n" +
+ "}\n" +
+ " \n",
+ "Team132rfa2b_2.java",
+ "\n" +
+ "public team class Team132rfa2b_2 extends Team132rfa2b_1 {}\n" +
+ " \n",
+ "Team132rfa2b_3.java",
+ "\n" +
+ "public team class Team132rfa2b_3 extends Team132rfa2b_2 {\n" +
+ " protected class Role132rfa2b {\n" +
+ " private String getValueInternal() {\n" +
+ " return \"O\"+tsuper.getValueInternal();\n" +
+ " }\n" +
+ " protected String getValue() {\n" +
+ " return getValueInternal();\n" +
+ " }\n" +
+ " }\n" +
+ "\n" +
+ " public String getValue() {\n" +
+ " return new Role132rfa2b().getValue();\n" +
+ " }\n" +
+ "}\n" +
+ " \n"
+ },
+ "OK");
+ }
+
// a role class accesses a feature inherited from its direct explicit super role
// 1.3.2-otjld-role-inherited-feature-access-3
public void test132_roleInheritedFeatureAccess3() {
diff --git a/testplugins/org.eclipse.objectteams.otdt.tests/otjld/org/eclipse/objectteams/otdt/tests/otjld/rolesandteams/ImplicitInheritance.java b/testplugins/org.eclipse.objectteams.otdt.tests/otjld/org/eclipse/objectteams/otdt/tests/otjld/rolesandteams/ImplicitInheritance.java
index b633483..234bd58 100644
--- a/testplugins/org.eclipse.objectteams.otdt.tests/otjld/org/eclipse/objectteams/otdt/tests/otjld/rolesandteams/ImplicitInheritance.java
+++ b/testplugins/org.eclipse.objectteams.otdt.tests/otjld/org/eclipse/objectteams/otdt/tests/otjld/rolesandteams/ImplicitInheritance.java
@@ -1373,22 +1373,22 @@
public void test0c16_implicitInheritanceRegression2() {
runConformTest(
new String[]{
- "p2/SubTeam.java",
- "package p2;\n" +
- "import p1.SuperTeam;\n" +
+ "p2b/SubTeam.java",
+ "package p2b;\n" +
+ "import p2a.SuperTeam;\n" +
"public team class SubTeam extends SuperTeam {\n" +
" public abstract class R {}\n" +
"}\n",
- "p1/SuperTeam.java",
- "package p1;\n" +
+ "p2a/SuperTeam.java",
+ "package p2a;\n" +
"public team class SuperTeam {\n" +
" final OtherTeam other=new OtherTeam();\n" +
" public abstract class R {\n" +
" OR<@other> otherRole;\n" +
" }\n" +
"}\n",
- "p1/OtherTeam.java",
- "package p1;\n" +
+ "p2a/OtherTeam.java",
+ "package p2a;\n" +
"public team class OtherTeam {\n" +
" public abstract class OR {}\n" +
"}\n"
@@ -1399,10 +1399,10 @@
public void test0c16_implicitInheritanceRegression3() {
runConformTest(
new String[]{
- "p2/SubTeam.java",
- "package p2;\n" +
+ "p3b/SubTeam.java",
+ "package p3b;\n" +
"import java.lang.annotation.*;\n" +
- "import p1.SuperTeam;\n" +
+ "import p3a.SuperTeam;\n" +
"public team class SubTeam extends SuperTeam {\n" +
" public void test() {\n" +
" new R().test(ElementType.METHOD);\n" +
@@ -1411,8 +1411,8 @@
" new SubTeam().test();\n" +
" }\n" +
"}\n",
- "p1/SuperTeam.java",
- "package p1;\n" +
+ "p3a/SuperTeam.java",
+ "package p3a;\n" +
"import java.lang.annotation.*;\n" +
"public team class SuperTeam {\n" +
" public class R {\n" +