Partial fix for Bug 352914 - Avoid unnecessary decapsulation accessors for callout to field
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 c5aafc2..aa7eceb 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
@@ -149,27 +149,37 @@
(ReferenceBinding)fieldLeafType, resolvedType().dimensions());
}
- // find accessor method which might have been generated already.
- char[] accessorSelector = getSelector();
- MethodBinding result = null;
- if (!this.resolvedField.isPrivate()) // don't reuse accessor to private field from super-base (see Trac #232)
- result = baseType.getMethod(scope, accessorSelector);
- // NOTE: could be optimized if type has no such method but exact type already has.
- // but the the OTRE would need to be informed..
+ if ( !baseType.isRole()
+ && this.calloutModifier == TerminalTokens.TokenNameget
+ && this.resolvedField.canBeSeenBy(scope.enclosingReceiverType(), this, scope))
+ {
+ // no accessor method needed, signal this fact by NOT setting selector to accessorSelector
+ // and not setting resolvedMethod below
+ this.parameters = Binding.NO_PARAMETERS;
+ return;
+ } else {
+ // find accessor method which might have been generated already.
+ char[] accessorSelector = getSelector();
+ MethodBinding result = null;
+ if (!this.resolvedField.isPrivate()) // don't reuse accessor to private field from super-base (see Trac #232)
+ result = baseType.getMethod(scope, accessorSelector);
+ // NOTE: could be optimized if type has no such method but exact type already has.
+ // but the the OTRE would need to be informed..
- if ( result == null
- || !isMethodCompatible(result))
- {
- // record this field access for Attribute generation:
- RoleModel roleModel = scope.enclosingSourceType().roleModel;
- ReferenceBinding targetClass = roleModel.addAccessedBaseField(this.resolvedField, this.calloutModifier);
+ if ( result == null
+ || !isMethodCompatible(result))
+ {
+ // record this field access for Attribute generation:
+ RoleModel roleModel = scope.enclosingSourceType().roleModel;
+ ReferenceBinding targetClass = roleModel.addAccessedBaseField(this.resolvedField, this.calloutModifier);
- // create accessor method:
- result = createMethod(targetClass, accessorSelector);
- baseType.addMethod(result);
- }
- this.selector = accessorSelector;
- this.resolvedMethod = result;
+ // create accessor method:
+ result = createMethod(targetClass, accessorSelector);
+ baseType.addMethod(result);
+ }
+ this.selector = accessorSelector;
+ this.resolvedMethod = result;
+ }
this.parameters = this.resolvedMethod.getSourceParameters();
}
@@ -243,6 +253,8 @@
* (only staticness of the method makes it look differently.)
*/
public TypeBinding[] resolvedParameters() {
+ if (this.resolvedMethod == null)
+ return this.parameters; // empty; FIXME(SH): handle "set" access
TypeBinding[] methodParams = super.resolvedParameters();
if (this.resolvedField.isStatic())
return methodParams; // no base argument when accessing a static field
@@ -276,13 +288,21 @@
public boolean checkBaseReturnType(CallinCalloutScope scope, int bindDir)
{
- TypeBinding accessorReturnType = (this.calloutModifier == TerminalTokens.TokenNameget) ?
- (this.returnType != null ? this.returnType.resolvedType : null):
- TypeBinding.VOID;
- if (!TypeAnalyzer.isSameType(scope.enclosingSourceType(), accessorReturnType, this.resolvedMethod.returnType))
+ TypeBinding accessorReturnType;
+ TypeBinding baseReturnType;
+ if (this.calloutModifier == TerminalTokens.TokenNameget) {
+ accessorReturnType = this.returnType != null ? this.returnType.resolvedType : null;
+ baseReturnType = this.resolvedField.type;
+ } else {
+ accessorReturnType = TypeBinding.VOID;
+ if (this.resolvedMethod != null)
+ baseReturnType = this.resolvedMethod.returnType;
+ else
+ baseReturnType = TypeBinding.VOID;
+ }
+ if (!TypeAnalyzer.isSameType(scope.enclosingSourceType(), accessorReturnType, baseReturnType))
{
- // copied from MethodSpec, but may not be needed:
- if (RoleTypeCreator.isCompatibleViaBaseAnchor(scope, accessorReturnType, this.returnType.resolvedType, bindDir))
+ if (RoleTypeCreator.isCompatibleViaBaseAnchor(scope, baseReturnType, accessorReturnType, bindDir))
return true;
scope.problemReporter().differentTypeInFieldSpec(this);
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/ast/MethodSpec.java b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/ast/MethodSpec.java
index e858758..a1d9320 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/ast/MethodSpec.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/ast/MethodSpec.java
@@ -357,7 +357,7 @@
/** Answer parameters of the resolved method (source visible ones only). */
public TypeBinding[] resolvedParameters() {
// parameters are first choice, because these might be instantiated parameters
- if (this.parameters == null || this.parameters == Binding.NO_PARAMETERS)
+ if ((this.parameters == null || this.parameters == Binding.NO_PARAMETERS) && this.resolvedMethod != null)
this.parameters = this.resolvedMethod.getSourceParameters();
return this.parameters;
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/lookup/CallinCalloutBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/lookup/CallinCalloutBinding.java
index 5b3ebb8..af980fe 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/lookup/CallinCalloutBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/lookup/CallinCalloutBinding.java
@@ -23,6 +23,7 @@
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.lookup.AnnotationBinding;
import org.eclipse.jdt.internal.compiler.lookup.Binding;
+import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.ProblemReasons;
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
@@ -53,6 +54,7 @@
public MethodBinding _roleMethodBinding;
public ReferenceBinding _declaringRoleClass;
public MethodBinding[] _baseMethods = Binding.NO_METHODS;
+ public FieldBinding _baseField;
public int type;
public int callinModifier; // TerminalTokens: before,after,replace
public int calloutModifier; // TerminalTokens: get or set
@@ -203,6 +205,8 @@
for (MethodBinding method : this._baseMethods)
if (!method.isValidBinding())
return method.problemId();
+ if (this._baseField != null)
+ return this._baseField.problemId();
return ProblemReasons.NoError;
}
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 65e912a..f01fa1f 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
@@ -551,45 +551,54 @@
char[] selector = calloutDecl.baseMethodSpec.selector;
ReferenceBinding baseType = this._role.getBaseTypeBinding();
- Expression receiver = new CastExpression(
+ Expression receiver = gen.castExpression(
gen.baseNameReference(IOTConstants._OT_BASE),
gen.baseclassReference(baseType),
CastExpression.DO_WRAP);
- MessageSend messageSend;
- if (calloutDecl.baseMethodSpec.isPrivate() && baseType.isRole()) {
- // tricky case: callout to a private role method (base-side)
- // requires the indirection via two wrapper methods (privateBridgeMethod)
-
- // compensate weakening:
- if (baseType instanceof WeakenedTypeBinding)
- baseType = ((WeakenedTypeBinding)baseType).getStrongType();
-
- // generated message send refers to public bridge, report decapsulation now:
- calloutDecl.scope.problemReporter().decapsulation(calloutDecl.baseMethodSpec, baseType, calloutDecl.scope);
-
- boolean isCalloutToField = calloutDecl.isCalloutToField();
- MethodBinding targetMethod = calloutDecl.baseMethodSpec.resolvedMethod;
- messageSend = new PrivateRoleMethodCall(receiver, selector, arguments, isCalloutToField,
- calloutDecl.scope, baseType, targetMethod, gen);
- } else {
- if (calloutDecl.baseMethodSpec.isStatic() || calloutDecl.isCalloutToField())
- // we thought we should use an instance
- // but callout-to-static and normal c-t-f is sent to the base *class*
- receiver = gen.baseNameReference(baseType.getRealClass());
-
- messageSend = gen.messageSend(receiver, selector, arguments);
+ Expression baseAccess = null;
+ if (calloutDecl.isCalloutToField()) {
+ FieldAccessSpec fieldSpec = (FieldAccessSpec) calloutDecl.baseMethodSpec;
+ if (fieldSpec.resolvedMethod == null) {
+ // not using a decapsulation accessor but direct field access:
+ baseAccess = gen.qualifiedNameReference(new char[][] {
+ IOTConstants._OT_BASE,
+ fieldSpec.getFieldName()
+ });
+ }
+ }
+ if (baseAccess == null) {
+ if (calloutDecl.baseMethodSpec.isPrivate() && baseType.isRole()) {
+ // tricky case: callout to a private role method (base-side)
+ // requires the indirection via two wrapper methods (privateBridgeMethod)
+
+ // compensate weakening:
+ if (baseType instanceof WeakenedTypeBinding)
+ baseType = ((WeakenedTypeBinding)baseType).getStrongType();
+
+ // generated message send refers to public bridge, report decapsulation now:
+ calloutDecl.scope.problemReporter().decapsulation(calloutDecl.baseMethodSpec, baseType, calloutDecl.scope);
+
+ boolean isCalloutToField = calloutDecl.isCalloutToField();
+ MethodBinding targetMethod = calloutDecl.baseMethodSpec.resolvedMethod;
+ baseAccess = new PrivateRoleMethodCall(receiver, selector, arguments, isCalloutToField,
+ calloutDecl.scope, baseType, targetMethod, gen);
+ } else {
+ if (calloutDecl.baseMethodSpec.isStatic() || calloutDecl.isCalloutToField())
+ // we thought we should use an instance
+ // but callout-to-static and normal c-t-f is sent to the base *class*
+ receiver = gen.baseNameReference(baseType.getRealClass());
+
+ baseAccess = gen.messageSend(receiver, selector, arguments);
+ }
}
- // TODO(SH): create receiver via gen, too.
- messageSend.receiver.sourceStart = sStart;
- messageSend.receiver.sourceEnd = sEnd;
boolean success = true;
ArrayList<Statement> statements = new ArrayList<Statement>(3);
if(returnType == TypeBinding.VOID)
{
// just the method call
- statements.add(messageSend);
+ statements.add(baseAccess);
// generate empty return statement so that it gets a proper source position
statements.add(gen.returnStatement(null));
@@ -601,7 +610,7 @@
gen.returnStatement(
gen.potentialLift(
null, // use default receiver
- messageSend,
+ baseAccess,
returnType,
false))); // no reversing required
}
@@ -610,7 +619,7 @@
// return result with parameter mapping
success = transformCalloutMethodBodyResultMapping(
statements,
- messageSend,
+ baseAccess,
calloutDecl,
roleMethodDeclaration);
}
@@ -1283,7 +1292,11 @@
callout.resolveMethodSpecs(roleClass.getRoleModel(), baseclass, true);
calloutBinding.inferred= isSetter ? InferenceKind.FIELDSET : InferenceKind.FIELDGET;
- calloutBinding._baseMethods = new MethodBinding[] {callout.baseMethodSpec.resolvedMethod};
+ if (callout.baseMethodSpec.resolvedMethod != null) {
+ calloutBinding._baseMethods = new MethodBinding[] {callout.baseMethodSpec.resolvedMethod};
+ } else {
+ calloutBinding._baseField = ((FieldAccessSpec)callout.baseMethodSpec).resolvedField;
+ }
roleClass.binding.addCallinCallouts(new CallinCalloutBinding[]{calloutBinding});
// translate to method declaration:
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/mappings/MethodMappingResolver.java b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/mappings/MethodMappingResolver.java
index aadbd4b..163b403 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/mappings/MethodMappingResolver.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/objectteams/otdt/internal/core/compiler/mappings/MethodMappingResolver.java
@@ -34,6 +34,8 @@
import org.eclipse.objectteams.otdt.internal.core.compiler.ast.AbstractMethodMappingDeclaration;
import org.eclipse.objectteams.otdt.internal.core.compiler.ast.CallinMappingDeclaration;
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.model.RoleModel;
@@ -163,13 +165,18 @@
calloutMappingDeclaration.binding._roleMethodBinding = roleMethodBinding;
if (this.resolveBaseMethods) {
- if ( calloutMappingDeclaration.baseMethodSpec != null
- && calloutMappingDeclaration.baseMethodSpec.resolvedMethod != null)
- {
- calloutMappingDeclaration.binding._baseMethods = new MethodBinding[]{calloutMappingDeclaration.baseMethodSpec.resolvedMethod};
- } else {
- calloutMappingDeclaration.binding._baseMethods = Binding.NO_METHODS;
- calloutMappingDeclaration.tagAsHavingErrors();
+ MethodSpec baseMethodSpec = calloutMappingDeclaration.baseMethodSpec;
+ if ( baseMethodSpec != null) {
+ if (baseMethodSpec.resolvedMethod != null) {
+ calloutMappingDeclaration.binding._baseMethods = new MethodBinding[]{baseMethodSpec.resolvedMethod};
+ } else if ( baseMethodSpec instanceof FieldAccessSpec
+ && ((FieldAccessSpec)baseMethodSpec).resolvedField != null)
+ {
+ calloutMappingDeclaration.binding._baseField = ((FieldAccessSpec)baseMethodSpec).resolvedField;
+ } else {
+ calloutMappingDeclaration.binding._baseMethods = Binding.NO_METHODS;
+ calloutMappingDeclaration.tagAsHavingErrors();
+ }
}
}
if ( roleMethodBinding != null