/** Normal field binding did not work, try to bind to a field of the delegate receiver. */ public TypeBinding reportError(BlockScope scope) { if (this.binding instanceof ProblemFieldBinding) { scope.problemReporter().invalidField(this, (FieldBinding) this.binding); } else if (this.binding instanceof ProblemReferenceBinding) { scope.problemReporter().invalidType(this, (TypeBinding) this.binding); } else { scope.problemReporter().unresolvableReference(this, 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 = scope.enclosingReceiverType(); this.constant = Constant.NotAConstant; if (( /*this.codegenBinding =*/ this.binding = scope.getBinding( this.tokens, this.bits & ASTNode.RestrictiveFlagMASK, this, true /*resolve*/)) .isValidBinding()) { switch (this.bits & ASTNode.RestrictiveFlagMASK) { case Binding.VARIABLE: // ============only variable=========== case Binding.TYPE | Binding.VARIABLE: if (this.binding instanceof LocalVariableBinding) { this.bits &= ~ASTNode.RestrictiveFlagMASK; // clear bits this.bits |= Binding.LOCAL; return this.resolvedType = getOtherFieldBindings(scope); } if (this.binding instanceof FieldBinding) { FieldBinding fieldBinding = (FieldBinding) this.binding; MethodScope methodScope = scope.methodScope(); // check for forward references if (this.indexOfFirstFieldBinding == 1 && methodScope.enclosingSourceType() == fieldBinding.original().declaringClass && methodScope.lastVisibleFieldID >= 0 && fieldBinding.id >= methodScope.lastVisibleFieldID && (!fieldBinding.isStatic() || methodScope.isStatic)) { scope.problemReporter().forwardReference(this, 0, methodScope.enclosingSourceType()); } this.bits &= ~ASTNode.RestrictiveFlagMASK; // clear bits this.bits |= Binding.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 this.bits &= ~ASTNode.RestrictiveFlagMASK; // clear bits this.bits |= Binding.TYPE; case Binding.TYPE: // =============only type ============== TypeBinding type = (TypeBinding) this.binding; // if (isTypeUseDeprecated(type, scope)) // scope.problemReporter().deprecatedType(type, this); return this.resolvedType = type; } } // ========error cases=============== return this.resolvedType = this.reportError(scope); }
/** Check and/or redirect the field access to the delegate receiver if any */ public TypeBinding checkFieldAccess(BlockScope scope) { FieldBinding fieldBinding = (FieldBinding) this.binding; MethodScope methodScope = scope.methodScope(); // check for forward references if (this.indexOfFirstFieldBinding == 1 && methodScope.enclosingSourceType() == fieldBinding.original().declaringClass && methodScope.lastVisibleFieldID >= 0 && fieldBinding.id >= methodScope.lastVisibleFieldID && (!fieldBinding.isStatic() || methodScope.isStatic)) { scope.problemReporter().forwardReference(this, 0, methodScope.enclosingSourceType()); } this.bits &= ~ASTNode.RestrictiveFlagMASK; // clear bits this.bits |= Binding.FIELD; return getOtherFieldBindings(scope); }
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 = this.otherBindings == null ? 0 : this.otherBindings.length; boolean needValue = otherBindingsCount == 0 ? valueRequired : !this.otherBindings[0].isStatic(); boolean complyTo14 = currentScope.compilerOptions().complianceLevel >= ClassFileConstants.JDK1_4; switch (this.bits & ASTNode.RestrictiveFlagMASK) { case Binding.FIELD: // reading a field break; case Binding.LOCAL: // reading a local variable LocalVariableBinding localBinding; if (!flowInfo.isDefinitelyAssigned(localBinding = (LocalVariableBinding) this.binding)) { if (localBinding.declaringScope instanceof CompilationUnitScope) currentScope.problemReporter().uninitializedGlobalVariable(localBinding, this); else currentScope.problemReporter().uninitializedLocalVariable(localBinding, this); } if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0) { localBinding.useFlag = LocalVariableBinding.USED; } else if (localBinding.useFlag == LocalVariableBinding.UNUSED) { localBinding.useFlag = LocalVariableBinding.FAKE_USED; } checkNPE(currentScope, flowContext, flowInfo, true); } if (needValue) { manageEnclosingInstanceAccessIfNecessary(currentScope, flowInfo); // only for first binding (if value needed only) } if (this.otherBindings != null) { for (int i = 0; i < otherBindingsCount; i++) { needValue = i < otherBindingsCount - 1 ? !this.otherBindings[i + 1].isStatic() : valueRequired; if (needValue || complyTo14) { TypeBinding lastReceiverType = getGenericCast(i); if (lastReceiverType == null) { if (i == 0) { lastReceiverType = ((VariableBinding) this.binding).type; } else { lastReceiverType = this.otherBindings[i - 1].type; } } } } } return flowInfo; }
public void manageEnclosingInstanceAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo) { if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0) { // If inlinable field, forget the access emulation, the code gen will directly target it if (((this.bits & ASTNode.DepthMASK) == 0) || (this.constant != Constant.NotAConstant)) { return; } if ((this.bits & ASTNode.RestrictiveFlagMASK) == Binding.LOCAL) { currentScope.emulateOuterAccess((LocalVariableBinding) this.binding); } } }
/* Inner emulation consists in either recording a dependency * link only, or performing one level of propagation. * * Dependency mechanism is used whenever dealing with source target * types, since by the time we reach them, we might not yet know their * exact need. */ public void manageEnclosingInstanceAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo) { if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) != 0) return; ReferenceBinding allocatedTypeErasure = binding.declaringClass; // perform some emulation work in case there is some and we are inside a local type only if (allocatedTypeErasure.isNestedType() && currentScope.enclosingSourceType().isLocalType()) { if (allocatedTypeErasure.isLocalType()) { ((LocalTypeBinding) allocatedTypeErasure).addInnerEmulationDependent(currentScope, false); // request cascade of accesses } } }
public void resolve(BlockScope scope) { MethodScope methodScope = scope.methodScope(); if (methodScope == null) { /* return statement outside of a method */ scope.problemReporter().cannotReturnOutsideFunction(this); return; } MethodBinding methodBinding = null; TypeBinding methodType = (methodScope.referenceContext instanceof AbstractMethodDeclaration) ? ((methodBinding = ((AbstractMethodDeclaration) methodScope.referenceContext).binding) == null ? null : methodBinding.returnType) : TypeBinding.ANY; TypeBinding expressionType; if (this.expression == null) { if (methodType != null && !methodType.isAnyType()) scope.problemReporter().shouldReturn(methodType, this); return; } this.expression.setExpectedType(methodType); // needed in case of generic method invocation if ((expressionType = this.expression.resolveType(scope)) == null) return; if (methodType == null) return; if (methodType != expressionType) // must call before computeConversion() and typeMismatchError() scope.compilationUnitScope().recordTypeConversion(methodType, expressionType); if (this.expression.isConstantValueOfTypeAssignableToType(expressionType, methodType) || expressionType.isCompatibleWith(methodType)) { return; } if (methodBinding != null && !methodBinding.isConstructor()) scope.problemReporter().typeMismatchError(expressionType, methodType, this.expression); }
public TypeBinding resolveType(BlockScope scope) { // Build an array type reference using the current dimensions // The parser does not check for the fact that dimension may be null // only at the -end- like new int [4][][]. The parser allows new int[][4][] // so this must be checked here......(this comes from a reduction to LL1 grammar) TypeBinding referenceType = this.type.resolveType(scope, true /* check bounds*/); // will check for null after dimensions are checked this.constant = Constant.NotAConstant; // check the validity of the dimension syntax (and test for all null dimensions) int explicitDimIndex = -1; loop: for (int i = this.dimensions.length; --i >= 0; ) { if (this.dimensions[i] != null) { if (explicitDimIndex < 0) explicitDimIndex = i; } else if (explicitDimIndex > 0) { break loop; } } // dimensions resolution for (int i = 0; i <= explicitDimIndex; i++) { Expression dimExpression; if ((dimExpression = this.dimensions[i]) != null) { TypeBinding dimensionType = dimExpression.resolveTypeExpecting(scope, TypeBinding.INT); } } // building the array binding if (referenceType != null) { this.resolvedType = scope.createArrayType(referenceType, this.dimensions.length); // check the initializer if (this.initializer != null) { if ((this.initializer.resolveTypeExpecting(scope, this.resolvedType)) != null) this.initializer.binding = (ArrayBinding) this.resolvedType; } } return this.resolvedType; }
public TypeBinding resolveType(BlockScope scope) { // Propagate the type checking to the arguments, and check if the constructor is defined. constant = Constant.NotAConstant; if (this.member != null) { this.resolvedType = this.member.resolveForAllocation(scope, this); if (this.resolvedType != null && !this.resolvedType.isValidBinding()) { scope.problemReporter().invalidType(this, this.resolvedType); } } else if (this.type == null) { // initialization of an enum constant this.resolvedType = scope.enclosingReceiverType(); } else { this.resolvedType = this.type.resolveType(scope, true /* check bounds*/); } // will check for null after args are resolved // buffering the arguments' types boolean argsContainCast = false; TypeBinding[] argumentTypes = Binding.NO_PARAMETERS; if (arguments != null) { boolean argHasError = false; int length = arguments.length; argumentTypes = new TypeBinding[length]; for (int i = 0; i < length; i++) { Expression argument = this.arguments[i]; if ((argumentTypes[i] = argument.resolveType(scope)) == null) { argHasError = true; argumentTypes[i] = TypeBinding.UNKNOWN; } } if (argHasError) { // if (this.resolvedType instanceof ReferenceBinding) { // // record a best guess, for clients who need hint about possible contructor match // TypeBinding[] pseudoArgs = new TypeBinding[length]; // for (int i = length; --i >= 0;) // pseudoArgs[i] = argumentTypes[i] == null ? this.resolvedType : argumentTypes[i]; // // replace args with errors with receiver // this.binding = scope.findMethod((ReferenceBinding) this.resolvedType, // TypeConstants.INIT, pseudoArgs, this); // } // return this.resolvedType; } } if (this.resolvedType == null || this.resolvedType.isAnyType() || this.resolvedType instanceof ProblemReferenceBinding) { this.binding = new ProblemMethodBinding( TypeConstants.INIT, Binding.NO_PARAMETERS, ProblemReasons.NotFound); this.resolvedType = TypeBinding.UNKNOWN; return this.resolvedType; } if (!this.resolvedType.isValidBinding()) return null; if (this.resolvedType instanceof ReferenceBinding) { ReferenceBinding allocationType = (ReferenceBinding) this.resolvedType; if (!(binding = scope.getConstructor(allocationType, argumentTypes, this)).isValidBinding()) { if (binding.declaringClass == null) binding.declaringClass = allocationType; scope.problemReporter().invalidConstructor(this, binding); return this.resolvedType; } if (argumentTypes.length != binding.parameters.length) scope.problemReporter().wrongNumberOfArguments(this, binding); if (isMethodUseDeprecated(binding, scope, true)) scope.problemReporter().deprecatedMethod(binding, this); checkInvocationArguments( scope, null, allocationType, this.binding, this.arguments, argumentTypes, argsContainCast, this); } return this.resolvedType; }
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 = this.otherBindings == null ? 0 : this.otherBindings.length; boolean needValue = otherBindingsCount == 0 || !this.otherBindings[0].isStatic(); boolean complyTo14 = currentScope.compilerOptions().complianceLevel >= ClassFileConstants.JDK1_4; FieldBinding lastFieldBinding = null; switch (this.bits & ASTNode.RestrictiveFlagMASK) { case Binding.FIELD: // reading a field lastFieldBinding = (FieldBinding) this.binding; break; case Binding.LOCAL: // first binding is a local variable LocalVariableBinding localBinding; if (!flowInfo.isDefinitelyAssigned(localBinding = (LocalVariableBinding) this.binding)) { currentScope.problemReporter().uninitializedLocalVariable(localBinding, this); } if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0) { localBinding.useFlag = LocalVariableBinding.USED; } else if (localBinding.useFlag == LocalVariableBinding.UNUSED) { localBinding.useFlag = LocalVariableBinding.FAKE_USED; } checkNPE(currentScope, flowContext, flowInfo, true); } if (needValue) { manageEnclosingInstanceAccessIfNecessary(currentScope, flowInfo); // only for first binding } // all intermediate field accesses are read accesses if (this.otherBindings != null) { for (int i = 0; i < otherBindingsCount - 1; i++) { lastFieldBinding = this.otherBindings[i]; needValue = !this.otherBindings[i + 1].isStatic(); } lastFieldBinding = this.otherBindings[otherBindingsCount - 1]; } if (isCompound) { TypeBinding lastReceiverType; switch (otherBindingsCount) { case 0: lastReceiverType = this.actualReceiverType; break; case 1: lastReceiverType = ((VariableBinding) this.binding).type; break; default: lastReceiverType = this.otherBindings[otherBindingsCount - 2].type; break; } } if (assignment.expression != null) { flowInfo = assignment .expression .analyseCode(currentScope, flowContext, flowInfo) .unconditionalInits(); } // equivalent to valuesRequired[maxOtherBindings] TypeBinding lastReceiverType; switch (otherBindingsCount) { case 0: lastReceiverType = this.actualReceiverType; break; case 1: lastReceiverType = ((VariableBinding) this.binding).type; break; default: lastReceiverType = this.otherBindings[otherBindingsCount - 2].type; break; } return flowInfo; }
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 = this.tokens.length; FieldBinding field; if ((this.bits & Binding.FIELD) != 0) { field = (FieldBinding) this.binding; if (!field.isStatic()) { // must check for the static status.... if (this.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, field); return null; } } else { // indirect static reference ? if (this.indexOfFirstFieldBinding > 1 && field.declaringClass != this.actualReceiverType && field.declaringClass.canBeSeenBy(scope)) { scope.problemReporter().indirectAccessToStaticField(this, field); } } // only last field is actually a write access if any if (isFieldUseDeprecated( field, scope, (this.bits & ASTNode.IsStrictlyAssigned) != 0 && this.indexOfFirstFieldBinding == length)) scope.problemReporter().deprecatedField(field, this); } else { field = null; } TypeBinding type = ((VariableBinding) this.binding).type; int index = this.indexOfFirstFieldBinding; if (index == length) { // restrictiveFlag == FIELD // perform capture conversion if read access return type; } // allocation of the fieldBindings array and its respective constants int otherBindingsLength = length - index; this.otherCodegenBindings = this.otherBindings = new FieldBinding[otherBindingsLength]; this.otherDepths = new int[otherBindingsLength]; // save first depth, since will be updated by visibility checks of other bindings int firstDepth = (this.bits & ASTNode.DepthMASK) >> ASTNode.DepthSHIFT; // iteration on each field while (index < length) { char[] token = this.tokens[index]; if (type == null) return null; // could not resolve type prior to this point this.bits &= ~ASTNode.DepthMASK; // flush previous depth if any FieldBinding previousField = field; field = scope.getField(type, token, this); int place = index - this.indexOfFirstFieldBinding; this.otherBindings[place] = field; this.otherDepths[place] = (this.bits & ASTNode.DepthMASK) >> ASTNode.DepthSHIFT; if (field.isValidBinding()) { // set generic cast of for previous field (if any) if (previousField != null) { TypeBinding fieldReceiverType = type; TypeBinding receiverErasure = type; if (receiverErasure instanceof ReferenceBinding) { if (receiverErasure.findSuperTypeWithSameErasure(field.declaringClass) == null) { fieldReceiverType = field.declaringClass; // handle indirect inheritance thru variable secondary bound } } FieldBinding originalBinding = previousField.original(); } // only last field is actually a write access if any if (isFieldUseDeprecated( field, scope, (this.bits & ASTNode.IsStrictlyAssigned) != 0 && index + 1 == length)) { scope.problemReporter().deprecatedField(field, this); } 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 { this.constant = Constant.NotAConstant; // don't fill other constants slots... scope.problemReporter().invalidField(this, field, index, type); setDepth(firstDepth); return null; } } setDepth(firstDepth); type = (this.otherBindings[otherBindingsLength - 1]).type; // perform capture conversion if read access return type; }
public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) { // process the element variable and collection this.collection.checkNPE(currentScope, flowContext, flowInfo); flowInfo = this.iterationVariable.analyseCode(scope, flowContext, flowInfo); FlowInfo condInfo = this.collection.analyseCode(scope, flowContext, flowInfo.copy()); LocalVariableBinding iterationVariableBinding = null; if (this.iterationVariable instanceof LocalDeclaration) iterationVariableBinding = ((LocalDeclaration) this.iterationVariable).binding; else if (this.iterationVariable instanceof SingleNameReference) { SingleNameReference singleNameReference = (SingleNameReference) this.iterationVariable; if (singleNameReference.binding instanceof LocalVariableBinding) iterationVariableBinding = (LocalVariableBinding) singleNameReference.binding; } // element variable will be assigned when iterating if (iterationVariableBinding != null) condInfo.markAsDefinitelyAssigned(iterationVariableBinding); // this.postCollectionInitStateIndex = // currentScope.methodScope().recordInitializationStates(condInfo); // process the action LoopingFlowContext loopingContext = new LoopingFlowContext(flowContext, flowInfo, this, scope); UnconditionalFlowInfo actionInfo = condInfo.nullInfoLessUnconditionalCopy(); if (iterationVariableBinding != null) actionInfo.markAsDefinitelyUnknown(iterationVariableBinding); FlowInfo exitBranch; if (!(action == null || (action.isEmptyBlock() && currentScope.compilerOptions().complianceLevel <= ClassFileConstants.JDK1_3))) { if (!this.action.complainIfUnreachable(actionInfo, scope, false)) { actionInfo = action.analyseCode(scope, loopingContext, actionInfo).unconditionalCopy(); } // code generation can be optimized when no need to continue in the loop exitBranch = flowInfo.unconditionalCopy().addInitializationsFrom(condInfo.initsWhenFalse()); // TODO (maxime) no need to test when false: can optimize (same for action being unreachable // above) if ((actionInfo.tagBits & loopingContext.initsOnContinue.tagBits & FlowInfo.UNREACHABLE) != 0) { } else { actionInfo = actionInfo.mergedWith(loopingContext.initsOnContinue); loopingContext.complainOnDeferredFinalChecks(scope, actionInfo); exitBranch.addPotentialInitializationsFrom(actionInfo); } } else { exitBranch = condInfo.initsWhenFalse(); } // we need the variable to iterate the collection even if the // element variable is not used // final boolean hasEmptyAction = this.action == null // || this.action.isEmptyBlock() // || ((this.action.bits & IsUsefulEmptyStatement) != 0); // end of loop loopingContext.complainOnDeferredNullChecks(currentScope, actionInfo); FlowInfo mergedInfo = FlowInfo.mergedOptimizedBranches( (loopingContext.initsOnBreak.tagBits & FlowInfo.UNREACHABLE) != 0 ? loopingContext.initsOnBreak : flowInfo.addInitializationsFrom( loopingContext.initsOnBreak), // recover upstream null info false, exitBranch, false, true /*for(;;){}while(true); unreachable(); */); // mergedInitStateIndex = currentScope.methodScope().recordInitializationStates(mergedInfo); return mergedInfo; }
public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) { if (this.expression != null) { flowInfo = this.expression.analyseCode(currentScope, flowContext, flowInfo); } // compute the return sequence (running the finally blocks) FlowContext traversedContext = flowContext; int subCount = 0; boolean saveValueNeeded = false; boolean hasValueToSave = this.expression != null && this.expression.constant == Constant.NotAConstant && !(this.expression instanceof NullLiteral); do { SubRoutineStatement sub; if ((sub = traversedContext.subroutine()) != null) { if (this.subroutines == null) { this.subroutines = new SubRoutineStatement[5]; } if (subCount == this.subroutines.length) { System.arraycopy( this.subroutines, 0, (this.subroutines = new SubRoutineStatement[subCount * 2]), 0, subCount); // grow } this.subroutines[subCount++] = sub; if (sub.isSubRoutineEscaping()) { saveValueNeeded = false; this.bits |= ASTNode.IsAnySubRoutineEscaping; break; } } traversedContext.recordReturnFrom(flowInfo.unconditionalInits()); if (traversedContext instanceof InsideSubRoutineFlowContext) { ASTNode node = traversedContext.associatedNode; if (node instanceof TryStatement) { TryStatement tryStatement = (TryStatement) node; flowInfo.addInitializationsFrom(tryStatement.subRoutineInits); // collect inits if (hasValueToSave) { if (this.saveValueVariable == null) { // closest subroutine secret variable is used prepareSaveValueLocation(tryStatement); } saveValueNeeded = true; } } } else if (traversedContext instanceof InitializationFlowContext) { currentScope.problemReporter().cannotReturnOutsideFunction(this); return FlowInfo.DEAD_END; } } while ((traversedContext = traversedContext.parent) != null); // resize subroutines if ((this.subroutines != null) && (subCount != this.subroutines.length)) { System.arraycopy( this.subroutines, 0, (this.subroutines = new SubRoutineStatement[subCount]), 0, subCount); } // secret local variable for return value (note that this can only occur in a real method) if (saveValueNeeded) { if (this.saveValueVariable != null) { this.saveValueVariable.useFlag = LocalVariableBinding.USED; } } else { this.saveValueVariable = null; if (this.expression != null && this.expression.resolvedType == TypeBinding.BOOLEAN) { this.expression.bits |= ASTNode.IsReturnedValue; } } return FlowInfo.DEAD_END; }