X-Git-Url: http://git.megacz.com/?a=blobdiff_plain;f=src%2Forg%2Feclipse%2Fjdt%2Finternal%2Fcompiler%2Fast%2FQualifiedNameReference.java;fp=src%2Forg%2Feclipse%2Fjdt%2Finternal%2Fcompiler%2Fast%2FQualifiedNameReference.java;h=8afa15a179800a0e209b35f2b32dcf11e070d491;hb=040fa5af2cd00017cf3575950cdaade34a6d7f6c;hp=0000000000000000000000000000000000000000;hpb=a580fb8376d315d05e4d6bfdff9ff1101a151cd6;p=org.ibex.tool.git diff --git a/src/org/eclipse/jdt/internal/compiler/ast/QualifiedNameReference.java b/src/org/eclipse/jdt/internal/compiler/ast/QualifiedNameReference.java new file mode 100644 index 0000000..8afa15a --- /dev/null +++ b/src/org/eclipse/jdt/internal/compiler/ast/QualifiedNameReference.java @@ -0,0 +1,812 @@ +/******************************************************************************* + * Copyright (c) 2000, 2004 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Common Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.compiler.ast; + +import org.eclipse.jdt.internal.compiler.ASTVisitor; +import org.eclipse.jdt.internal.compiler.impl.*; +import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; +import org.eclipse.jdt.internal.compiler.codegen.*; +import org.eclipse.jdt.internal.compiler.flow.*; +import org.eclipse.jdt.internal.compiler.lookup.*; +import org.eclipse.jdt.internal.compiler.problem.ProblemSeverities; + +public class QualifiedNameReference extends NameReference { + + public char[][] tokens; + public long[] sourcePositions; + public FieldBinding[] otherBindings, otherCodegenBindings; + int[] otherDepths; + public int indexOfFirstFieldBinding;//points (into tokens) for the first token that corresponds to first FieldBinding + SyntheticAccessMethodBinding syntheticWriteAccessor; + SyntheticAccessMethodBinding[] syntheticReadAccessors; + protected FieldBinding lastFieldBinding; + public QualifiedNameReference( + char[][] sources, + long[] positions, + int sourceStart, + int sourceEnd) { + super(); + this.tokens = sources; + this.sourcePositions = positions; + this.sourceStart = sourceStart; + this.sourceEnd = sourceEnd; + } + public FlowInfo analyseAssignment( + BlockScope currentScope, + FlowContext flowContext, + FlowInfo flowInfo, + Assignment assignment, + boolean isCompound) { + + // determine the rank until which we now we do not need any actual value for the field access + int otherBindingsCount = otherBindings == null ? 0 : otherBindings.length; + boolean needValue = otherBindingsCount == 0 || !this.otherBindings[0].isStatic(); + switch (bits & RestrictiveFlagMASK) { + case FIELD : // reading a field + lastFieldBinding = (FieldBinding) binding; + if (needValue) { + manageSyntheticReadAccessIfNecessary(currentScope, lastFieldBinding, this.actualReceiverType, 0, flowInfo); + } // check if final blank field + if (lastFieldBinding.isBlankFinal() + && this.otherBindings != null // the last field binding is only assigned + && currentScope.allowBlankFinalFieldAssignment(lastFieldBinding)) { + if (!flowInfo.isDefinitelyAssigned(lastFieldBinding)) { + currentScope.problemReporter().uninitializedBlankFinalField( + lastFieldBinding, + this); + } + } + break; + case LOCAL : + // first binding is a local variable + LocalVariableBinding localBinding; + if (!flowInfo + .isDefinitelyAssigned(localBinding = (LocalVariableBinding) binding)) { + currentScope.problemReporter().uninitializedLocalVariable(localBinding, this); + } + if (flowInfo.isReachable()) { + localBinding.useFlag = LocalVariableBinding.USED; + } else if (localBinding.useFlag == LocalVariableBinding.UNUSED) { + localBinding.useFlag = LocalVariableBinding.FAKE_USED; + } + } + + if (needValue) { + manageEnclosingInstanceAccessIfNecessary(currentScope, flowInfo); + // only for first binding + } + // all intermediate field accesses are read accesses + if (otherBindings != null) { + for (int i = 0; i < otherBindingsCount-1; i++) { + lastFieldBinding = otherBindings[i]; + needValue = !otherBindings[i+1].isStatic(); + if (needValue) { + manageSyntheticReadAccessIfNecessary( + currentScope, + lastFieldBinding, + i == 0 + ? ((VariableBinding)binding).type + : otherBindings[i-1].type, + i + 1, + flowInfo); + } + } + lastFieldBinding = otherBindings[otherBindingsCount-1]; + } + + if (isCompound) { + if (binding == lastFieldBinding + && lastFieldBinding.isBlankFinal() + && currentScope.allowBlankFinalFieldAssignment(lastFieldBinding) + && (!flowInfo.isDefinitelyAssigned(lastFieldBinding))) { + currentScope.problemReporter().uninitializedBlankFinalField( + lastFieldBinding, + this); + } + TypeBinding lastReceiverType; + if (lastFieldBinding == binding){ + lastReceiverType = this.actualReceiverType; + } else if (otherBindingsCount == 1){ + lastReceiverType = ((VariableBinding)this.binding).type; + } else { + lastReceiverType = this.otherBindings[otherBindingsCount-2].type; + } + manageSyntheticReadAccessIfNecessary( + currentScope, + lastFieldBinding, + lastReceiverType, + lastFieldBinding == binding + ? 0 + : otherBindingsCount, + flowInfo); + } + + if (assignment.expression != null) { + flowInfo = + assignment + .expression + .analyseCode(currentScope, flowContext, flowInfo) + .unconditionalInits(); + } + + // the last field access is a write access + if (lastFieldBinding.isFinal()) { + // in a context where it can be assigned? + if (lastFieldBinding.isBlankFinal() + && !isCompound + && currentScope.allowBlankFinalFieldAssignment(lastFieldBinding) + && indexOfFirstFieldBinding == 1) { + if (flowInfo.isPotentiallyAssigned(lastFieldBinding)) { + currentScope.problemReporter().duplicateInitializationOfBlankFinalField(lastFieldBinding, this); + } else { + flowContext.recordSettingFinal(lastFieldBinding, this, flowInfo); + } + flowInfo.markAsDefinitelyAssigned(lastFieldBinding); + } else { + currentScope.problemReporter().cannotAssignToFinalField(lastFieldBinding, this); + if (currentScope.allowBlankFinalFieldAssignment(lastFieldBinding)) { // pretend it got assigned + flowInfo.markAsDefinitelyAssigned(lastFieldBinding); + } + } + } + // equivalent to valuesRequired[maxOtherBindings] + TypeBinding lastReceiverType; + if (lastFieldBinding == binding){ + lastReceiverType = this.actualReceiverType; + } else if (otherBindingsCount == 1){ + lastReceiverType = ((VariableBinding)this.binding).type; + } else { + lastReceiverType = this.otherBindings[otherBindingsCount-2].type; + } + manageSyntheticWriteAccessIfNecessary(currentScope, lastFieldBinding, lastReceiverType, flowInfo); + + 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) { + + // determine the rank until which we now we do not need any actual value for the field access + int otherBindingsCount = otherBindings == null ? 0 : otherBindings.length; + + boolean needValue = otherBindingsCount == 0 ? valueRequired : !this.otherBindings[0].isStatic(); + switch (bits & RestrictiveFlagMASK) { + case FIELD : // reading a field + if (needValue) { + manageSyntheticReadAccessIfNecessary(currentScope, (FieldBinding) binding, this.actualReceiverType, 0, flowInfo); + } + // check if reading a final blank field + FieldBinding fieldBinding; + if ((fieldBinding = (FieldBinding) binding).isBlankFinal() + && (indexOfFirstFieldBinding == 1) + // was an implicit reference to the first field binding + && currentScope.allowBlankFinalFieldAssignment(fieldBinding) + && (!flowInfo.isDefinitelyAssigned(fieldBinding))) { + currentScope.problemReporter().uninitializedBlankFinalField(fieldBinding, this); + } + break; + case LOCAL : // reading a local variable + LocalVariableBinding localBinding; + if (!flowInfo + .isDefinitelyAssigned(localBinding = (LocalVariableBinding) binding)) { + currentScope.problemReporter().uninitializedLocalVariable(localBinding, this); + } + if (flowInfo.isReachable()) { + localBinding.useFlag = LocalVariableBinding.USED; + } else if (localBinding.useFlag == LocalVariableBinding.UNUSED) { + localBinding.useFlag = LocalVariableBinding.FAKE_USED; + } + } + if (needValue) { + manageEnclosingInstanceAccessIfNecessary(currentScope, flowInfo); + // only for first binding + } + if (otherBindings != null) { + for (int i = 0; i < otherBindingsCount; i++) { + needValue = i < otherBindingsCount-1 ? !otherBindings[i+1].isStatic() : valueRequired; + if (needValue) { + manageSyntheticReadAccessIfNecessary( + currentScope, + otherBindings[i], + i == 0 + ? ((VariableBinding)binding).type + : otherBindings[i-1].type, + i + 1, + flowInfo); + } + } + } + return flowInfo; + } + /** + * Check and/or redirect the field access to the delegate receiver if any + */ + public TypeBinding checkFieldAccess(BlockScope scope) { + // check for forward references + FieldBinding fieldBinding = (FieldBinding) binding; + MethodScope methodScope = scope.methodScope(); + if (methodScope.enclosingSourceType() == fieldBinding.declaringClass + && methodScope.lastVisibleFieldID >= 0 + && fieldBinding.id >= methodScope.lastVisibleFieldID) { + if ((!fieldBinding.isStatic() || methodScope.isStatic) + && this.indexOfFirstFieldBinding == 1) + scope.problemReporter().forwardReference(this, 0, scope.enclosingSourceType()); + } + bits &= ~RestrictiveFlagMASK; // clear bits + bits |= FIELD; + return getOtherFieldBindings(scope); + } + public void generateAssignment( + BlockScope currentScope, + CodeStream codeStream, + Assignment assignment, + boolean valueRequired) { + + generateReadSequence(currentScope, codeStream); + assignment.expression.generateCode(currentScope, codeStream, true); + fieldStore(codeStream, lastFieldBinding, syntheticWriteAccessor, valueRequired); + // equivalent to valuesRequired[maxOtherBindings] + if (valueRequired) { + codeStream.generateImplicitConversion(assignment.implicitConversion); + } + } + public void generateCode( + BlockScope currentScope, + CodeStream codeStream, + boolean valueRequired) { + + int pc = codeStream.position; + if (constant != NotAConstant) { + if (valueRequired) { + codeStream.generateConstant(constant, implicitConversion); + } + } else { + generateReadSequence(currentScope, codeStream); + if (valueRequired) { + if (lastFieldBinding.declaringClass == null) { // array length + codeStream.arraylength(); + codeStream.generateImplicitConversion(implicitConversion); + } else { + if (lastFieldBinding.constant != NotAConstant) { + if (!lastFieldBinding.isStatic()){ + codeStream.invokeObjectGetClass(); + codeStream.pop(); + } + // inline the last field constant + codeStream.generateConstant(lastFieldBinding.constant, implicitConversion); + } else { + SyntheticAccessMethodBinding accessor = + syntheticReadAccessors == null + ? null + : syntheticReadAccessors[syntheticReadAccessors.length - 1]; + if (accessor == null) { + if (lastFieldBinding.isStatic()) { + codeStream.getstatic(lastFieldBinding); + } else { + codeStream.getfield(lastFieldBinding); + } + } else { + codeStream.invokestatic(accessor); + } + codeStream.generateImplicitConversion(implicitConversion); + } + } + } else { + if (lastFieldBinding != null && !lastFieldBinding.isStatic()){ + codeStream.invokeObjectGetClass(); // perform null check + codeStream.pop(); + } + + } + } + codeStream.recordPositionsFrom(pc, this.sourceStart); + } + public void generateCompoundAssignment( + BlockScope currentScope, + CodeStream codeStream, + Expression expression, + int operator, + int assignmentImplicitConversion, + boolean valueRequired) { + + generateReadSequence(currentScope, codeStream); + SyntheticAccessMethodBinding accessor = + syntheticReadAccessors == null + ? null + : syntheticReadAccessors[syntheticReadAccessors.length - 1]; + if (lastFieldBinding.isStatic()) { + if (accessor == null) { + codeStream.getstatic(lastFieldBinding); + } else { + codeStream.invokestatic(accessor); + } + } else { + codeStream.dup(); + if (accessor == null) { + codeStream.getfield(lastFieldBinding); + } else { + codeStream.invokestatic(accessor); + } + } + // the last field access is a write access + // perform the actual compound operation + int operationTypeID; + if ((operationTypeID = implicitConversion >> 4) == T_String) { + codeStream.generateStringAppend(currentScope, null, expression); + } else { + // promote the array reference to the suitable operation type + codeStream.generateImplicitConversion(implicitConversion); + // generate the increment value (will by itself be promoted to the operation value) + if (expression == IntLiteral.One) { // prefix operation + codeStream.generateConstant(expression.constant, 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); + } + // actual assignment + fieldStore(codeStream, lastFieldBinding, syntheticWriteAccessor, valueRequired); + // equivalent to valuesRequired[maxOtherBindings] + } + public void generatePostIncrement( + BlockScope currentScope, + CodeStream codeStream, + CompoundAssignment postIncrement, + boolean valueRequired) { + generateReadSequence(currentScope, codeStream); + SyntheticAccessMethodBinding accessor = + syntheticReadAccessors == null + ? null + : syntheticReadAccessors[syntheticReadAccessors.length - 1]; + if (lastFieldBinding.isStatic()) { + if (accessor == null) { + codeStream.getstatic(lastFieldBinding); + } else { + codeStream.invokestatic(accessor); + } + } else { + codeStream.dup(); + if (accessor == null) { + codeStream.getfield(lastFieldBinding); + } else { + codeStream.invokestatic(accessor); + } + } + // duplicate the old field value + if (valueRequired) { + if (lastFieldBinding.isStatic()) { + if ((lastFieldBinding.type == LongBinding) + || (lastFieldBinding.type == DoubleBinding)) { + codeStream.dup2(); + } else { + codeStream.dup(); + } + } else { // Stack: [owner][old field value] ---> [old field value][owner][old field value] + if ((lastFieldBinding.type == LongBinding) + || (lastFieldBinding.type == DoubleBinding)) { + codeStream.dup2_x1(); + } else { + codeStream.dup_x1(); + } + } + } + codeStream.generateConstant( + postIncrement.expression.constant, + implicitConversion); + codeStream.sendOperator(postIncrement.operator, lastFieldBinding.type.id); + codeStream.generateImplicitConversion( + postIncrement.assignmentImplicitConversion); + fieldStore(codeStream, lastFieldBinding, syntheticWriteAccessor, false); + } + /* + * Generate code for all bindings (local and fields) excluding the last one, which may then be generated code + * for a read or write access. + */ + public void generateReadSequence( + BlockScope currentScope, + CodeStream codeStream) { + + // determine the rank until which we now we do not need any actual value for the field access + int otherBindingsCount = this.otherCodegenBindings == null ? 0 : otherCodegenBindings.length; + + boolean needValue = otherBindingsCount == 0 || !this.otherBindings[0].isStatic(); + switch (bits & RestrictiveFlagMASK) { + case FIELD : + lastFieldBinding = (FieldBinding) this.codegenBinding; + // if first field is actually constant, we can inline it + if (lastFieldBinding.constant != NotAConstant) { + break; + } + if (needValue && !lastFieldBinding.isStatic()) { + if ((bits & DepthMASK) != 0) { + ReferenceBinding targetType = currentScope.enclosingSourceType().enclosingTypeAt((bits & DepthMASK) >> DepthSHIFT); + Object[] emulationPath = currentScope.getEmulationPath(targetType, true /*only exact match*/, false/*consider enclosing arg*/); + codeStream.generateOuterAccess(emulationPath, this, targetType, currentScope); + } else { + generateReceiver(codeStream); + } + } + break; + case LOCAL : // reading the first local variable + lastFieldBinding = null; + if (!needValue) break; // no value needed + LocalVariableBinding localBinding = (LocalVariableBinding) this.codegenBinding; + // regular local variable read + if (localBinding.constant != NotAConstant) { + codeStream.generateConstant(localBinding.constant, 0); + // no implicit conversion + } else { + // outer local? + if ((bits & DepthMASK) != 0) { + // outer local can be reached either through a synthetic arg or a synthetic field + VariableBinding[] path = currentScope.getEmulationPath(localBinding); + codeStream.generateOuterAccess(path, this, localBinding, currentScope); + } else { + codeStream.load(localBinding); + } + } + } + + // all intermediate field accesses are read accesses + // only the last field binding is a write access + if (this.otherCodegenBindings != null) { + for (int i = 0; i < otherBindingsCount; i++) { + FieldBinding nextField = this.otherCodegenBindings[i]; + if (lastFieldBinding != null) { + needValue = !nextField.isStatic(); + if (needValue) { + MethodBinding accessor = + syntheticReadAccessors == null ? null : syntheticReadAccessors[i]; + if (accessor == null) { + if (lastFieldBinding.constant != NotAConstant) { + if (this.lastFieldBinding != this.codegenBinding && !this.lastFieldBinding.isStatic()) { + codeStream.invokeObjectGetClass(); // perform null check + codeStream.pop(); + } + codeStream.generateConstant(lastFieldBinding.constant, 0); + } else if (lastFieldBinding.isStatic()) { + codeStream.getstatic(lastFieldBinding); + } else { + codeStream.getfield(lastFieldBinding); + } + } else { + codeStream.invokestatic(accessor); + } + } else { + if (this.codegenBinding != this.lastFieldBinding && !this.lastFieldBinding.isStatic()){ + codeStream.invokeObjectGetClass(); // perform null check + codeStream.pop(); + } + } + } + this.lastFieldBinding = nextField; + } + } + } + public void generateReceiver(CodeStream codeStream) { + codeStream.aload_0(); + } + public TypeBinding getOtherFieldBindings(BlockScope scope) { + // At this point restrictiveFlag may ONLY have two potential value : FIELD LOCAL (i.e cast <<(VariableBinding) binding>> is valid) + int length = tokens.length; + if ((bits & FIELD) != 0) { + FieldBinding fieldBinding = (FieldBinding) binding; + if (!fieldBinding.isStatic()) { + //must check for the static status.... + if (indexOfFirstFieldBinding > 1 //accessing to a field using a type as "receiver" is allowed only with static field + || scope.methodScope().isStatic) { // the field is the first token of the qualified reference.... + scope.problemReporter().staticFieldAccessToNonStaticVariable(this, fieldBinding); + return null; + } + } else { + // indirect static reference ? + if (indexOfFirstFieldBinding > 1 + && fieldBinding.declaringClass != actualReceiverType) { + scope.problemReporter().indirectAccessToStaticField(this, fieldBinding); + } + } + // only last field is actually a write access if any + if (isFieldUseDeprecated(fieldBinding, scope, (this.bits & IsStrictlyAssignedMASK) !=0 && indexOfFirstFieldBinding == length)) + scope.problemReporter().deprecatedField(fieldBinding, this); + } + TypeBinding type = ((VariableBinding) binding).type; + int index = indexOfFirstFieldBinding; + if (index == length) { // restrictiveFlag == FIELD + this.constant = FieldReference.getConstantFor((FieldBinding) binding, this, false, scope); + return type; + } + // allocation of the fieldBindings array and its respective constants + int otherBindingsLength = length - index; + otherCodegenBindings = otherBindings = new FieldBinding[otherBindingsLength]; + otherDepths = new int[otherBindingsLength]; + + // fill the first constant (the one of the binding) + this.constant = + ((bits & FIELD) != 0) + ? FieldReference.getConstantFor((FieldBinding) binding, this, false, scope) + : ((VariableBinding) binding).constant; + // save first depth, since will be updated by visibility checks of other bindings + int firstDepth = (bits & DepthMASK) >> DepthSHIFT; + // iteration on each field + while (index < length) { + char[] token = tokens[index]; + if (type == null) + return null; // could not resolve type prior to this point + + bits &= ~DepthMASK; // flush previous depth if any + FieldBinding field = scope.getField(type, token, this); + int place = index - indexOfFirstFieldBinding; + otherBindings[place] = field; + otherDepths[place] = (bits & DepthMASK) >> DepthSHIFT; + if (field.isValidBinding()) { + // only last field is actually a write access if any + if (isFieldUseDeprecated(field, scope, (this.bits & IsStrictlyAssignedMASK) !=0 && index+1 == length)) { + scope.problemReporter().deprecatedField(field, this); + } + Constant someConstant = FieldReference.getConstantFor(field, this, false, scope); + // constant propagation can only be performed as long as the previous one is a constant too. + if (this.constant != NotAConstant) { + this.constant = someConstant; + } + + if (field.isStatic()) { + // static field accessed through receiver? legal but unoptimal (optional warning) + scope.problemReporter().nonStaticAccessToStaticField(this, field); + // indirect static reference ? + if (field.declaringClass != type) { + scope.problemReporter().indirectAccessToStaticField(this, field); + } + } + type = field.type; + index++; + } else { + constant = NotAConstant; //don't fill other constants slots... + scope.problemReporter().invalidField(this, field, index, type); + setDepth(firstDepth); + return null; + } + } + setDepth(firstDepth); + return (otherBindings[otherBindingsLength - 1]).type; + } + public void manageEnclosingInstanceAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo) { + if (!flowInfo.isReachable()) return; + //If inlinable field, forget the access emulation, the code gen will directly target it + if (((bits & DepthMASK) == 0) || (constant != NotAConstant)) { + return; + } + if ((bits & RestrictiveFlagMASK) == LOCAL) { + currentScope.emulateOuterAccess((LocalVariableBinding) binding); + } + } + public void manageSyntheticReadAccessIfNecessary( + BlockScope currentScope, + FieldBinding fieldBinding, + TypeBinding lastReceiverType, + int index, + FlowInfo flowInfo) { + if (!flowInfo.isReachable()) return; + // index == 0 denotes the first fieldBinding, index > 0 denotes one of the 'otherBindings' + if (fieldBinding.constant != NotAConstant) + return; + if (fieldBinding.isPrivate()) { // private access + if (fieldBinding.declaringClass != currentScope.enclosingSourceType()) { + if (syntheticReadAccessors == null) { + if (otherBindings == null) + syntheticReadAccessors = new SyntheticAccessMethodBinding[1]; + else + syntheticReadAccessors = + new SyntheticAccessMethodBinding[otherBindings.length + 1]; + } + syntheticReadAccessors[index] = ((SourceTypeBinding) fieldBinding.declaringClass).addSyntheticMethod(fieldBinding, true); + currentScope.problemReporter().needToEmulateFieldReadAccess(fieldBinding, this); + return; + } + } else if (fieldBinding.isProtected()){ + int depth = index == 0 ? (bits & DepthMASK) >> DepthSHIFT : otherDepths[index-1]; + // implicit protected access (only for first one) + if (depth > 0 && (fieldBinding.declaringClass.getPackage() + != currentScope.enclosingSourceType().getPackage())) { + if (syntheticReadAccessors == null) { + if (otherBindings == null) + syntheticReadAccessors = new SyntheticAccessMethodBinding[1]; + else + syntheticReadAccessors = + new SyntheticAccessMethodBinding[otherBindings.length + 1]; + } + syntheticReadAccessors[index] = + ((SourceTypeBinding) currentScope.enclosingSourceType().enclosingTypeAt(depth)) + .addSyntheticMethod(fieldBinding, true); + currentScope.problemReporter().needToEmulateFieldReadAccess(fieldBinding, this); + return; + } + } + // if the binding declaring class is not visible, need special action + // for runtime compatibility on 1.2 VMs : change the declaring class of the binding + // NOTE: from target 1.2 on, field's declaring class is touched if any different from receiver type + if (fieldBinding.declaringClass != lastReceiverType + && !lastReceiverType.isArrayType() + && fieldBinding.declaringClass != null + && fieldBinding.constant == NotAConstant + && ((currentScope.environment().options.targetJDK >= ClassFileConstants.JDK1_2 + && (index > 0 || indexOfFirstFieldBinding > 1 || !fieldBinding.isStatic()) + && fieldBinding.declaringClass.id != T_Object) + || !fieldBinding.declaringClass.canBeSeenBy(currentScope))){ + if (index == 0){ + this.codegenBinding = currentScope.enclosingSourceType().getUpdatedFieldBinding(fieldBinding, (ReferenceBinding)lastReceiverType); + } else { + if (this.otherCodegenBindings == this.otherBindings){ + int l = this.otherBindings.length; + System.arraycopy(this.otherBindings, 0, this.otherCodegenBindings = new FieldBinding[l], 0, l); + } + this.otherCodegenBindings[index-1] = currentScope.enclosingSourceType().getUpdatedFieldBinding(fieldBinding, (ReferenceBinding)lastReceiverType); + } + } + } + /* + * No need to emulate access to protected fields since not implicitly accessed + */ + public void manageSyntheticWriteAccessIfNecessary( + BlockScope currentScope, + FieldBinding fieldBinding, + TypeBinding lastReceiverType, + FlowInfo flowInfo) { + if (!flowInfo.isReachable()) return; + if (fieldBinding.isPrivate()) { + if (fieldBinding.declaringClass != currentScope.enclosingSourceType()) { + syntheticWriteAccessor = ((SourceTypeBinding) fieldBinding.declaringClass) + .addSyntheticMethod(fieldBinding, false); + currentScope.problemReporter().needToEmulateFieldWriteAccess(fieldBinding, this); + return; + } + } else if (fieldBinding.isProtected()){ + int depth = fieldBinding == binding ? (bits & DepthMASK) >> DepthSHIFT : otherDepths[otherDepths.length-1]; + if (depth > 0 && (fieldBinding.declaringClass.getPackage() + != currentScope.enclosingSourceType().getPackage())) { + syntheticWriteAccessor = ((SourceTypeBinding) currentScope.enclosingSourceType().enclosingTypeAt(depth)) + .addSyntheticMethod(fieldBinding, false); + currentScope.problemReporter().needToEmulateFieldWriteAccess(fieldBinding, this); + return; + } + } + // if the binding declaring class is not visible, need special action + // for runtime compatibility on 1.2 VMs : change the declaring class of the binding + // NOTE: from target 1.2 on, field's declaring class is touched if any different from receiver type + if (fieldBinding.declaringClass != lastReceiverType + && !lastReceiverType.isArrayType() + && fieldBinding.declaringClass != null + && fieldBinding.constant == NotAConstant + && ((currentScope.environment().options.targetJDK >= ClassFileConstants.JDK1_2 + && (fieldBinding != binding || indexOfFirstFieldBinding > 1 || !fieldBinding.isStatic()) + && fieldBinding.declaringClass.id != T_Object) + || !fieldBinding.declaringClass.canBeSeenBy(currentScope))){ + if (fieldBinding == binding){ + this.codegenBinding = currentScope.enclosingSourceType().getUpdatedFieldBinding(fieldBinding, (ReferenceBinding)lastReceiverType); + } else { + if (this.otherCodegenBindings == this.otherBindings){ + int l = this.otherBindings.length; + System.arraycopy(this.otherBindings, 0, this.otherCodegenBindings = new FieldBinding[l], 0, l); + } + this.otherCodegenBindings[this.otherCodegenBindings.length-1] = currentScope.enclosingSourceType().getUpdatedFieldBinding(fieldBinding, (ReferenceBinding)lastReceiverType); + } + } + + } + + public StringBuffer printExpression(int indent, StringBuffer output) { + + for (int i = 0; i < tokens.length; i++) { + if (i > 0) output.append('.'); + output.append(tokens[i]); + } + return output; + } + + /** + * Normal field binding did not work, try to bind to a field of the delegate receiver. + */ + public TypeBinding reportError(BlockScope scope) { + if (binding instanceof ProblemFieldBinding) { + scope.problemReporter().invalidField(this, (FieldBinding) binding); + } else if (binding instanceof ProblemReferenceBinding) { + scope.problemReporter().invalidType(this, (TypeBinding) binding); + } else { + scope.problemReporter().unresolvableReference(this, binding); + } + return null; + } + public TypeBinding resolveType(BlockScope scope) { + // field and/or local are done before type lookups + // the only available value for the restrictiveFlag BEFORE + // the TC is Flag_Type Flag_LocalField and Flag_TypeLocalField + this.actualReceiverType = this.receiverType = scope.enclosingSourceType(); + constant = Constant.NotAConstant; + if ((this.codegenBinding = this.binding = scope.getBinding(tokens, bits & RestrictiveFlagMASK, this, true /*resolve*/)).isValidBinding()) { + switch (bits & RestrictiveFlagMASK) { + case VARIABLE : //============only variable=========== + case TYPE | VARIABLE : + if (binding instanceof LocalVariableBinding) { + if (!((LocalVariableBinding) binding).isFinal() && ((bits & DepthMASK) != 0)) + scope.problemReporter().cannotReferToNonFinalOuterLocal( + (LocalVariableBinding) binding, + this); + bits &= ~RestrictiveFlagMASK; // clear bits + bits |= LOCAL; + return this.resolvedType = getOtherFieldBindings(scope); + } + if (binding instanceof FieldBinding) { + // check for forward references + FieldBinding fieldBinding = (FieldBinding) binding; + MethodScope methodScope = scope.methodScope(); + if (methodScope.enclosingSourceType() == fieldBinding.declaringClass + && methodScope.lastVisibleFieldID >= 0 + && fieldBinding.id >= methodScope.lastVisibleFieldID) { + if ((!fieldBinding.isStatic() || methodScope.isStatic) + && this.indexOfFirstFieldBinding == 1) { + scope.problemReporter().forwardReference(this, 0, scope.enclosingSourceType()); + } + } + if (!fieldBinding.isStatic() + && this.indexOfFirstFieldBinding == 1 + && scope.environment().options.getSeverity(CompilerOptions.UnqualifiedFieldAccess) != ProblemSeverities.Ignore) { + scope.problemReporter().unqualifiedFieldAccess(this, fieldBinding); + } + bits &= ~RestrictiveFlagMASK; // clear bits + bits |= FIELD; + + // check for deprecated receiver type + // deprecation check for receiver type if not first token + if (indexOfFirstFieldBinding > 1) { + if (isTypeUseDeprecated(this.actualReceiverType, scope)) + scope.problemReporter().deprecatedType(this.actualReceiverType, this); + } + + return this.resolvedType = getOtherFieldBindings(scope); + } + // thus it was a type + bits &= ~RestrictiveFlagMASK; // clear bits + bits |= TYPE; + case TYPE : //=============only type ============== + if (isTypeUseDeprecated((TypeBinding) binding, scope)) + scope.problemReporter().deprecatedType((TypeBinding) binding, this); + return this.resolvedType = (TypeBinding) binding; + } + } + //========error cases=============== + return this.resolvedType = this.reportError(scope); + } + + public void setFieldIndex(int index) { + this.indexOfFirstFieldBinding = index; + } + + public void traverse(ASTVisitor visitor, BlockScope scope) { + visitor.visit(this, scope); + visitor.endVisit(this, scope); + } + public String unboundReferenceErrorName() { + return new String(tokens[0]); + } +}