blob: 70fe09a842a596fdc7d4c22ea96442a66ab2c610 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2012 IBM Corporation and others.
* 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
* $Id: FieldReference.java 23405 2010-02-03 17:02:18Z stephan $
*
* Contributors:
* IBM Corporation - initial API and implementation
* Stephan Herrmann <stephan@cs.tu-berlin.de> - Contribution for bug 185682 - Increment/decrement operators mark local variables as read
* Fraunhofer FIRST - extended API and implementation
* Technical University Berlin - extended API and implementation
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.ASTVisitor;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.codegen.CodeStream;
import org.eclipse.jdt.internal.compiler.codegen.Opcodes;
import org.eclipse.jdt.internal.compiler.flow.FlowContext;
import org.eclipse.jdt.internal.compiler.flow.FlowInfo;
import org.eclipse.jdt.internal.compiler.impl.Constant;
import org.eclipse.jdt.internal.compiler.lookup.ArrayBinding;
import org.eclipse.jdt.internal.compiler.lookup.Binding;
import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
import org.eclipse.jdt.internal.compiler.lookup.InvocationSite;
import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.MethodScope;
import org.eclipse.jdt.internal.compiler.lookup.MissingTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.ProblemFieldBinding;
import org.eclipse.jdt.internal.compiler.lookup.ProblemReasons;
import org.eclipse.jdt.internal.compiler.lookup.ProblemReferenceBinding;
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;
import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
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.mappings.CalloutImplementor;
import org.eclipse.objectteams.otdt.internal.core.compiler.model.FieldModel;
import org.eclipse.objectteams.otdt.internal.core.compiler.util.RoleTypeCreator;
/**
* OTDT changes:
*
* What: Trigger CollectedReplacementsTransformer
* Why: Might need to insert a cast to the role types class version.
*
* What: Unwrap receiver type in resolve if it's a role type.
* Why: Role type interface does not have fields, only class has.
*
* What: wrap resolvedType if it's a role.
*
* What: Create synth. accessors if a role accesses a field of its enclosing team.
* Why: The code containing the access might be copy-inherited to a different
* package which would break access restriction at run-time.
*
* What: baseclass decapsulation for receiver.
*
* @version $Id: FieldReference.java 23405 2010-02-03 17:02:18Z stephan $
*/
public class FieldReference extends Reference implements InvocationSite {
//{ObjectTeams: for baseclass decapsulation (implement interface from Expression):
private DecapsulationState baseclassDecapsulation = DecapsulationState.NONE;
public void setBaseclassDecapsulation(DecapsulationState state) {
this.baseclassDecapsulation = state;
}
@Override
public DecapsulationState getBaseclassDecapsulation() {
return this.baseclassDecapsulation;
}
@Override
public void tagReportedBaseclassDecapsulation() {
setBaseclassDecapsulation(DecapsulationState.REPORTED);
}
// SH}
public static final int READ = 0;
public static final int WRITE = 1;
public Expression receiver;
public char[] token;
public FieldBinding binding; // exact binding resulting from lookup
public MethodBinding[] syntheticAccessors; // [0]=read accessor [1]=write accessor
public long nameSourcePosition; //(start<<32)+end
public TypeBinding actualReceiverType;
public TypeBinding genericCast;
public FieldReference(char[] source, long pos) {
this.token = source;
this.nameSourcePosition = pos;
//by default the position are the one of the field (not true for super access)
this.sourceStart = (int) (pos >>> 32);
this.sourceEnd = (int) (pos & 0x00000000FFFFFFFFL);
this.bits |= Binding.FIELD;
}
public FlowInfo analyseAssignment(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo, Assignment assignment, boolean isCompound) {
// compound assignment extra work
if (isCompound) { // check the variable part is initialized if blank final
if (this.binding.isBlankFinal()
&& this.receiver.isThis()
&& currentScope.needBlankFinalFieldInitializationCheck(this.binding)) {
FlowInfo fieldInits = flowContext.getInitsForFinalBlankInitializationCheck(this.binding.declaringClass.original(), flowInfo);
if (!fieldInits.isDefinitelyAssigned(this.binding)) {
currentScope.problemReporter().uninitializedBlankFinalField(this.binding, this);
// we could improve error msg here telling "cannot use compound assignment on final blank field"
}
}
manageSyntheticAccessIfNecessary(currentScope, flowInfo, true /*read-access*/);
}
flowInfo =
this.receiver
.analyseCode(currentScope, flowContext, flowInfo, !this.binding.isStatic())
.unconditionalInits();
if (assignment.expression != null) {
flowInfo =
assignment
.expression
.analyseCode(currentScope, flowContext, flowInfo)
.unconditionalInits();
}
manageSyntheticAccessIfNecessary(currentScope, flowInfo, false /*write-access*/);
// check if assigning a final field
if (this.binding.isFinal()) {
// in a context where it can be assigned?
if (this.binding.isBlankFinal()
&& !isCompound
&& this.receiver.isThis()
&& !(this.receiver instanceof QualifiedThisReference)
&& ((this.receiver.bits & ASTNode.ParenthesizedMASK) == 0) // (this).x is forbidden
&& currentScope.allowBlankFinalFieldAssignment(this.binding)) {
if (flowInfo.isPotentiallyAssigned(this.binding)) {
currentScope.problemReporter().duplicateInitializationOfBlankFinalField(
this.binding,
this);
} else {
flowContext.recordSettingFinal(this.binding, this, flowInfo);
}
flowInfo.markAsDefinitelyAssigned(this.binding);
} else {
// assigning a final field outside an initializer or constructor or wrong reference
currentScope.problemReporter().cannotAssignToFinalField(this.binding, this);
}
}
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=318682
if (!this.binding.isStatic()) {
if (this.receiver.isThis()) {
currentScope.resetDeclaringClassMethodStaticFlag(this.binding.declaringClass);
}
} else if (this.receiver.isThis()) {
if ((this.receiver.bits & ASTNode.IsImplicitThis) == 0) {
// explicit this, not allowed in static context
currentScope.resetDeclaringClassMethodStaticFlag(this.binding.declaringClass);
}
}
return flowInfo;
}
public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) {
return analyseCode(currentScope, flowContext, flowInfo, true);
}
public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo, boolean valueRequired) {
boolean nonStatic = !this.binding.isStatic();
this.receiver.analyseCode(currentScope, flowContext, flowInfo, nonStatic);
if (nonStatic) {
this.receiver.checkNPE(currentScope, flowContext, flowInfo);
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=318682
if (this.receiver.isThis()) {
currentScope.resetDeclaringClassMethodStaticFlag(this.binding.declaringClass);
}
} else if (this.receiver.isThis()) {
if ((this.receiver.bits & ASTNode.IsImplicitThis) == 0) {
// explicit this receiver, not allowed in static context
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=318682
currentScope.resetEnclosingMethodStaticFlag();
}
}
if (valueRequired || currentScope.compilerOptions().complianceLevel >= ClassFileConstants.JDK1_4) {
manageSyntheticAccessIfNecessary(currentScope, flowInfo, true /*read-access*/);
}
return flowInfo;
}
/**
* @see org.eclipse.jdt.internal.compiler.ast.Expression#computeConversion(org.eclipse.jdt.internal.compiler.lookup.Scope, org.eclipse.jdt.internal.compiler.lookup.TypeBinding, org.eclipse.jdt.internal.compiler.lookup.TypeBinding)
*/
public void computeConversion(Scope scope, TypeBinding runtimeTimeType, TypeBinding compileTimeType) {
if (runtimeTimeType == null || compileTimeType == null)
return;
// set the generic cast after the fact, once the type expectation is fully known (no need for strict cast)
if (this.binding != null && this.binding.isValidBinding()) {
FieldBinding originalBinding = this.binding.original();
TypeBinding originalType = originalBinding.type;
// extra cast needed if field type is type variable
if (originalType.leafComponentType().isTypeVariable()) {
TypeBinding targetType = (!compileTimeType.isBaseType() && runtimeTimeType.isBaseType())
? compileTimeType // unboxing: checkcast before conversion
: runtimeTimeType;
this.genericCast = originalBinding.type.genericCast(targetType);
if (this.genericCast instanceof ReferenceBinding) {
ReferenceBinding referenceCast = (ReferenceBinding) this.genericCast;
if (!referenceCast.canBeSeenBy(scope)) {
scope.problemReporter().invalidType(this,
new ProblemReferenceBinding(
CharOperation.splitOn('.', referenceCast.shortReadableName()),
referenceCast,
ProblemReasons.NotVisible));
}
}
}
}
super.computeConversion(scope, runtimeTimeType, compileTimeType);
}
public FieldBinding fieldBinding() {
return this.binding;
}
public void generateAssignment(BlockScope currentScope, CodeStream codeStream, Assignment assignment, boolean valueRequired) {
int pc = codeStream.position;
FieldBinding codegenBinding = this.binding.original();
//{ObjectTeams: inferred callout-to-static-field??
if (!checkGeneratedSynthArgsForFieldAccess(this.syntheticAccessors, codeStream, currentScope))
// if nothing generated, generate regular receiver code:
// SH}
this.receiver.generateCode(currentScope, codeStream, !codegenBinding.isStatic());
codeStream.recordPositionsFrom(pc, this.sourceStart);
assignment.expression.generateCode(currentScope, codeStream, true);
fieldStore(currentScope, codeStream, codegenBinding, this.syntheticAccessors == null ? null : this.syntheticAccessors[FieldReference.WRITE], this.actualReceiverType, this.receiver.isImplicitThis(), valueRequired);
if (valueRequired) {
codeStream.generateImplicitConversion(assignment.implicitConversion);
}
// no need for generic cast as value got dupped
}
/**
* Field reference code generation
*
* @param currentScope org.eclipse.jdt.internal.compiler.lookup.BlockScope
* @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream
* @param valueRequired boolean
*/
public void generateCode(BlockScope currentScope, CodeStream codeStream, boolean valueRequired) {
int pc = codeStream.position;
if (this.constant != Constant.NotAConstant) {
if (valueRequired) {
codeStream.generateConstant(this.constant, this.implicitConversion);
}
codeStream.recordPositionsFrom(pc, this.sourceStart);
return;
}
FieldBinding codegenBinding = this.binding.original();
boolean isStatic = codegenBinding.isStatic();
boolean isThisReceiver = this.receiver instanceof ThisReference;
Constant fieldConstant = codegenBinding.constant();
if (fieldConstant != Constant.NotAConstant) {
if (!isThisReceiver) {
this.receiver.generateCode(currentScope, codeStream, !isStatic);
if (!isStatic){
codeStream.invokeObjectGetClass();
codeStream.pop();
}
}
if (valueRequired) {
codeStream.generateConstant(fieldConstant, this.implicitConversion);
}
codeStream.recordPositionsFrom(pc, this.sourceStart);
return;
}
if (valueRequired
|| (!isThisReceiver && currentScope.compilerOptions().complianceLevel >= ClassFileConstants.JDK1_4)
|| ((this.implicitConversion & TypeIds.UNBOXING) != 0)
|| (this.genericCast != null)) {
this.receiver.generateCode(currentScope, codeStream, !isStatic);
if ((this.bits & NeedReceiverGenericCast) != 0) {
codeStream.checkcast(this.actualReceiverType);
}
pc = codeStream.position;
if (codegenBinding.declaringClass == null) { // array length
codeStream.arraylength();
if (valueRequired) {
codeStream.generateImplicitConversion(this.implicitConversion);
} else {
// could occur if !valueRequired but compliance >= 1.4
codeStream.pop();
}
} else {
if (this.syntheticAccessors == null || this.syntheticAccessors[FieldReference.READ] == null) {
TypeBinding constantPoolDeclaringClass = CodeStream.getConstantPoolDeclaringClass(currentScope, codegenBinding, this.actualReceiverType, this.receiver.isImplicitThis());
if (isStatic) {
codeStream.fieldAccess(Opcodes.OPC_getstatic, codegenBinding, constantPoolDeclaringClass);
} else {
codeStream.fieldAccess(Opcodes.OPC_getfield, codegenBinding, constantPoolDeclaringClass);
}
} else {
codeStream.invoke(Opcodes.OPC_invokestatic, this.syntheticAccessors[FieldReference.READ], null /* default declaringClass */);
}
// required cast must occur even if no value is required
if (this.genericCast != null) codeStream.checkcast(this.genericCast);
if (valueRequired) {
codeStream.generateImplicitConversion(this.implicitConversion);
} else {
boolean isUnboxing = (this.implicitConversion & TypeIds.UNBOXING) != 0;
// conversion only generated if unboxing
if (isUnboxing) codeStream.generateImplicitConversion(this.implicitConversion);
switch (isUnboxing ? postConversionType(currentScope).id : codegenBinding.type.id) {
case T_long :
case T_double :
codeStream.pop2();
break;
default :
codeStream.pop();
}
}
}
} else {
if (isThisReceiver) {
if (isStatic){
// if no valueRequired, still need possible side-effects of <clinit> invocation, if field belongs to different class
if (this.binding.original().declaringClass != this.actualReceiverType.erasure()) {
MethodBinding accessor = this.syntheticAccessors == null ? null : this.syntheticAccessors[FieldReference.READ];
if (accessor == null) {
TypeBinding constantPoolDeclaringClass = CodeStream.getConstantPoolDeclaringClass(currentScope, codegenBinding, this.actualReceiverType, this.receiver.isImplicitThis());
codeStream.fieldAccess(Opcodes.OPC_getstatic, codegenBinding, constantPoolDeclaringClass);
} else {
codeStream.invoke(Opcodes.OPC_invokestatic, accessor, null /* default declaringClass */);
}
switch (codegenBinding.type.id) {
case T_long :
case T_double :
codeStream.pop2();
break;
default :
codeStream.pop();
}
}
}
} else {
this.receiver.generateCode(currentScope, codeStream, !isStatic);
if (!isStatic){
codeStream.invokeObjectGetClass(); // perform null check
codeStream.pop();
}
}
}
codeStream.recordPositionsFrom(pc, this.sourceEnd);
}
public void generateCompoundAssignment(BlockScope currentScope, CodeStream codeStream, Expression expression, int operator, int assignmentImplicitConversion, boolean valueRequired) {
boolean isStatic;
// check if compound assignment is the only usage of a private field
reportOnlyUselesslyReadPrivateField(currentScope, this.binding, valueRequired);
FieldBinding codegenBinding = this.binding.original();
this.receiver.generateCode(currentScope, codeStream, !(isStatic = codegenBinding.isStatic()));
if (isStatic) {
if (this.syntheticAccessors == null || this.syntheticAccessors[FieldReference.READ] == null) {
TypeBinding constantPoolDeclaringClass = CodeStream.getConstantPoolDeclaringClass(currentScope, codegenBinding, this.actualReceiverType, this.receiver.isImplicitThis());
codeStream.fieldAccess(Opcodes.OPC_getstatic, codegenBinding, constantPoolDeclaringClass);
} else {
codeStream.invoke(Opcodes.OPC_invokestatic, this.syntheticAccessors[FieldReference.READ], null /* default declaringClass */);
}
} else {
codeStream.dup();
if (this.syntheticAccessors == null || this.syntheticAccessors[FieldReference.READ] == null) {
TypeBinding constantPoolDeclaringClass = CodeStream.getConstantPoolDeclaringClass(currentScope, codegenBinding, this.actualReceiverType, this.receiver.isImplicitThis());
codeStream.fieldAccess(Opcodes.OPC_getfield, codegenBinding, constantPoolDeclaringClass);
} else {
codeStream.invoke(Opcodes.OPC_invokestatic, this.syntheticAccessors[FieldReference.READ], null /* default declaringClass */);
}
}
int operationTypeID;
switch(operationTypeID = (this.implicitConversion & TypeIds.IMPLICIT_CONVERSION_MASK) >> 4) {
case T_JavaLangString :
case T_JavaLangObject :
case T_undefined :
codeStream.generateStringConcatenationAppend(currentScope, null, expression);
break;
default :
if (this.genericCast != null)
codeStream.checkcast(this.genericCast);
// promote the array reference to the suitable operation type
codeStream.generateImplicitConversion(this.implicitConversion);
// generate the increment value (will by itself be promoted to the operation value)
if (expression == IntLiteral.One) { // prefix operation
codeStream.generateConstant(expression.constant, this.implicitConversion);
} else {
expression.generateCode(currentScope, codeStream, true);
}
// perform the operation
codeStream.sendOperator(operator, operationTypeID);
// cast the value back to the array reference type
codeStream.generateImplicitConversion(assignmentImplicitConversion);
}
fieldStore(currentScope, codeStream, codegenBinding, this.syntheticAccessors == null ? null : this.syntheticAccessors[FieldReference.WRITE], this.actualReceiverType, this.receiver.isImplicitThis(), valueRequired);
// no need for generic cast as value got dupped
}
public void generatePostIncrement(BlockScope currentScope, CodeStream codeStream, CompoundAssignment postIncrement, boolean valueRequired) {
boolean isStatic;
// check if postIncrement is the only usage of a private field
reportOnlyUselesslyReadPrivateField(currentScope, this.binding, valueRequired);
FieldBinding codegenBinding = this.binding.original();
this.receiver.generateCode(currentScope, codeStream, !(isStatic = codegenBinding.isStatic()));
if (isStatic) {
if (this.syntheticAccessors == null || this.syntheticAccessors[FieldReference.READ] == null) {
TypeBinding constantPoolDeclaringClass = CodeStream.getConstantPoolDeclaringClass(currentScope, codegenBinding, this.actualReceiverType, this.receiver.isImplicitThis());
codeStream.fieldAccess(Opcodes.OPC_getstatic, codegenBinding, constantPoolDeclaringClass);
} else {
codeStream.invoke(Opcodes.OPC_invokestatic, this.syntheticAccessors[FieldReference.READ], null /* default declaringClass */);
}
} else {
codeStream.dup();
if (this.syntheticAccessors == null || this.syntheticAccessors[FieldReference.READ] == null) {
TypeBinding constantPoolDeclaringClass = CodeStream.getConstantPoolDeclaringClass(currentScope, codegenBinding, this.actualReceiverType, this.receiver.isImplicitThis());
codeStream.fieldAccess(Opcodes.OPC_getfield, codegenBinding, constantPoolDeclaringClass);
} else {
codeStream.invoke(Opcodes.OPC_invokestatic, this.syntheticAccessors[FieldReference.READ], null /* default declaringClass */);
}
}
TypeBinding operandType;
if (this.genericCast != null) {
codeStream.checkcast(this.genericCast);
operandType = this.genericCast;
} else {
operandType = codegenBinding.type;
}
if (valueRequired) {
if (isStatic) {
switch (operandType.id) {
case TypeIds.T_long :
case TypeIds.T_double :
codeStream.dup2();
break;
default :
codeStream.dup();
break;
}
} else { // Stack: [owner][old field value] ---> [old field value][owner][old field value]
switch (operandType.id) {
case TypeIds.T_long :
case TypeIds.T_double :
codeStream.dup2_x1();
break;
default :
codeStream.dup_x1();
break;
}
}
}
codeStream.generateImplicitConversion(this.implicitConversion);
codeStream.generateConstant(
postIncrement.expression.constant,
this.implicitConversion);
codeStream.sendOperator(postIncrement.operator, this.implicitConversion & TypeIds.COMPILE_TYPE_MASK);
codeStream.generateImplicitConversion(
postIncrement.preAssignImplicitConversion);
fieldStore(currentScope, codeStream, codegenBinding, this.syntheticAccessors == null ? null : this.syntheticAccessors[FieldReference.WRITE], this.actualReceiverType, this.receiver.isImplicitThis(), false);
}
/**
* @see org.eclipse.jdt.internal.compiler.lookup.InvocationSite#genericTypeArguments()
*/
public TypeBinding[] genericTypeArguments() {
return null;
}
public boolean isSuperAccess() {
return this.receiver.isSuper();
}
public boolean isTypeAccess() {
return this.receiver != null && this.receiver.isTypeReference();
}
/*
* No need to emulate access to protected fields since not implicitly accessed
*/
public void manageSyntheticAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo, boolean isReadAccess) {
if ((flowInfo.tagBits & FlowInfo.UNREACHABLE_OR_DEAD) != 0) return;
//{ObjectTeams: don't create (more) synthetics for base field accessed via callout:
if ( FieldModel.isCalloutAccessed(this.binding)
&& this.syntheticAccessors != null
&& this.syntheticAccessors[isReadAccess ? FieldReference.READ : FieldReference.WRITE] != null)
{
return; // synthetic binding already created during resolveType; avoid CCE if declaringClass is binary.
}
// SH}
// if field from parameterized type got found, use the original field at codegen time
FieldBinding codegenBinding = this.binding.original();
if (this.binding.isPrivate()) {
if ((currentScope.enclosingSourceType() != codegenBinding.declaringClass)
&& this.binding.constant() == Constant.NotAConstant) {
if (this.syntheticAccessors == null)
this.syntheticAccessors = new MethodBinding[2];
this.syntheticAccessors[isReadAccess ? FieldReference.READ : FieldReference.WRITE] =
((SourceTypeBinding) codegenBinding.declaringClass).addSyntheticMethod(codegenBinding, isReadAccess, false /* not super ref in remote type*/,
//{ObjectTeams: new arg:
RoleTypeBinding.isRoleWithExplicitAnchor(this.actualReceiverType));
// SH}
currentScope.problemReporter().needToEmulateFieldAccess(codegenBinding, this, isReadAccess);
return;
}
} else if (this.receiver instanceof QualifiedSuperReference) { // qualified super
// qualified super need emulation always
SourceTypeBinding destinationType = (SourceTypeBinding) (((QualifiedSuperReference) this.receiver).currentCompatibleType);
if (this.syntheticAccessors == null)
this.syntheticAccessors = new MethodBinding[2];
this.syntheticAccessors[isReadAccess ? FieldReference.READ : FieldReference.WRITE] = destinationType.addSyntheticMethod(codegenBinding, isReadAccess, isSuperAccess(),
//{ObjectTeams: new arg:
RoleTypeBinding.isRoleWithExplicitAnchor(this.actualReceiverType));
// SH}
currentScope.problemReporter().needToEmulateFieldAccess(codegenBinding, this, isReadAccess);
return;
//{ObjectTeams: references to the enclosing team and role other than current need accessor, too:
/* orig:
} else if (this.binding.isProtected()) {
SourceTypeBinding enclosingSourceType;
if (((this.bits & ASTNode.DepthMASK) != 0)
&& this.binding.declaringClass.getPackage()
!= (enclosingSourceType = currentScope.enclosingSourceType()).getPackage()) {
SourceTypeBinding currentCompatibleType =
(SourceTypeBinding) enclosingSourceType.enclosingTypeAt(
(this.bits & ASTNode.DepthMASK) >> ASTNode.DepthSHIFT);
*/
} else if ( this.binding.isProtected()
|| this.binding.isDefault()
|| isRemoteRoleFieldAccess())
{
SourceTypeBinding enclosingSourceType = currentScope.enclosingSourceType();
int depth = getDepthForSynthFieldAccess(this.binding, enclosingSourceType);
if (depth > 0 || isRemoteRoleFieldAccess()) // TODO(SH): optimize: avoid accessor if role-to-role access?
{
SourceTypeBinding currentCompatibleType =
(SourceTypeBinding)enclosingSourceType.enclosingTypeAt(depth);
// orig:
if (this.syntheticAccessors == null)
this.syntheticAccessors = new MethodBinding[2];
this.syntheticAccessors[isReadAccess ? FieldReference.READ : FieldReference.WRITE] = currentCompatibleType.addSyntheticMethod(codegenBinding, isReadAccess, isSuperAccess(),
//{ObjectTeams: new arg:
RoleTypeBinding.isRoleWithExplicitAnchor(this.actualReceiverType));
// SH}
currentScope.problemReporter().needToEmulateFieldAccess(codegenBinding, this, isReadAccess);
return;
}
// SH}
}
}
//{ObjectTeams:
private boolean isRemoteRoleFieldAccess() {
if (this.receiver.isThis() && !(this.receiver instanceof QualifiedThisReference))
return false;
if (this.binding.isSynthetic())
return false;
return this.binding.declaringClass != null
&& this.binding.declaringClass.isRole();
}
// SH}
public int nullStatus(FlowInfo flowInfo) {
return FlowInfo.UNKNOWN;
}
public Constant optimizedBooleanConstant() {
switch (this.resolvedType.id) {
case T_boolean :
case T_JavaLangBoolean :
return this.constant != Constant.NotAConstant ? this.constant : this.binding.constant();
default :
return Constant.NotAConstant;
}
}
/**
* @see org.eclipse.jdt.internal.compiler.ast.Expression#postConversionType(Scope)
*/
public TypeBinding postConversionType(Scope scope) {
TypeBinding convertedType = this.resolvedType;
if (this.genericCast != null)
convertedType = this.genericCast;
int runtimeType = (this.implicitConversion & TypeIds.IMPLICIT_CONVERSION_MASK) >> 4;
switch (runtimeType) {
case T_boolean :
convertedType = TypeBinding.BOOLEAN;
break;
case T_byte :
convertedType = TypeBinding.BYTE;
break;
case T_short :
convertedType = TypeBinding.SHORT;
break;
case T_char :
convertedType = TypeBinding.CHAR;
break;
case T_int :
convertedType = TypeBinding.INT;
break;
case T_float :
convertedType = TypeBinding.FLOAT;
break;
case T_long :
convertedType = TypeBinding.LONG;
break;
case T_double :
convertedType = TypeBinding.DOUBLE;
break;
default :
}
if ((this.implicitConversion & TypeIds.BOXING) != 0) {
convertedType = scope.environment().computeBoxingType(convertedType);
}
return convertedType;
}
public StringBuffer printExpression(int indent, StringBuffer output) {
//{ObjectTeams: remove unneeded dot in debug view
if (this.receiver.isImplicitThis())
return output.append(this.token);
else
//MW}
return this.receiver.printExpression(0, output).append('.').append(this.token);
}
public TypeBinding resolveType(BlockScope scope) {
// Answer the signature type of the field.
// constants are propaged when the field is final
// and initialized with a (compile time) constant
//always ignore receiver cast, since may affect constant pool reference
boolean receiverCast = false;
if (this.receiver instanceof CastExpression) {
this.receiver.bits |= ASTNode.DisableUnnecessaryCastCheck; // will check later on
receiverCast = true;
}
this.actualReceiverType = this.receiver.resolveType(scope);
if (this.actualReceiverType == null) {
this.constant = Constant.NotAConstant;
return null;
}
//{ObjectTeams: unwrap type of this reference:
if (this.receiver instanceof ThisReference && this.actualReceiverType instanceof RoleTypeBinding)
this.actualReceiverType = ((RoleTypeBinding)this.actualReceiverType).getRealClass();
// SH}
if (receiverCast) {
// due to change of declaring class with receiver type, only identity cast should be notified
if (((CastExpression)this.receiver).expression.resolvedType == this.actualReceiverType) {
scope.problemReporter().unnecessaryCast((CastExpression)this.receiver);
}
}
// the case receiverType.isArrayType and token = 'length' is handled by the scope API
FieldBinding fieldBinding = this.binding = scope.getField(this.actualReceiverType, this.token, this);
//{ObjectTeams: baseclass decapsulation for receiver?
if (checkBaseclassDecapsulation(scope))
fieldBinding = this.binding; // modified during check
// SH}
if (!fieldBinding.isValidBinding()) {
this.constant = Constant.NotAConstant;
//{ObjectTeams: one more chance: inferred use of callout-to-field:
if (fieldBinding.problemId() == ProblemReasons.NotFound) {
boolean isSetter = ((this.bits & IsStrictlyAssigned) != 0);
CalloutMappingDeclaration callout = CalloutImplementor.inferCalloutAccess(scope, this.receiver, this, this.token, isSetter, this.expectedType);
if (callout != null) {
if (this.syntheticAccessors == null)
this.syntheticAccessors = new MethodBinding[2];
this.syntheticAccessors[isSetter ? FieldReference.WRITE : FieldReference.READ] =
new SyntheticMethodBinding(callout.roleMethodSpec.resolvedMethod, SyntheticMethodBinding.InferredCalloutToField);
fieldBinding = ((FieldAccessSpec)callout.baseMethodSpec).resolvedField;
this.binding = fieldBinding;
setDepth(fieldBinding.isStatic() ? 1 : 0); // static c-t-f needs to pass the enclosing team
if (((FieldAccessSpec)callout.baseMethodSpec).isSetter())
this.resolvedType = callout.roleMethodSpec.resolvedParameters()[0];
else
this.resolvedType = callout.roleMethodSpec.resolvedType();
}
}
if (!fieldBinding.isValidBinding()) { // might have been corrected by callout inference
// orig:
if (this.receiver.resolvedType instanceof ProblemReferenceBinding) {
// problem already got signaled on receiver, do not report secondary problem
return null;
}
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=245007 avoid secondary errors in case of
// missing super type for anonymous classes ...
ReferenceBinding declaringClass = fieldBinding.declaringClass;
boolean avoidSecondary = declaringClass != null &&
declaringClass.isAnonymousType() &&
declaringClass.superclass() instanceof MissingTypeBinding;
if (!avoidSecondary) {
scope.problemReporter().invalidField(this, this.actualReceiverType);
}
if (fieldBinding instanceof ProblemFieldBinding) {
ProblemFieldBinding problemFieldBinding = (ProblemFieldBinding) fieldBinding;
FieldBinding closestMatch = problemFieldBinding.closestMatch;
switch(problemFieldBinding.problemId()) {
case ProblemReasons.InheritedNameHidesEnclosingName :
case ProblemReasons.NotVisible :
case ProblemReasons.NonStaticReferenceInConstructorInvocation :
case ProblemReasons.NonStaticReferenceInStaticContext :
if (closestMatch != null) {
fieldBinding = closestMatch;
}
}
}
if (!fieldBinding.isValidBinding()) {
return null;
}
// :giro
}
// SH}
}
// handle indirect inheritance thru variable secondary bound
// receiver may receive generic cast, as part of implicit conversion
TypeBinding oldReceiverType = this.actualReceiverType;
this.actualReceiverType = this.actualReceiverType.getErasureCompatibleType(fieldBinding.declaringClass);
this.receiver.computeConversion(scope, this.actualReceiverType, this.actualReceiverType);
if (this.actualReceiverType != oldReceiverType && this.receiver.postConversionType(scope) != this.actualReceiverType) { // record need for explicit cast at codegen since receiver could not handle it
this.bits |= NeedReceiverGenericCast;
}
if (isFieldUseDeprecated(fieldBinding, scope, this.bits)) {
scope.problemReporter().deprecatedField(fieldBinding, this);
}
boolean isImplicitThisRcv = this.receiver.isImplicitThis();
this.constant = isImplicitThisRcv ? fieldBinding.constant() : Constant.NotAConstant;
if (fieldBinding.isStatic()) {
// static field accessed through receiver? legal but unoptimal (optional warning)
if (!(isImplicitThisRcv
|| (this.receiver instanceof NameReference
&& (((NameReference) this.receiver).bits & Binding.TYPE) != 0))) {
scope.problemReporter().nonStaticAccessToStaticField(this, fieldBinding);
}
ReferenceBinding declaringClass = this.binding.declaringClass;
if (!isImplicitThisRcv
&& declaringClass != this.actualReceiverType
&& declaringClass.canBeSeenBy(scope)) {
scope.problemReporter().indirectAccessToStaticField(this, fieldBinding);
}
// check if accessing enum static field in initializer
if (declaringClass.isEnum()) {
MethodScope methodScope = scope.methodScope();
SourceTypeBinding sourceType = scope.enclosingSourceType();
if (this.constant == Constant.NotAConstant
&& !methodScope.isStatic
&& (sourceType == declaringClass || sourceType.superclass == declaringClass) // enum constant body
&& methodScope.isInsideInitializerOrConstructor()) {
scope.problemReporter().enumStaticFieldUsedDuringInitialization(this.binding, this);
}
}
}
TypeBinding fieldType = fieldBinding.type;
if (fieldType != null) {
//{ObjectTeams: use pre-computed type for inferred callout-to-field:
if (this.resolvedType != null)
fieldType = this.resolvedType;
// SH}
if ((this.bits & ASTNode.IsStrictlyAssigned) == 0) {
fieldType = fieldType.capture(scope, this.sourceEnd); // perform capture conversion if read access
}
//{ObjectTeams: also wrap resolvedType:
fieldType = RoleTypeCreator.maybeWrapQualifiedRoleType(
scope, this.receiver, fieldType, this);
// SH}
this.resolvedType = fieldType;
if ((fieldType.tagBits & TagBits.HasMissingType) != 0) {
scope.problemReporter().invalidType(this, fieldType);
return null;
}
}
return fieldType;
}
//{ObjectTeams: baseclass decapsulation for receiver?
/** Is baseclass decapsulation allowed for this node?
* PRE: this node is resolved, perhaps to a ProblemFieldBinding.
* POST: If decapsulation takes place, it has been reported.
* @param scope enclosing scope of this node.
* @return whether decapsulation actually takes place.
*/
protected boolean checkBaseclassDecapsulation(Scope scope) {
if ( this.binding.problemId() == ProblemReasons.ReceiverTypeNotVisible
&& this.receiver.getBaseclassDecapsulation().isAllowed())
{
if ( this.actualReceiverType != null
&& !this.actualReceiverType.isCompatibleWith(scope.getJavaLangObject()))
return false; // the case of confined roles
// special field myArray.length:
if (this.actualReceiverType.isArrayType() && CharOperation.equals(this.binding.name, TypeConstants.LENGTH))
this.binding = ArrayBinding.ArrayLength;
else
this.binding = ((ReferenceBinding)this.actualReceiverType).getField(this.token, true);
scope.problemReporter().decapsulation(this.receiver);
SourceTypeBinding sourceType = scope.enclosingSourceType();
if (sourceType.isRole())
sourceType.roleModel.markBaseClassDecapsulation((ReferenceBinding)this.resolvedType);
return true;
}
return false;
}
//SH}
public void setActualReceiverType(ReferenceBinding receiverType) {
this.actualReceiverType = receiverType;
}
public void setDepth(int depth) {
this.bits &= ~ASTNode.DepthMASK; // flush previous depth if any
if (depth > 0) {
this.bits |= (depth & 0xFF) << ASTNode.DepthSHIFT; // encoded on 8 bits
}
}
public void setFieldIndex(int index) {
// ignored
}
public void traverse(ASTVisitor visitor, BlockScope scope) {
if (visitor.visit(this, scope)) {
this.receiver.traverse(visitor, scope);
}
visitor.endVisit(this, scope);
}
}