0001: /*******************************************************************************
0002: * Copyright (c) 2000, 2007 IBM Corporation and others.
0003: * All rights reserved. This program and the accompanying materials
0004: * are made available under the terms of the Eclipse Public License v1.0
0005: * which accompanies this distribution, and is available at
0006: * http://www.eclipse.org/legal/epl-v10.html
0007: *
0008: * Contributors:
0009: * IBM Corporation - initial API and implementation
0010: *******************************************************************************/package org.eclipse.jdt.internal.compiler.ast;
0011:
0012: import org.eclipse.jdt.internal.compiler.ASTVisitor;
0013: import org.eclipse.jdt.internal.compiler.impl.*;
0014: import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
0015: import org.eclipse.jdt.internal.compiler.codegen.*;
0016: import org.eclipse.jdt.internal.compiler.flow.*;
0017: import org.eclipse.jdt.internal.compiler.lookup.*;
0018: import org.eclipse.jdt.internal.compiler.problem.ProblemSeverities;
0019:
0020: public class SingleNameReference extends NameReference implements
0021: OperatorIds {
0022:
0023: public static final int READ = 0;
0024: public static final int WRITE = 1;
0025: public char[] token;
0026: public MethodBinding[] syntheticAccessors; // [0]=read accessor [1]=write accessor
0027: public TypeBinding genericCast;
0028:
0029: public SingleNameReference(char[] source, long pos) {
0030: super ();
0031: token = source;
0032: sourceStart = (int) (pos >>> 32);
0033: sourceEnd = (int) pos;
0034: }
0035:
0036: public FlowInfo analyseAssignment(BlockScope currentScope,
0037: FlowContext flowContext, FlowInfo flowInfo,
0038: Assignment assignment, boolean isCompound) {
0039:
0040: boolean isReachable = (flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0;
0041: // compound assignment extra work
0042: if (isCompound) { // check the variable part is initialized if blank final
0043: switch (bits & RestrictiveFlagMASK) {
0044: case Binding.FIELD: // reading a field
0045: FieldBinding fieldBinding;
0046: if ((fieldBinding = (FieldBinding) binding)
0047: .isBlankFinal()
0048: && currentScope
0049: .needBlankFinalFieldInitializationCheck(fieldBinding)) {
0050: if (!flowInfo.isDefinitelyAssigned(fieldBinding)) {
0051: currentScope.problemReporter()
0052: .uninitializedBlankFinalField(
0053: fieldBinding, this );
0054: }
0055: }
0056: manageSyntheticAccessIfNecessary(currentScope,
0057: flowInfo, true /*read-access*/);
0058: break;
0059: case Binding.LOCAL: // reading a local variable
0060: // check if assigning a final blank field
0061: LocalVariableBinding localBinding;
0062: if (!flowInfo
0063: .isDefinitelyAssigned(localBinding = (LocalVariableBinding) binding)) {
0064: currentScope.problemReporter()
0065: .uninitializedLocalVariable(localBinding,
0066: this );
0067: // we could improve error msg here telling "cannot use compound assignment on final local variable"
0068: }
0069: if (isReachable) {
0070: localBinding.useFlag = LocalVariableBinding.USED;
0071: } else if (localBinding.useFlag == LocalVariableBinding.UNUSED) {
0072: localBinding.useFlag = LocalVariableBinding.FAKE_USED;
0073: }
0074: }
0075: }
0076: if (assignment.expression != null) {
0077: flowInfo = assignment.expression.analyseCode(currentScope,
0078: flowContext, flowInfo).unconditionalInits();
0079: }
0080: switch (bits & RestrictiveFlagMASK) {
0081: case Binding.FIELD: // assigning to a field
0082: manageSyntheticAccessIfNecessary(currentScope, flowInfo,
0083: false /*write-access*/);
0084:
0085: FieldBinding fieldBinding = (FieldBinding) binding;
0086: ReferenceBinding declaringClass = fieldBinding.declaringClass;
0087: // check if accessing enum static field in initializer
0088: if (declaringClass.isEnum()) {
0089: MethodScope methodScope = currentScope.methodScope();
0090: SourceTypeBinding sourceType = currentScope
0091: .enclosingSourceType();
0092: if (fieldBinding.isStatic()
0093: && this .constant == Constant.NotAConstant
0094: && !methodScope.isStatic
0095: && (sourceType == declaringClass || sourceType.super class == declaringClass) // enum constant body
0096: && methodScope
0097: .isInsideInitializerOrConstructor()) {
0098: currentScope.problemReporter()
0099: .enumStaticFieldUsedDuringInitialization(
0100: fieldBinding, this );
0101: }
0102: }
0103: // check if assigning a final field
0104: if (fieldBinding.isFinal()) {
0105: // inside a context where allowed
0106: if (!isCompound
0107: && fieldBinding.isBlankFinal()
0108: && currentScope
0109: .allowBlankFinalFieldAssignment(fieldBinding)) {
0110: if (flowInfo.isPotentiallyAssigned(fieldBinding)) {
0111: currentScope
0112: .problemReporter()
0113: .duplicateInitializationOfBlankFinalField(
0114: fieldBinding, this );
0115: } else {
0116: flowContext.recordSettingFinal(fieldBinding,
0117: this , flowInfo);
0118: }
0119: flowInfo.markAsDefinitelyAssigned(fieldBinding);
0120: } else {
0121: currentScope.problemReporter()
0122: .cannotAssignToFinalField(fieldBinding,
0123: this );
0124: }
0125: }
0126: break;
0127: case Binding.LOCAL: // assigning to a local variable
0128: LocalVariableBinding localBinding = (LocalVariableBinding) binding;
0129: if (!flowInfo.isDefinitelyAssigned(localBinding)) {// for local variable debug attributes
0130: bits |= FirstAssignmentToLocal;
0131: } else {
0132: bits &= ~FirstAssignmentToLocal;
0133: }
0134: if (localBinding.isFinal()) {
0135: if ((bits & DepthMASK) == 0) {
0136: // tolerate assignment to final local in unreachable code (45674)
0137: if ((isReachable && isCompound)
0138: || !localBinding.isBlankFinal()) {
0139: currentScope.problemReporter()
0140: .cannotAssignToFinalLocal(localBinding,
0141: this );
0142: } else if (flowInfo
0143: .isPotentiallyAssigned(localBinding)) {
0144: currentScope.problemReporter()
0145: .duplicateInitializationOfFinalLocal(
0146: localBinding, this );
0147: } else {
0148: flowContext.recordSettingFinal(localBinding,
0149: this , flowInfo);
0150: }
0151: } else {
0152: currentScope.problemReporter()
0153: .cannotAssignToFinalOuterLocal(
0154: localBinding, this );
0155: }
0156: } else /* avoid double diagnostic */if ((localBinding.tagBits & TagBits.IsArgument) != 0) {
0157: currentScope.problemReporter().parameterAssignment(
0158: localBinding, this );
0159: }
0160: flowInfo.markAsDefinitelyAssigned(localBinding);
0161: }
0162: manageEnclosingInstanceAccessIfNecessary(currentScope, flowInfo);
0163: return flowInfo;
0164: }
0165:
0166: public FlowInfo analyseCode(BlockScope currentScope,
0167: FlowContext flowContext, FlowInfo flowInfo) {
0168: return analyseCode(currentScope, flowContext, flowInfo, true);
0169: }
0170:
0171: public FlowInfo analyseCode(BlockScope currentScope,
0172: FlowContext flowContext, FlowInfo flowInfo,
0173: boolean valueRequired) {
0174:
0175: switch (bits & RestrictiveFlagMASK) {
0176: case Binding.FIELD: // reading a field
0177: if (valueRequired
0178: || currentScope.compilerOptions().complianceLevel >= ClassFileConstants.JDK1_4) {
0179: manageSyntheticAccessIfNecessary(currentScope,
0180: flowInfo, true /*read-access*/);
0181: }
0182: FieldBinding fieldBinding = (FieldBinding) binding;
0183: ReferenceBinding declaringClass = fieldBinding.declaringClass;
0184: // check if accessing enum static field in initializer
0185: if (declaringClass.isEnum()) {
0186: MethodScope methodScope = currentScope.methodScope();
0187: SourceTypeBinding sourceType = currentScope
0188: .enclosingSourceType();
0189: if (fieldBinding.isStatic()
0190: && this .constant == Constant.NotAConstant
0191: && !methodScope.isStatic
0192: && (sourceType == declaringClass || sourceType.super class == declaringClass) // enum constant body
0193: && methodScope
0194: .isInsideInitializerOrConstructor()) {
0195: currentScope.problemReporter()
0196: .enumStaticFieldUsedDuringInitialization(
0197: fieldBinding, this );
0198: }
0199: }
0200: // check if reading a final blank field
0201: if (fieldBinding.isBlankFinal()
0202: && currentScope
0203: .needBlankFinalFieldInitializationCheck(fieldBinding)) {
0204: if (!flowInfo.isDefinitelyAssigned(fieldBinding)) {
0205: currentScope.problemReporter()
0206: .uninitializedBlankFinalField(fieldBinding,
0207: this );
0208: }
0209: }
0210: break;
0211: case Binding.LOCAL: // reading a local variable
0212: LocalVariableBinding localBinding;
0213: if (!flowInfo
0214: .isDefinitelyAssigned(localBinding = (LocalVariableBinding) binding)) {
0215: currentScope.problemReporter()
0216: .uninitializedLocalVariable(localBinding, this );
0217: }
0218: if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0) {
0219: localBinding.useFlag = LocalVariableBinding.USED;
0220: } else if (localBinding.useFlag == LocalVariableBinding.UNUSED) {
0221: localBinding.useFlag = LocalVariableBinding.FAKE_USED;
0222: }
0223: }
0224: if (valueRequired) {
0225: manageEnclosingInstanceAccessIfNecessary(currentScope,
0226: flowInfo);
0227: }
0228: return flowInfo;
0229: }
0230:
0231: public TypeBinding checkFieldAccess(BlockScope scope) {
0232:
0233: FieldBinding fieldBinding = (FieldBinding) binding;
0234:
0235: bits &= ~RestrictiveFlagMASK; // clear bits
0236: bits |= Binding.FIELD;
0237: MethodScope methodScope = scope.methodScope();
0238: boolean isStatic = fieldBinding.isStatic();
0239: if (!isStatic) {
0240: // must check for the static status....
0241: if (methodScope.isStatic) {
0242: scope.problemReporter()
0243: .staticFieldAccessToNonStaticVariable(this ,
0244: fieldBinding);
0245: this .constant = Constant.NotAConstant;
0246: return fieldBinding.type;
0247: }
0248: }
0249: this .constant = fieldBinding.constant();
0250:
0251: if (isFieldUseDeprecated(fieldBinding, scope,
0252: (this .bits & IsStrictlyAssigned) != 0))
0253: scope.problemReporter().deprecatedField(fieldBinding, this );
0254:
0255: if ((this .bits & IsStrictlyAssigned) == 0
0256: && methodScope.enclosingSourceType() == fieldBinding
0257: .original().declaringClass
0258: && methodScope.lastVisibleFieldID >= 0
0259: && fieldBinding.id >= methodScope.lastVisibleFieldID
0260: && (!fieldBinding.isStatic() || methodScope.isStatic)) {
0261: scope.problemReporter().forwardReference(this , 0,
0262: methodScope.enclosingSourceType());
0263: this .bits |= ASTNode.IgnoreNoEffectAssignCheck;
0264: }
0265: return fieldBinding.type;
0266:
0267: }
0268:
0269: /**
0270: * @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)
0271: */
0272: public void computeConversion(Scope scope,
0273: TypeBinding runtimeTimeType, TypeBinding compileTimeType) {
0274: if (runtimeTimeType == null || compileTimeType == null)
0275: return;
0276: if ((bits & Binding.FIELD) != 0 && this .binding != null
0277: && this .binding.isValidBinding()) {
0278: // set the generic cast after the fact, once the type expectation is fully known (no need for strict cast)
0279: FieldBinding field = (FieldBinding) this .binding;
0280: FieldBinding originalBinding = field.original();
0281: TypeBinding originalType = originalBinding.type;
0282: // extra cast needed if method return type is type variable
0283: if (originalBinding != field
0284: && originalType != field.type
0285: && runtimeTimeType.id != T_JavaLangObject
0286: && (originalType.tagBits & TagBits.HasTypeVariable) != 0) {
0287: TypeBinding targetType = (!compileTimeType.isBaseType() && runtimeTimeType
0288: .isBaseType()) ? compileTimeType // unboxing: checkcast before conversion
0289: : runtimeTimeType;
0290: this .genericCast = originalType.genericCast(scope
0291: .boxing(targetType));
0292: }
0293: }
0294: super
0295: .computeConversion(scope, runtimeTimeType,
0296: compileTimeType);
0297: }
0298:
0299: public void generateAssignment(BlockScope currentScope,
0300: CodeStream codeStream, Assignment assignment,
0301: boolean valueRequired) {
0302:
0303: // optimizing assignment like: i = i + 1 or i = 1 + i
0304: if (assignment.expression.isCompactableOperation()) {
0305: BinaryExpression operation = (BinaryExpression) assignment.expression;
0306: int operator = (operation.bits & OperatorMASK) >> OperatorSHIFT;
0307: SingleNameReference variableReference;
0308: if ((operation.left instanceof SingleNameReference)
0309: && ((variableReference = (SingleNameReference) operation.left).binding == binding)) {
0310: // i = i + value, then use the variable on the right hand side, since it has the correct implicit conversion
0311: variableReference.generateCompoundAssignment(
0312: currentScope, codeStream,
0313: syntheticAccessors == null ? null
0314: : syntheticAccessors[WRITE],
0315: operation.right, operator,
0316: operation.implicitConversion, valueRequired);
0317: if (valueRequired) {
0318: codeStream
0319: .generateImplicitConversion(assignment.implicitConversion);
0320: }
0321: return;
0322: }
0323: if ((operation.right instanceof SingleNameReference)
0324: && ((operator == PLUS) || (operator == MULTIPLY)) // only commutative operations
0325: && ((variableReference = (SingleNameReference) operation.right).binding == binding)
0326: && (operation.left.constant != Constant.NotAConstant) // exclude non constant expressions, since could have side-effect
0327: && (((operation.left.implicitConversion & IMPLICIT_CONVERSION_MASK) >> 4) != T_JavaLangString) // exclude string concatenation which would occur backwards
0328: && (((operation.right.implicitConversion & IMPLICIT_CONVERSION_MASK) >> 4) != T_JavaLangString)) { // exclude string concatenation which would occur backwards
0329: // i = value + i, then use the variable on the right hand side, since it has the correct implicit conversion
0330: variableReference.generateCompoundAssignment(
0331: currentScope, codeStream,
0332: syntheticAccessors == null ? null
0333: : syntheticAccessors[WRITE],
0334: operation.left, operator,
0335: operation.implicitConversion, valueRequired);
0336: if (valueRequired) {
0337: codeStream
0338: .generateImplicitConversion(assignment.implicitConversion);
0339: }
0340: return;
0341: }
0342: }
0343: switch (bits & RestrictiveFlagMASK) {
0344: case Binding.FIELD: // assigning to a field
0345: FieldBinding fieldBinding;
0346: int pc = codeStream.position;
0347: if (!(fieldBinding = (FieldBinding) this .codegenBinding)
0348: .isStatic()) { // need a receiver?
0349: if ((bits & DepthMASK) != 0) {
0350: ReferenceBinding targetType = currentScope
0351: .enclosingSourceType().enclosingTypeAt(
0352: (bits & DepthMASK) >> DepthSHIFT);
0353: Object[] emulationPath = currentScope
0354: .getEmulationPath(targetType,
0355: true /*only exact match*/, false/*consider enclosing arg*/);
0356: codeStream.generateOuterAccess(emulationPath, this ,
0357: targetType, currentScope);
0358: } else {
0359: this .generateReceiver(codeStream);
0360: }
0361: }
0362: codeStream.recordPositionsFrom(pc, this .sourceStart);
0363: assignment.expression.generateCode(currentScope,
0364: codeStream, true);
0365: fieldStore(codeStream, fieldBinding,
0366: syntheticAccessors == null ? null
0367: : syntheticAccessors[WRITE], valueRequired);
0368: if (valueRequired) {
0369: codeStream
0370: .generateImplicitConversion(assignment.implicitConversion);
0371: }
0372: // no need for generic cast as value got dupped
0373: return;
0374: case Binding.LOCAL: // assigning to a local variable
0375: LocalVariableBinding localBinding = (LocalVariableBinding) this .codegenBinding;
0376: if (localBinding.resolvedPosition != -1) {
0377: assignment.expression.generateCode(currentScope,
0378: codeStream, true);
0379: } else {
0380: if (assignment.expression.constant != Constant.NotAConstant) {
0381: // assigning an unused local to a constant value = no actual assignment is necessary
0382: if (valueRequired) {
0383: codeStream.generateConstant(
0384: assignment.expression.constant,
0385: assignment.implicitConversion);
0386: }
0387: } else {
0388: assignment.expression.generateCode(currentScope,
0389: codeStream, true);
0390: /* Even though the value may not be required, we force it to be produced, and discard it later
0391: on if it was actually not necessary, so as to provide the same behavior as JDK1.2beta3. */
0392: if (valueRequired) {
0393: codeStream
0394: .generateImplicitConversion(assignment.implicitConversion); // implicit conversion
0395: } else {
0396: if ((localBinding.type == TypeBinding.LONG)
0397: || (localBinding.type == TypeBinding.DOUBLE)) {
0398: codeStream.pop2();
0399: } else {
0400: codeStream.pop();
0401: }
0402: }
0403: }
0404: return;
0405: }
0406: // 26903, need extra cast to store null in array local var
0407: if (localBinding.type.isArrayType()
0408: && (assignment.expression.resolvedType == TypeBinding.NULL // arrayLoc = null
0409: || ((assignment.expression instanceof CastExpression) // arrayLoc = (type[])null
0410: && (((CastExpression) assignment.expression)
0411: .innermostCastedExpression().resolvedType == TypeBinding.NULL)))) {
0412: codeStream.checkcast(localBinding.type);
0413: }
0414:
0415: // normal local assignment (since cannot store in outer local which are final locations)
0416: codeStream.store(localBinding, valueRequired);
0417: if ((bits & FirstAssignmentToLocal) != 0) { // for local variable debug attributes
0418: localBinding
0419: .recordInitializationStartPC(codeStream.position);
0420: }
0421: // implicit conversion
0422: if (valueRequired) {
0423: codeStream
0424: .generateImplicitConversion(assignment.implicitConversion);
0425: }
0426: }
0427: }
0428:
0429: public void generateCode(BlockScope currentScope,
0430: CodeStream codeStream, boolean valueRequired) {
0431: int pc = codeStream.position;
0432: if (constant != Constant.NotAConstant) {
0433: if (valueRequired) {
0434: codeStream.generateConstant(constant,
0435: implicitConversion);
0436: }
0437: codeStream.recordPositionsFrom(pc, this .sourceStart);
0438: return;
0439: } else {
0440: switch (bits & RestrictiveFlagMASK) {
0441: case Binding.FIELD: // reading a field
0442: FieldBinding fieldBinding = (FieldBinding) this .codegenBinding;
0443: Constant fieldConstant = fieldBinding.constant();
0444: if (fieldConstant != Constant.NotAConstant) {
0445: // directly use inlined value for constant fields
0446: if (valueRequired) {
0447: codeStream.generateConstant(fieldConstant,
0448: implicitConversion);
0449: }
0450: codeStream
0451: .recordPositionsFrom(pc, this .sourceStart);
0452: return;
0453: }
0454: if (fieldBinding.isStatic()) {
0455: if (!valueRequired
0456: // if no valueRequired, still need possible side-effects of <clinit> invocation, if field belongs to different class
0457: && ((FieldBinding) binding).original().declaringClass == this .actualReceiverType
0458: .erasure()
0459: && ((implicitConversion & TypeIds.UNBOXING) == 0)
0460: && this .genericCast == null) {
0461: // if no valueRequired, optimize out entire gen
0462: codeStream.recordPositionsFrom(pc,
0463: this .sourceStart);
0464: return;
0465: }
0466: // managing private access
0467: if ((syntheticAccessors == null)
0468: || (syntheticAccessors[READ] == null)) {
0469: codeStream.getstatic(fieldBinding);
0470: } else {
0471: codeStream
0472: .invokestatic(syntheticAccessors[READ]);
0473: }
0474: } else {
0475: if (!valueRequired
0476: && (implicitConversion & TypeIds.UNBOXING) == 0
0477: && this .genericCast == null) {
0478: // if no valueRequired, optimize out entire gen
0479: codeStream.recordPositionsFrom(pc,
0480: this .sourceStart);
0481: return;
0482: }
0483: // managing enclosing instance access
0484: if ((bits & DepthMASK) != 0) {
0485: ReferenceBinding targetType = currentScope
0486: .enclosingSourceType()
0487: .enclosingTypeAt(
0488: (bits & DepthMASK) >> DepthSHIFT);
0489: Object[] emulationPath = currentScope
0490: .getEmulationPath(targetType,
0491: true /*only exact match*/,
0492: false/*consider enclosing arg*/);
0493: codeStream.generateOuterAccess(emulationPath,
0494: this , targetType, currentScope);
0495: } else {
0496: generateReceiver(codeStream);
0497: }
0498: // managing private access
0499: if ((syntheticAccessors == null)
0500: || (syntheticAccessors[READ] == null)) {
0501: codeStream.getfield(fieldBinding);
0502: } else {
0503: codeStream
0504: .invokestatic(syntheticAccessors[READ]);
0505: }
0506: }
0507: break;
0508: case Binding.LOCAL: // reading a local
0509: LocalVariableBinding localBinding = (LocalVariableBinding) this .codegenBinding;
0510: if (!valueRequired
0511: && (implicitConversion & TypeIds.UNBOXING) == 0) {
0512: // if no valueRequired, optimize out entire gen
0513: codeStream
0514: .recordPositionsFrom(pc, this .sourceStart);
0515: return;
0516: }
0517: // outer local?
0518: if ((bits & DepthMASK) != 0) {
0519: // outer local can be reached either through a synthetic arg or a synthetic field
0520: VariableBinding[] path = currentScope
0521: .getEmulationPath(localBinding);
0522: codeStream.generateOuterAccess(path, this ,
0523: localBinding, currentScope);
0524: } else {
0525: // regular local variable read
0526: codeStream.load(localBinding);
0527: }
0528: break;
0529: default: // type
0530: codeStream.recordPositionsFrom(pc, this .sourceStart);
0531: return;
0532: }
0533: }
0534: // required cast must occur even if no value is required
0535: if (this .genericCast != null)
0536: codeStream.checkcast(this .genericCast);
0537: if (valueRequired) {
0538: codeStream.generateImplicitConversion(implicitConversion);
0539: } else {
0540: boolean isUnboxing = (implicitConversion & TypeIds.UNBOXING) != 0;
0541: // conversion only generated if unboxing
0542: if (isUnboxing)
0543: codeStream
0544: .generateImplicitConversion(implicitConversion);
0545: switch (isUnboxing ? postConversionType(currentScope).id
0546: : this .resolvedType.id) {
0547: case T_long:
0548: case T_double:
0549: codeStream.pop2();
0550: break;
0551: default:
0552: codeStream.pop();
0553: }
0554: }
0555: codeStream.recordPositionsFrom(pc, this .sourceStart);
0556: }
0557:
0558: /*
0559: * Regular API for compound assignment, relies on the fact that there is only one reference to the
0560: * variable, which carries both synthetic read/write accessors.
0561: * The APIs with an extra argument is used whenever there are two references to the same variable which
0562: * are optimized in one access: e.g "a = a + 1" optimized into "a++".
0563: */
0564: public void generateCompoundAssignment(BlockScope currentScope,
0565: CodeStream codeStream, Expression expression, int operator,
0566: int assignmentImplicitConversion, boolean valueRequired) {
0567:
0568: this .generateCompoundAssignment(currentScope, codeStream,
0569: syntheticAccessors == null ? null
0570: : syntheticAccessors[WRITE], expression,
0571: operator, assignmentImplicitConversion, valueRequired);
0572: }
0573:
0574: /*
0575: * The APIs with an extra argument is used whenever there are two references to the same variable which
0576: * are optimized in one access: e.g "a = a + 1" optimized into "a++".
0577: */
0578: public void generateCompoundAssignment(BlockScope currentScope,
0579: CodeStream codeStream, MethodBinding writeAccessor,
0580: Expression expression, int operator,
0581: int assignmentImplicitConversion, boolean valueRequired) {
0582: switch (bits & RestrictiveFlagMASK) {
0583: case Binding.FIELD: // assigning to a field
0584: FieldBinding fieldBinding;
0585: if ((fieldBinding = (FieldBinding) this .codegenBinding)
0586: .isStatic()) {
0587: if ((syntheticAccessors == null)
0588: || (syntheticAccessors[READ] == null)) {
0589: codeStream.getstatic(fieldBinding);
0590: } else {
0591: codeStream.invokestatic(syntheticAccessors[READ]);
0592: }
0593: } else {
0594: if ((bits & DepthMASK) != 0) {
0595: ReferenceBinding targetType = currentScope
0596: .enclosingSourceType().enclosingTypeAt(
0597: (bits & DepthMASK) >> DepthSHIFT);
0598: Object[] emulationPath = currentScope
0599: .getEmulationPath(targetType,
0600: true /*only exact match*/, false/*consider enclosing arg*/);
0601: codeStream.generateOuterAccess(emulationPath, this ,
0602: targetType, currentScope);
0603: } else {
0604: codeStream.aload_0();
0605: }
0606: codeStream.dup();
0607: if ((syntheticAccessors == null)
0608: || (syntheticAccessors[READ] == null)) {
0609: codeStream.getfield(fieldBinding);
0610: } else {
0611: codeStream.invokestatic(syntheticAccessors[READ]);
0612: }
0613: }
0614: break;
0615: case Binding.LOCAL: // assigning to a local variable (cannot assign to outer local)
0616: LocalVariableBinding localBinding = (LocalVariableBinding) this .codegenBinding;
0617: Constant assignConstant;
0618: int increment;
0619: // using incr bytecode if possible
0620: switch (localBinding.type.id) {
0621: case T_JavaLangString:
0622: codeStream.generateStringConcatenationAppend(
0623: currentScope, this , expression);
0624: if (valueRequired) {
0625: codeStream.dup();
0626: }
0627: codeStream.store(localBinding, false);
0628: return;
0629: case T_int:
0630: if (((assignConstant = expression.constant) != Constant.NotAConstant)
0631: && (assignConstant.typeID() != T_float) // only for integral types
0632: && (assignConstant.typeID() != T_double)
0633: && ((increment = assignConstant.intValue()) == (short) increment)) { // 16 bits value
0634: switch (operator) {
0635: case PLUS:
0636: codeStream.iinc(localBinding.resolvedPosition,
0637: increment);
0638: if (valueRequired) {
0639: codeStream.load(localBinding);
0640: }
0641: return;
0642: case MINUS:
0643: codeStream.iinc(localBinding.resolvedPosition,
0644: -increment);
0645: if (valueRequired) {
0646: codeStream.load(localBinding);
0647: }
0648: return;
0649: }
0650: }
0651: default:
0652: codeStream.load(localBinding);
0653: }
0654: }
0655: // perform the actual compound operation
0656: int operationTypeID;
0657: switch (operationTypeID = (implicitConversion & IMPLICIT_CONVERSION_MASK) >> 4) {
0658: case T_JavaLangString:
0659: case T_JavaLangObject:
0660: case T_undefined:
0661: // we enter here if the single name reference is a field of type java.lang.String or if the type of the
0662: // operation is java.lang.Object
0663: // For example: o = o + ""; // where the compiled type of o is java.lang.Object.
0664: codeStream.generateStringConcatenationAppend(currentScope,
0665: null, expression);
0666: // no need for generic cast on previous #getfield since using Object string buffer methods.
0667: break;
0668: default:
0669: // promote the array reference to the suitable operation type
0670: if (this .genericCast != null)
0671: codeStream.checkcast(this .genericCast);
0672: codeStream
0673: .generateImplicitConversion(this .implicitConversion);
0674: // generate the increment value (will by itself be promoted to the operation value)
0675: if (expression == IntLiteral.One) { // prefix operation
0676: codeStream.generateConstant(expression.constant,
0677: this .implicitConversion);
0678: } else {
0679: expression.generateCode(currentScope, codeStream, true);
0680: }
0681: // perform the operation
0682: codeStream.sendOperator(operator, operationTypeID);
0683: // cast the value back to the array reference type
0684: codeStream
0685: .generateImplicitConversion(assignmentImplicitConversion);
0686: }
0687: // store the result back into the variable
0688: switch (bits & RestrictiveFlagMASK) {
0689: case Binding.FIELD: // assigning to a field
0690: fieldStore(codeStream, (FieldBinding) this .codegenBinding,
0691: writeAccessor, valueRequired);
0692: // no need for generic cast as value got dupped
0693: return;
0694: case Binding.LOCAL: // assigning to a local variable
0695: LocalVariableBinding localBinding = (LocalVariableBinding) this .codegenBinding;
0696: if (valueRequired) {
0697: if ((localBinding.type == TypeBinding.LONG)
0698: || (localBinding.type == TypeBinding.DOUBLE)) {
0699: codeStream.dup2();
0700: } else {
0701: codeStream.dup();
0702: }
0703: }
0704: codeStream.store(localBinding, false);
0705: }
0706: }
0707:
0708: public void generatePostIncrement(BlockScope currentScope,
0709: CodeStream codeStream, CompoundAssignment postIncrement,
0710: boolean valueRequired) {
0711: switch (bits & RestrictiveFlagMASK) {
0712: case Binding.FIELD: // assigning to a field
0713: FieldBinding fieldBinding;
0714: if ((fieldBinding = (FieldBinding) this .codegenBinding)
0715: .isStatic()) {
0716: if ((syntheticAccessors == null)
0717: || (syntheticAccessors[READ] == null)) {
0718: codeStream.getstatic(fieldBinding);
0719: } else {
0720: codeStream.invokestatic(syntheticAccessors[READ]);
0721: }
0722: } else {
0723: if ((bits & DepthMASK) != 0) {
0724: ReferenceBinding targetType = currentScope
0725: .enclosingSourceType().enclosingTypeAt(
0726: (bits & DepthMASK) >> DepthSHIFT);
0727: Object[] emulationPath = currentScope
0728: .getEmulationPath(targetType,
0729: true /*only exact match*/, false/*consider enclosing arg*/);
0730: codeStream.generateOuterAccess(emulationPath, this ,
0731: targetType, currentScope);
0732: } else {
0733: codeStream.aload_0();
0734: }
0735: codeStream.dup();
0736: if ((syntheticAccessors == null)
0737: || (syntheticAccessors[READ] == null)) {
0738: codeStream.getfield(fieldBinding);
0739: } else {
0740: codeStream.invokestatic(syntheticAccessors[READ]);
0741: }
0742: }
0743: if (valueRequired) {
0744: if (fieldBinding.isStatic()) {
0745: if ((fieldBinding.type == TypeBinding.LONG)
0746: || (fieldBinding.type == TypeBinding.DOUBLE)) {
0747: codeStream.dup2();
0748: } else {
0749: codeStream.dup();
0750: }
0751: } else { // Stack: [owner][old field value] ---> [old field value][owner][old field value]
0752: if ((fieldBinding.type == TypeBinding.LONG)
0753: || (fieldBinding.type == TypeBinding.DOUBLE)) {
0754: codeStream.dup2_x1();
0755: } else {
0756: codeStream.dup_x1();
0757: }
0758: }
0759: }
0760: if (this .genericCast != null)
0761: codeStream.checkcast(this .genericCast);
0762: codeStream
0763: .generateImplicitConversion(this .implicitConversion);
0764: codeStream.generateConstant(
0765: postIncrement.expression.constant,
0766: this .implicitConversion);
0767: codeStream.sendOperator(postIncrement.operator,
0768: this .implicitConversion & COMPILE_TYPE_MASK);
0769: codeStream
0770: .generateImplicitConversion(postIncrement.preAssignImplicitConversion);
0771: fieldStore(codeStream, fieldBinding,
0772: this .syntheticAccessors == null ? null
0773: : this .syntheticAccessors[WRITE], false);
0774: // no need for generic cast
0775: return;
0776: case Binding.LOCAL: // assigning to a local variable
0777: LocalVariableBinding localBinding = (LocalVariableBinding) this .codegenBinding;
0778: // using incr bytecode if possible
0779: if (localBinding.type == TypeBinding.INT) {
0780: if (valueRequired) {
0781: codeStream.load(localBinding);
0782: }
0783: if (postIncrement.operator == PLUS) {
0784: codeStream.iinc(localBinding.resolvedPosition, 1);
0785: } else {
0786: codeStream.iinc(localBinding.resolvedPosition, -1);
0787: }
0788: } else {
0789: codeStream.load(localBinding);
0790: if (valueRequired) {
0791: if ((localBinding.type == TypeBinding.LONG)
0792: || (localBinding.type == TypeBinding.DOUBLE)) {
0793: codeStream.dup2();
0794: } else {
0795: codeStream.dup();
0796: }
0797: }
0798: codeStream
0799: .generateImplicitConversion(implicitConversion);
0800: codeStream.generateConstant(
0801: postIncrement.expression.constant,
0802: implicitConversion);
0803: codeStream.sendOperator(postIncrement.operator,
0804: this .implicitConversion & COMPILE_TYPE_MASK);
0805: codeStream
0806: .generateImplicitConversion(postIncrement.preAssignImplicitConversion);
0807:
0808: codeStream.store(localBinding, false);
0809: }
0810: }
0811: }
0812:
0813: public void generateReceiver(CodeStream codeStream) {
0814: codeStream.aload_0();
0815: }
0816:
0817: /**
0818: * @see org.eclipse.jdt.internal.compiler.lookup.InvocationSite#genericTypeArguments()
0819: */
0820: public TypeBinding[] genericTypeArguments() {
0821: return null;
0822: }
0823:
0824: /**
0825: * Returns the local variable referenced by this node. Can be a direct reference (SingleNameReference)
0826: * or thru a cast expression etc...
0827: */
0828: public LocalVariableBinding localVariableBinding() {
0829: switch (bits & RestrictiveFlagMASK) {
0830: case Binding.FIELD: // reading a field
0831: break;
0832: case Binding.LOCAL: // reading a local variable
0833: return (LocalVariableBinding) this .binding;
0834: }
0835: return null;
0836: }
0837:
0838: public void manageEnclosingInstanceAccessIfNecessary(
0839: BlockScope currentScope, FlowInfo flowInfo) {
0840:
0841: if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0) {
0842: //If inlinable field, forget the access emulation, the code gen will directly target it
0843: if (((bits & DepthMASK) == 0)
0844: || (constant != Constant.NotAConstant))
0845: return;
0846:
0847: if ((bits & RestrictiveFlagMASK) == Binding.LOCAL) {
0848: currentScope
0849: .emulateOuterAccess((LocalVariableBinding) binding);
0850: }
0851: }
0852: }
0853:
0854: public void manageSyntheticAccessIfNecessary(
0855: BlockScope currentScope, FlowInfo flowInfo,
0856: boolean isReadAccess) {
0857:
0858: if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) != 0)
0859: return;
0860:
0861: //If inlinable field, forget the access emulation, the code gen will directly target it
0862: if (constant != Constant.NotAConstant)
0863: return;
0864:
0865: if ((bits & Binding.FIELD) != 0) {
0866: FieldBinding fieldBinding = (FieldBinding) binding;
0867: FieldBinding codegenField = fieldBinding.original();
0868: this .codegenBinding = codegenField;
0869: if (((bits & DepthMASK) != 0) && (codegenField.isPrivate() // private access
0870: || (codegenField.isProtected() // implicit protected access
0871: && codegenField.declaringClass.getPackage() != currentScope
0872: .enclosingSourceType().getPackage()))) {
0873: if (syntheticAccessors == null)
0874: syntheticAccessors = new MethodBinding[2];
0875: syntheticAccessors[isReadAccess ? READ : WRITE] = ((SourceTypeBinding) currentScope
0876: .enclosingSourceType().enclosingTypeAt(
0877: (bits & DepthMASK) >> DepthSHIFT))
0878: .addSyntheticMethod(codegenField, isReadAccess);
0879: currentScope.problemReporter()
0880: .needToEmulateFieldAccess(codegenField, this ,
0881: isReadAccess);
0882: return;
0883: }
0884: // if the binding declaring class is not visible, need special action
0885: // for runtime compatibility on 1.2 VMs : change the declaring class of the binding
0886: // NOTE: from target 1.2 on, field's declaring class is touched if any different from receiver type
0887: // and not from Object or implicit static field access.
0888: if (fieldBinding.declaringClass != this .actualReceiverType
0889: && !this .actualReceiverType.isArrayType()
0890: && fieldBinding.declaringClass != null // array.length
0891: && fieldBinding.constant() == Constant.NotAConstant) {
0892: CompilerOptions options = currentScope
0893: .compilerOptions();
0894: if ((options.targetJDK >= ClassFileConstants.JDK1_2
0895: && (options.complianceLevel >= ClassFileConstants.JDK1_4 || !fieldBinding
0896: .isStatic()) && fieldBinding.declaringClass.id != T_JavaLangObject) // no change for Object fields
0897: || !fieldBinding.declaringClass
0898: .canBeSeenBy(currentScope)) {
0899:
0900: this .codegenBinding = currentScope
0901: .enclosingSourceType()
0902: .getUpdatedFieldBinding(
0903: codegenField,
0904: (ReferenceBinding) this .actualReceiverType
0905: .erasure());
0906: }
0907: }
0908: }
0909: }
0910:
0911: public int nullStatus(FlowInfo flowInfo) {
0912: if (this .constant != null
0913: && this .constant != Constant.NotAConstant) {
0914: return FlowInfo.NON_NULL; // constant expression cannot be null
0915: }
0916: switch (bits & RestrictiveFlagMASK) {
0917: case Binding.FIELD: // reading a field
0918: return FlowInfo.UNKNOWN;
0919: case Binding.LOCAL: // reading a local variable
0920: LocalVariableBinding local = (LocalVariableBinding) this .binding;
0921: if (local != null) {
0922: if (flowInfo.isDefinitelyNull(local))
0923: return FlowInfo.NULL;
0924: if (flowInfo.isDefinitelyNonNull(local))
0925: return FlowInfo.NON_NULL;
0926: return FlowInfo.UNKNOWN;
0927: }
0928: }
0929: return FlowInfo.NON_NULL; // never get there
0930: }
0931:
0932: /**
0933: * @see org.eclipse.jdt.internal.compiler.ast.Expression#postConversionType(Scope)
0934: */
0935: public TypeBinding postConversionType(Scope scope) {
0936: TypeBinding convertedType = this .resolvedType;
0937: if (this .genericCast != null)
0938: convertedType = this .genericCast;
0939: int runtimeType = (this .implicitConversion & IMPLICIT_CONVERSION_MASK) >> 4;
0940: switch (runtimeType) {
0941: case T_boolean:
0942: convertedType = TypeBinding.BOOLEAN;
0943: break;
0944: case T_byte:
0945: convertedType = TypeBinding.BYTE;
0946: break;
0947: case T_short:
0948: convertedType = TypeBinding.SHORT;
0949: break;
0950: case T_char:
0951: convertedType = TypeBinding.CHAR;
0952: break;
0953: case T_int:
0954: convertedType = TypeBinding.INT;
0955: break;
0956: case T_float:
0957: convertedType = TypeBinding.FLOAT;
0958: break;
0959: case T_long:
0960: convertedType = TypeBinding.LONG;
0961: break;
0962: case T_double:
0963: convertedType = TypeBinding.DOUBLE;
0964: break;
0965: default:
0966: }
0967: if ((this .implicitConversion & BOXING) != 0) {
0968: convertedType = scope.environment().computeBoxingType(
0969: convertedType);
0970: }
0971: return convertedType;
0972: }
0973:
0974: public StringBuffer printExpression(int indent, StringBuffer output) {
0975:
0976: return output.append(token);
0977: }
0978:
0979: public TypeBinding reportError(BlockScope scope) {
0980:
0981: //=====error cases=======
0982: constant = Constant.NotAConstant;
0983: if (binding instanceof ProblemFieldBinding) {
0984: scope.problemReporter().invalidField(this ,
0985: (FieldBinding) binding);
0986: } else if (binding instanceof ProblemReferenceBinding) {
0987: scope.problemReporter().invalidType(this ,
0988: (TypeBinding) binding);
0989: } else {
0990: scope.problemReporter()
0991: .unresolvableReference(this , binding);
0992: }
0993: return null;
0994: }
0995:
0996: public TypeBinding resolveType(BlockScope scope) {
0997: // for code gen, harm the restrictiveFlag
0998:
0999: if (this .actualReceiverType != null) {
1000: this .binding = scope.getField(this .actualReceiverType,
1001: token, this );
1002: } else {
1003: this .actualReceiverType = scope.enclosingSourceType();
1004: this .binding = scope.getBinding(token, bits
1005: & RestrictiveFlagMASK, this , true /*resolve*/);
1006: }
1007: this .codegenBinding = this .binding;
1008: if (this .binding.isValidBinding()) {
1009: switch (bits & RestrictiveFlagMASK) {
1010: case Binding.VARIABLE: // =========only variable============
1011: case Binding.VARIABLE | Binding.TYPE: //====both variable and type============
1012: if (binding instanceof VariableBinding) {
1013: VariableBinding variable = (VariableBinding) binding;
1014: if (binding instanceof LocalVariableBinding) {
1015: bits &= ~RestrictiveFlagMASK; // clear bits
1016: bits |= Binding.LOCAL;
1017: if (!variable.isFinal()
1018: && (bits & DepthMASK) != 0) {
1019: scope
1020: .problemReporter()
1021: .cannotReferToNonFinalOuterLocal(
1022: (LocalVariableBinding) variable,
1023: this );
1024: }
1025: TypeBinding fieldType = variable.type;
1026: if ((this .bits & IsStrictlyAssigned) == 0) {
1027: constant = variable.constant();
1028: if (fieldType != null)
1029: fieldType = fieldType.capture(scope,
1030: this .sourceEnd); // perform capture conversion if read access
1031: } else {
1032: constant = Constant.NotAConstant;
1033: }
1034: return this .resolvedType = fieldType;
1035: }
1036: // a field
1037: FieldBinding field = (FieldBinding) this .binding;
1038: if (!field.isStatic()
1039: && scope
1040: .compilerOptions()
1041: .getSeverity(
1042: CompilerOptions.UnqualifiedFieldAccess) != ProblemSeverities.Ignore) {
1043: scope.problemReporter().unqualifiedFieldAccess(
1044: this , field);
1045: }
1046: // perform capture conversion if read access
1047: TypeBinding fieldType = checkFieldAccess(scope);
1048: return this .resolvedType = (((this .bits & IsStrictlyAssigned) == 0) ? fieldType
1049: .capture(scope, this .sourceEnd)
1050: : fieldType);
1051: }
1052:
1053: // thus it was a type
1054: bits &= ~RestrictiveFlagMASK; // clear bits
1055: bits |= Binding.TYPE;
1056: case Binding.TYPE: //========only type==============
1057: constant = Constant.NotAConstant;
1058: //deprecated test
1059: TypeBinding type = (TypeBinding) binding;
1060: if (isTypeUseDeprecated(type, scope))
1061: scope.problemReporter().deprecatedType(type, this );
1062: type = scope.environment().convertToRawType(type);
1063: return this .resolvedType = type;
1064: }
1065: }
1066:
1067: // error scenarii
1068: return this .resolvedType = this .reportError(scope);
1069: }
1070:
1071: public void traverse(ASTVisitor visitor, BlockScope scope) {
1072: visitor.visit(this , scope);
1073: visitor.endVisit(this , scope);
1074: }
1075:
1076: public void traverse(ASTVisitor visitor, ClassScope scope) {
1077: visitor.visit(this , scope);
1078: visitor.endVisit(this , scope);
1079: }
1080:
1081: public String unboundReferenceErrorName() {
1082:
1083: return new String(token);
1084: }
1085: }
|