org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ecj Show documentation
Show all versions of ecj Show documentation
This is Eclipse JDT Core Batch Compiler used by Scout SDK
The newest version!
/*******************************************************************************
* Copyright (c) 2000, 2015 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
* Stephan Herrmann - Contributions for
* bug 185682 - Increment/decrement operators mark local variables as read
* bug 349326 - [1.7] new warning for missing try-with-resources
* bug 186342 - [compiler][null] Using annotations for null checking
* bug 365859 - [compiler][null] distinguish warnings based on flow analysis vs. null annotations
* bug 331649 - [compiler][null] consider null annotations for fields
* Bug 466308 - [hovering] Javadoc header for parameter is wrong with annotation-based null analysis
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.lookup;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.eclipse.jdt.internal.compiler.ast.FakedTrackingVariable;
import org.eclipse.jdt.internal.compiler.ast.Initializer;
import org.eclipse.jdt.internal.compiler.ast.LambdaExpression;
import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.impl.Constant;
import org.eclipse.jdt.internal.compiler.impl.ReferenceContext;
public class LocalVariableBinding extends VariableBinding {
public int resolvedPosition; // for code generation (position in method context)
public static final int UNUSED = 0;
public static final int USED = 1;
public static final int FAKE_USED = 2;
public int useFlag; // for flow analysis (default is UNUSED), values < 0 indicate the number of compound uses (postIncrement or compoundAssignment)
public BlockScope declaringScope; // back-pointer to its declaring scope
public LocalDeclaration declaration; // for source-positions
public int[] initializationPCs;
public int initializationCount = 0;
public FakedTrackingVariable closeTracker; // track closing of instances of type AutoCloseable, maybe null
// for synthetic local variables
// if declaration slot is not positionned, the variable will not be listed in attribute
// note that the name of a variable should be chosen so as not to conflict with user ones (usually starting with a space char is all needed)
public LocalVariableBinding(char[] name, TypeBinding type, int modifiers, boolean isArgument) {
super(name, type, modifiers, isArgument ? Constant.NotAConstant : null);
if (isArgument) this.tagBits |= TagBits.IsArgument;
this.tagBits |= TagBits.IsEffectivelyFinal;
}
// regular local variable or argument
public LocalVariableBinding(LocalDeclaration declaration, TypeBinding type, int modifiers, boolean isArgument) {
this(declaration.name, type, modifiers, isArgument);
this.declaration = declaration;
}
// argument
public LocalVariableBinding(LocalDeclaration declaration, TypeBinding type, int modifiers, MethodScope declaringScope) {
this(declaration, type, modifiers, true);
this.declaringScope = declaringScope;
}
/* API
* Answer the receiver's binding type from Binding.BindingID.
*/
public final int kind() {
return LOCAL;
}
/*
* declaringUniqueKey # scopeIndex(0-based) # varName [# occurrenceCount(0-based)]
* p.X { void foo() { int local; int local;} } --> Lp/X;.foo()V#1#local#1
*
* for method parameter, we have no scopeIndex, but instead we append the parameter rank:
* declaringUniqueKey # varName # occurrenceCount(always 0) # argument rank (0-based)
* with parameter names:
* p.X { void foo(int i0, int i1) { } } --> Lp/X;.foo()V#i1#0#1
* without parameter names (see org.eclipse.jdt.internal.core.util.BindingKeyResolver.SyntheticLocalVariableBinding):
* p.X { void foo(int i0, int i1) { } } --> Lp/X;.foo()V#arg1#0#1
*/
public char[] computeUniqueKey(boolean isLeaf) {
StringBuffer buffer = new StringBuffer();
// declaring method or type
BlockScope scope = this.declaringScope;
int occurenceCount = 0;
if (scope != null) {
// the scope can be null. See https://bugs.eclipse.org/bugs/show_bug.cgi?id=185129
MethodScope methodScope = scope instanceof MethodScope ? (MethodScope) scope : scope.enclosingMethodScope();
ReferenceContext referenceContext = methodScope.referenceContext;
if (referenceContext instanceof AbstractMethodDeclaration) {
MethodBinding methodBinding = ((AbstractMethodDeclaration) referenceContext).binding;
if (methodBinding != null) {
buffer.append(methodBinding.computeUniqueKey(false/*not a leaf*/));
}
} else if (referenceContext instanceof TypeDeclaration) {
TypeBinding typeBinding = ((TypeDeclaration) referenceContext).binding;
if (typeBinding != null) {
buffer.append(typeBinding.computeUniqueKey(false/*not a leaf*/));
}
} else if (referenceContext instanceof LambdaExpression) {
MethodBinding methodBinding = ((LambdaExpression) referenceContext).binding;
if (methodBinding != null) {
buffer.append(methodBinding.computeUniqueKey(false/*not a leaf*/));
}
}
// scope index
getScopeKey(scope, buffer);
// find number of occurences of a variable with the same name in the scope
LocalVariableBinding[] locals = scope.locals;
for (int i = 0; i < scope.localIndex; i++) { // use linear search assuming the number of locals per scope is low
LocalVariableBinding local = locals[i];
if (CharOperation.equals(this.name, local.name)) {
if (this == local)
break;
occurenceCount++;
}
}
}
// variable name
buffer.append('#');
buffer.append(this.name);
boolean addParameterRank = this.isParameter() && this.declaringScope != null;
// add occurence count to avoid same key for duplicate variables
// (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=149590)
if (occurenceCount > 0 || addParameterRank) {
buffer.append('#');
buffer.append(occurenceCount);
if (addParameterRank) {
int pos = -1;
LocalVariableBinding[] params = this.declaringScope.locals;
for (int i = 0; i < params.length; i++) {
if (params[i] == this) {
pos = i;
break;
}
}
if (pos > -1) {
buffer.append('#');
buffer.append(pos);
}
}
}
int length = buffer.length();
char[] uniqueKey = new char[length];
buffer.getChars(0, length, uniqueKey, 0);
return uniqueKey;
}
public AnnotationBinding[] getAnnotations() {
if (this.declaringScope == null) {
if ((this.tagBits & TagBits.AnnotationResolved) != 0) {
// annotation are already resolved
if (this.declaration == null) {
return Binding.NO_ANNOTATIONS;
}
Annotation[] annotations = this.declaration.annotations;
if (annotations != null) {
int length = annotations.length;
AnnotationBinding[] annotationBindings = new AnnotationBinding[length];
for (int i = 0; i < length; i++) {
AnnotationBinding compilerAnnotation = annotations[i].getCompilerAnnotation();
if (compilerAnnotation == null) {
return Binding.NO_ANNOTATIONS;
}
annotationBindings[i] = compilerAnnotation;
}
return annotationBindings;
}
}
return Binding.NO_ANNOTATIONS;
}
SourceTypeBinding sourceType = this.declaringScope.enclosingSourceType();
if (sourceType == null)
return Binding.NO_ANNOTATIONS;
if ((this.tagBits & TagBits.AnnotationResolved) == 0) {
if (((this.tagBits & TagBits.IsArgument) != 0) && this.declaration != null) {
Annotation[] annotationNodes = this.declaration.annotations;
if (annotationNodes != null) {
ASTNode.resolveAnnotations(this.declaringScope, annotationNodes, this, true);
}
}
}
return sourceType.retrieveAnnotations(this);
}
private void getScopeKey(BlockScope scope, StringBuffer buffer) {
int scopeIndex = scope.scopeIndex();
if (scopeIndex != -1) {
getScopeKey((BlockScope)scope.parent, buffer);
buffer.append('#');
buffer.append(scopeIndex);
}
}
// Answer whether the variable binding is a secret variable added for code gen purposes
public boolean isSecret() {
return this.declaration == null && (this.tagBits & TagBits.IsArgument) == 0;
}
public void recordInitializationEndPC(int pc) {
if (this.initializationPCs[((this.initializationCount - 1) << 1) + 1] == -1)
this.initializationPCs[((this.initializationCount - 1) << 1) + 1] = pc;
}
public void recordInitializationStartPC(int pc) {
if (this.initializationPCs == null) {
return;
}
if (this.initializationCount > 0) {
int previousEndPC = this.initializationPCs[ ((this.initializationCount - 1) << 1) + 1];
// interval still open, keep using it (108180)
if (previousEndPC == -1) {
return;
}
// optimize cases where reopening a contiguous interval
if (previousEndPC == pc) {
this.initializationPCs[ ((this.initializationCount - 1) << 1) + 1] = -1; // reuse previous interval (its range will be augmented)
return;
}
}
int index = this.initializationCount << 1;
if (index == this.initializationPCs.length) {
System.arraycopy(this.initializationPCs, 0, (this.initializationPCs = new int[this.initializationCount << 2]), 0, index);
}
this.initializationPCs[index] = pc;
this.initializationPCs[index + 1] = -1;
this.initializationCount++;
}
public void setAnnotations(AnnotationBinding[] annotations, Scope scope) {
// note: we don's use this.declaringScope because we might be called before Scope.addLocalVariable(this)
// which is where this.declaringScope is set.
if (scope == null)
return;
SourceTypeBinding sourceType = scope.enclosingSourceType();
if (sourceType != null)
sourceType.storeAnnotations(this, annotations);
}
public void resetInitializations() {
this.initializationCount = 0;
this.initializationPCs = null;
}
public String toString() {
String s = super.toString();
switch (this.useFlag){
case USED:
s += "[pos: " + String.valueOf(this.resolvedPosition) + "]"; //$NON-NLS-2$ //$NON-NLS-1$
break;
case UNUSED:
s += "[pos: unused]"; //$NON-NLS-1$
break;
case FAKE_USED:
s += "[pos: fake_used]"; //$NON-NLS-1$
break;
}
s += "[id:" + String.valueOf(this.id) + "]"; //$NON-NLS-2$ //$NON-NLS-1$
if (this.initializationCount > 0) {
s += "[pc: "; //$NON-NLS-1$
for (int i = 0; i < this.initializationCount; i++) {
if (i > 0)
s += ", "; //$NON-NLS-1$
s += String.valueOf(this.initializationPCs[i << 1]) + "-" + ((this.initializationPCs[(i << 1) + 1] == -1) ? "?" : String.valueOf(this.initializationPCs[(i<< 1) + 1])); //$NON-NLS-2$ //$NON-NLS-1$
}
s += "]"; //$NON-NLS-1$
}
return s;
}
public boolean isParameter() {
return ((this.tagBits & TagBits.IsArgument) != 0);
}
public boolean isCatchParameter() {
return false;
}
public MethodBinding getEnclosingMethod() {
BlockScope blockScope = this.declaringScope;
if (blockScope != null) {
ReferenceContext referenceContext = blockScope.referenceContext();
if (referenceContext instanceof Initializer) {
return null;
}
if (referenceContext instanceof AbstractMethodDeclaration) {
return ((AbstractMethodDeclaration) referenceContext).binding;
}
}
return null;
}
}