KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jdt > internal > compiler > lookup > BlockScope


1 /*******************************************************************************
2  * Copyright (c) 2000, 2007 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.jdt.internal.compiler.lookup;
12
13 import org.eclipse.jdt.core.compiler.CharOperation;
14 import org.eclipse.jdt.internal.compiler.ast.*;
15 import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
16 import org.eclipse.jdt.internal.compiler.codegen.CodeStream;
17 import org.eclipse.jdt.internal.compiler.impl.Constant;
18 import org.eclipse.jdt.internal.compiler.problem.ProblemReporter;
19
20 public class BlockScope extends Scope {
21
22     // Local variable management
23
public LocalVariableBinding[] locals;
24     public int localIndex; // position for next variable
25
public int startIndex; // start position in this scope - for ordering scopes vs. variables
26
public int offset; // for variable allocation throughout scopes
27
public int maxOffset; // for variable allocation throughout scopes
28

29     // finally scopes must be shifted behind respective try&catch scope(s) so as to avoid
30
// collisions of secret variables (return address, save value).
31
public BlockScope[] shiftScopes;
32
33     public Scope[] subscopes = new Scope[1]; // need access from code assist
34
public int subscopeCount = 0; // need access from code assist
35
// record the current case statement being processed (for entire switch case block).
36
public CaseStatement enclosingCase; // from 1.4 on, local types should not be accessed across switch case blocks (52221)
37

38     public final static VariableBinding[] EmulationPathToImplicitThis = {};
39     public final static VariableBinding[] NoEnclosingInstanceInConstructorCall = {};
40
41     public final static VariableBinding[] NoEnclosingInstanceInStaticContext = {};
42
43 public BlockScope(BlockScope parent) {
44     this(parent, true);
45 }
46
47 public BlockScope(BlockScope parent, boolean addToParentScope) {
48     this(Scope.BLOCK_SCOPE, parent);
49     this.locals = new LocalVariableBinding[5];
50     if (addToParentScope) parent.addSubscope(this);
51     this.startIndex = parent.localIndex;
52 }
53
54 public BlockScope(BlockScope parent, int variableCount) {
55     this(Scope.BLOCK_SCOPE, parent);
56     this.locals = new LocalVariableBinding[variableCount];
57     parent.addSubscope(this);
58     this.startIndex = parent.localIndex;
59 }
60
61 protected BlockScope(int kind, Scope parent) {
62     super(kind, parent);
63 }
64
65 /* Create the class scope & binding for the anonymous type.
66  */

67 public final void addAnonymousType(TypeDeclaration anonymousType, ReferenceBinding superBinding) {
68     ClassScope anonymousClassScope = new ClassScope(this, anonymousType);
69     anonymousClassScope.buildAnonymousTypeBinding(
70         enclosingSourceType(),
71         superBinding);
72 }
73
74 /* Create the class scope & binding for the local type.
75  */

76 public final void addLocalType(TypeDeclaration localType) {
77     ClassScope localTypeScope = new ClassScope(this, localType);
78     addSubscope(localTypeScope);
79     localTypeScope.buildLocalTypeBinding(enclosingSourceType());
80 }
81
82 /* Insert a local variable into a given scope, updating its position
83  * and checking there are not too many locals or arguments allocated.
84  */

85 public final void addLocalVariable(LocalVariableBinding binding) {
86     checkAndSetModifiersForVariable(binding);
87     // insert local in scope
88
if (this.localIndex == this.locals.length)
89         System.arraycopy(
90             this.locals,
91             0,
92             (this.locals = new LocalVariableBinding[this.localIndex * 2]),
93             0,
94             this.localIndex);
95     this.locals[this.localIndex++] = binding;
96
97     // update local variable binding
98
binding.declaringScope = this;
99     binding.id = this.outerMostMethodScope().analysisIndex++;
100     // share the outermost method scope analysisIndex
101
}
102
103 public void addSubscope(Scope childScope) {
104     if (this.subscopeCount == this.subscopes.length)
105         System.arraycopy(
106             this.subscopes,
107             0,
108             (this.subscopes = new Scope[this.subscopeCount * 2]),
109             0,
110             this.subscopeCount);
111     this.subscopes[this.subscopeCount++] = childScope;
112 }
113
114 /* Answer true if the receiver is suitable for assigning final blank fields.
115  *
116  * in other words, it is inside an initializer, a constructor or a clinit
117  */

118 public final boolean allowBlankFinalFieldAssignment(FieldBinding binding) {
119     if (enclosingReceiverType() != binding.declaringClass)
120         return false;
121
122     MethodScope methodScope = methodScope();
123     if (methodScope.isStatic != binding.isStatic())
124         return false;
125     return methodScope.isInsideInitializer() // inside initializer
126
|| ((AbstractMethodDeclaration) methodScope.referenceContext).isInitializationMethod(); // inside constructor or clinit
127
}
128 String JavaDoc basicToString(int tab) {
129     String JavaDoc newLine = "\n"; //$NON-NLS-1$
130
for (int i = tab; --i >= 0;)
131         newLine += "\t"; //$NON-NLS-1$
132

133     String JavaDoc s = newLine + "--- Block Scope ---"; //$NON-NLS-1$
134
newLine += "\t"; //$NON-NLS-1$
135
s += newLine + "locals:"; //$NON-NLS-1$
136
for (int i = 0; i < this.localIndex; i++)
137         s += newLine + "\t" + this.locals[i].toString(); //$NON-NLS-1$
138
s += newLine + "startIndex = " + this.startIndex; //$NON-NLS-1$
139
return s;
140 }
141
142 private void checkAndSetModifiersForVariable(LocalVariableBinding varBinding) {
143     int modifiers = varBinding.modifiers;
144     if ((modifiers & ExtraCompilerModifiers.AccAlternateModifierProblem) != 0 && varBinding.declaration != null){
145         problemReporter().duplicateModifierForVariable(varBinding.declaration, this instanceof MethodScope);
146     }
147     int realModifiers = modifiers & ExtraCompilerModifiers.AccJustFlag;
148     
149     int unexpectedModifiers = ~ClassFileConstants.AccFinal;
150     if ((realModifiers & unexpectedModifiers) != 0 && varBinding.declaration != null){
151         problemReporter().illegalModifierForVariable(varBinding.declaration, this instanceof MethodScope);
152     }
153     varBinding.modifiers = modifiers;
154 }
155
156 /* Compute variable positions in scopes given an initial position offset
157  * ignoring unused local variables.
158  *
159  * No argument is expected here (ilocal is the first non-argument local of the outermost scope)
160  * Arguments are managed by the MethodScope method
161  */

162 void computeLocalVariablePositions(int ilocal, int initOffset, CodeStream codeStream) {
163     this.offset = initOffset;
164     this.maxOffset = initOffset;
165
166     // local variable init
167
int maxLocals = this.localIndex;
168     boolean hasMoreVariables = ilocal < maxLocals;
169
170     // scope init
171
int iscope = 0, maxScopes = this.subscopeCount;
172     boolean hasMoreScopes = maxScopes > 0;
173
174     // iterate scopes and variables in parallel
175
while (hasMoreVariables || hasMoreScopes) {
176         if (hasMoreScopes
177             && (!hasMoreVariables || (this.subscopes[iscope].startIndex() <= ilocal))) {
178             // consider subscope first
179
if (this.subscopes[iscope] instanceof BlockScope) {
180                 BlockScope subscope = (BlockScope) this.subscopes[iscope];
181                 int subOffset = subscope.shiftScopes == null ? this.offset : subscope.maxShiftedOffset();
182                 subscope.computeLocalVariablePositions(0, subOffset, codeStream);
183                 if (subscope.maxOffset > this.maxOffset)
184                     this.maxOffset = subscope.maxOffset;
185             }
186             hasMoreScopes = ++iscope < maxScopes;
187         } else {
188             
189             // consider variable first
190
LocalVariableBinding local = this.locals[ilocal]; // if no local at all, will be locals[ilocal]==null
191

192             // check if variable is actually used, and may force it to be preserved
193
boolean generateCurrentLocalVar = (local.useFlag == LocalVariableBinding.USED && local.constant() == Constant.NotAConstant);
194                 
195             // do not report fake used variable
196
if (local.useFlag == LocalVariableBinding.UNUSED
197                 && (local.declaration != null) // unused (and non secret) local
198
&& ((local.declaration.bits & ASTNode.IsLocalDeclarationReachable) != 0)) { // declaration is reachable
199

200                 if (!(local.declaration instanceof Argument)) // do not report unused catch arguments
201
this.problemReporter().unusedLocalVariable(local.declaration);
202             }
203             
204             // could be optimized out, but does need to preserve unread variables ?
205
if (!generateCurrentLocalVar) {
206                 if (local.declaration != null && compilerOptions().preserveAllLocalVariables) {
207                     generateCurrentLocalVar = true; // force it to be preserved in the generated code
208
local.useFlag = LocalVariableBinding.USED;
209                 }
210             }
211             
212             // allocate variable
213
if (generateCurrentLocalVar) {
214
215                 if (local.declaration != null) {
216                     codeStream.record(local); // record user-defined local variables for attribute generation
217
}
218                 // assign variable position
219
local.resolvedPosition = this.offset;
220
221                 if ((local.type == TypeBinding.LONG) || (local.type == TypeBinding.DOUBLE)) {
222                     this.offset += 2;
223                 } else {
224                     this.offset++;
225                 }
226                 if (this.offset > 0xFFFF) { // no more than 65535 words of locals
227
this.problemReporter().noMoreAvailableSpaceForLocal(
228                         local,
229                         local.declaration == null ? (ASTNode)this.methodScope().referenceContext : local.declaration);
230                 }
231             } else {
232                 local.resolvedPosition = -1; // not generated
233
}
234             hasMoreVariables = ++ilocal < maxLocals;
235         }
236     }
237     if (this.offset > this.maxOffset)
238         this.maxOffset = this.offset;
239 }
240
241 /*
242  * Record the suitable binding denoting a synthetic field or constructor argument,
243  * mapping to the actual outer local variable in the scope context.
244  * Note that this may not need any effect, in case the outer local variable does not
245  * need to be emulated and can directly be used as is (using its back pointer to its
246  * declaring scope).
247  */

248 public void emulateOuterAccess(LocalVariableBinding outerLocalVariable) {
249     BlockScope outerVariableScope = outerLocalVariable.declaringScope;
250     if (outerVariableScope == null)
251         return; // no need to further emulate as already inserted (val$this$0)
252
MethodScope currentMethodScope = this.methodScope();
253     if (outerVariableScope.methodScope() != currentMethodScope) {
254         NestedTypeBinding currentType = (NestedTypeBinding) this.enclosingSourceType();
255
256         //do nothing for member types, pre emulation was performed already
257
if (!currentType.isLocalType()) {
258             return;
259         }
260         // must also add a synthetic field if we're not inside a constructor
261
if (!currentMethodScope.isInsideInitializerOrConstructor()) {
262             currentType.addSyntheticArgumentAndField(outerLocalVariable);
263         } else {
264             currentType.addSyntheticArgument(outerLocalVariable);
265         }
266     }
267 }
268
269 /* Note that it must never produce a direct access to the targetEnclosingType,
270  * but instead a field sequence (this$2.this$1.this$0) so as to handle such a test case:
271  *
272  * class XX {
273  * void foo() {
274  * class A {
275  * class B {
276  * class C {
277  * boolean foo() {
278  * return (Object) A.this == (Object) B.this;
279  * }
280  * }
281  * }
282  * }
283  * new A().new B().new C();
284  * }
285  * }
286  * where we only want to deal with ONE enclosing instance for C (could not figure out an A for C)
287  */

288 public final ReferenceBinding findLocalType(char[] name) {
289     long compliance = compilerOptions().complianceLevel;
290     for (int i = this.subscopeCount-1; i >= 0; i--) {
291         if (this.subscopes[i] instanceof ClassScope) {
292             LocalTypeBinding sourceType = (LocalTypeBinding)((ClassScope) this.subscopes[i]).referenceContext.binding;
293             // from 1.4 on, local types should not be accessed across switch case blocks (52221)
294
if (compliance >= ClassFileConstants.JDK1_4 && sourceType.enclosingCase != null) {
295                 if (!this.isInsideCase(sourceType.enclosingCase)) {
296                     continue;
297                 }
298             }
299             if (CharOperation.equals(sourceType.sourceName(), name))
300                 return sourceType;
301         }
302     }
303     return null;
304 }
305
306 /**
307  * Returns all declarations of most specific locals containing a given position in their source range.
308  * This code does not recurse in nested types.
309  * Returned array may have null values at trailing indexes.
310  */

311 public LocalDeclaration[] findLocalVariableDeclarations(int position) {
312     // local variable init
313
int ilocal = 0, maxLocals = this.localIndex;
314     boolean hasMoreVariables = maxLocals > 0;
315     LocalDeclaration[] localDeclarations = null;
316     int declPtr = 0;
317
318     // scope init
319
int iscope = 0, maxScopes = this.subscopeCount;
320     boolean hasMoreScopes = maxScopes > 0;
321
322     // iterate scopes and variables in parallel
323
while (hasMoreVariables || hasMoreScopes) {
324         if (hasMoreScopes
325             && (!hasMoreVariables || (this.subscopes[iscope].startIndex() <= ilocal))) {
326             // consider subscope first
327
Scope subscope = this.subscopes[iscope];
328             if (subscope.kind == Scope.BLOCK_SCOPE) { // do not dive in nested types
329
localDeclarations = ((BlockScope)subscope).findLocalVariableDeclarations(position);
330                 if (localDeclarations != null) {
331                     return localDeclarations;
332                 }
333             }
334             hasMoreScopes = ++iscope < maxScopes;
335         } else {
336             // consider variable first
337
LocalVariableBinding local = this.locals[ilocal]; // if no local at all, will be locals[ilocal]==null
338
if (local != null) {
339                 LocalDeclaration localDecl = local.declaration;
340                 if (localDecl != null) {
341                     if (localDecl.declarationSourceStart <= position) {
342                         if (position <= localDecl.declarationSourceEnd) {
343                             if (localDeclarations == null) {
344                                 localDeclarations = new LocalDeclaration[maxLocals];
345                             }
346                             localDeclarations[declPtr++] = localDecl;
347                         }
348                     } else {
349                         return localDeclarations;
350                     }
351                 }
352             }
353             hasMoreVariables = ++ilocal < maxLocals;
354             if (!hasMoreVariables && localDeclarations != null) {
355                 return localDeclarations;
356             }
357         }
358     }
359     return null;
360 }
361
362 public LocalVariableBinding findVariable(char[] variableName) {
363     int varLength = variableName.length;
364     for (int i = this.localIndex-1; i >= 0; i--) { // lookup backward to reach latest additions first
365
LocalVariableBinding local;
366         char[] localName;
367         if ((localName = (local = this.locals[i]).name).length == varLength && CharOperation.equals(localName, variableName))
368             return local;
369     }
370     return null;
371 }
372
373 /* API
374  * flag is a mask of the following values VARIABLE (= FIELD or LOCAL), TYPE.
375  * Only bindings corresponding to the mask will be answered.
376  *
377  * if the VARIABLE mask is set then
378  * If the first name provided is a field (or local) then the field (or local) is answered
379  * Otherwise, package names and type names are consumed until a field is found.
380  * In this case, the field is answered.
381  *
382  * if the TYPE mask is set,
383  * package names and type names are consumed until the end of the input.
384  * Only if all of the input is consumed is the type answered
385  *
386  * All other conditions are errors, and a problem binding is returned.
387  *
388  * NOTE: If a problem binding is returned, senders should extract the compound name
389  * from the binding & not assume the problem applies to the entire compoundName.
390  *
391  * The VARIABLE mask has precedence over the TYPE mask.
392  *
393  * InvocationSite implements
394  * isSuperAccess(); this is used to determine if the discovered field is visible.
395  * setFieldIndex(int); this is used to record the number of names that were consumed.
396  *
397  * For example, getBinding({"foo","y","q", VARIABLE, site) will answer
398  * the binding for the field or local named "foo" (or an error binding if none exists).
399  * In addition, setFieldIndex(1) will be sent to the invocation site.
400  * If a type named "foo" exists, it will not be detected (and an error binding will be answered)
401  *
402  * IMPORTANT NOTE: This method is written under the assumption that compoundName is longer than length 1.
403  */

404 public Binding getBinding(char[][] compoundName, int mask, InvocationSite invocationSite, boolean needResolve) {
405     Binding binding = getBinding(compoundName[0], mask | Binding.TYPE | Binding.PACKAGE, invocationSite, needResolve);
406     invocationSite.setFieldIndex(1);
407     if (binding instanceof VariableBinding) return binding;
408     CompilationUnitScope unitScope = compilationUnitScope();
409     // in the problem case, we want to ensure we record the qualified dependency in case a type is added
410
// and we do not know that its package was also added (can happen with CompilationParticipants)
411
unitScope.recordQualifiedReference(compoundName);
412     if (!binding.isValidBinding()) return binding;
413
414     int length = compoundName.length;
415     int currentIndex = 1;
416     foundType : if (binding instanceof PackageBinding) {
417         PackageBinding packageBinding = (PackageBinding) binding;
418         while (currentIndex < length) {
419             unitScope.recordReference(packageBinding.compoundName, compoundName[currentIndex]);
420             binding = packageBinding.getTypeOrPackage(compoundName[currentIndex++]);
421             invocationSite.setFieldIndex(currentIndex);
422             if (binding == null) {
423                 if (currentIndex == length) {
424                     // must be a type if its the last name, otherwise we have no idea if its a package or type
425
return new ProblemReferenceBinding(
426                         CharOperation.subarray(compoundName, 0, currentIndex),
427                         null,
428                         ProblemReasons.NotFound);
429                 }
430                 return new ProblemBinding(
431                     CharOperation.subarray(compoundName, 0, currentIndex),
432                     ProblemReasons.NotFound);
433             }
434             if (binding instanceof ReferenceBinding) {
435                 if (!binding.isValidBinding())
436                     return new ProblemReferenceBinding(
437                         CharOperation.subarray(compoundName, 0, currentIndex),
438                         ((ReferenceBinding)binding).closestMatch(),
439                         binding.problemId());
440                 if (!((ReferenceBinding) binding).canBeSeenBy(this))
441                     return new ProblemReferenceBinding(
442                         CharOperation.subarray(compoundName, 0, currentIndex),
443                         (ReferenceBinding) binding,
444                         ProblemReasons.NotVisible);
445                 break foundType;
446             }
447             packageBinding = (PackageBinding) binding;
448         }
449
450         // It is illegal to request a PACKAGE from this method.
451
return new ProblemReferenceBinding(
452             CharOperation.subarray(compoundName, 0, currentIndex),
453             null,
454             ProblemReasons.NotFound);
455     }
456
457     // know binding is now a ReferenceBinding
458
ReferenceBinding referenceBinding = (ReferenceBinding) binding;
459     binding = environment().convertToRawType(referenceBinding);
460     if (invocationSite instanceof ASTNode) {
461         ASTNode invocationNode = (ASTNode) invocationSite;
462         if (invocationNode.isTypeUseDeprecated(referenceBinding, this)) {
463             problemReporter().deprecatedType(referenceBinding, invocationNode);
464         }
465     }
466     while (currentIndex < length) {
467         referenceBinding = (ReferenceBinding) binding;
468         char[] nextName = compoundName[currentIndex++];
469         invocationSite.setFieldIndex(currentIndex);
470         invocationSite.setActualReceiverType(referenceBinding);
471         if ((mask & Binding.FIELD) != 0 && (binding = findField(referenceBinding, nextName, invocationSite, true /*resolve*/)) != null) {
472             if (!binding.isValidBinding()) {
473                 return new ProblemFieldBinding(
474                     ((ProblemFieldBinding)binding).closestMatch,
475                     ((ProblemFieldBinding)binding).declaringClass,
476                     CharOperation.concatWith(CharOperation.subarray(compoundName, 0, currentIndex), '.'),
477                     binding.problemId());
478             }
479             break; // binding is now a field
480
}
481         if ((binding = findMemberType(nextName, referenceBinding)) == null) {
482             if ((mask & Binding.FIELD) != 0) {
483                 return new ProblemBinding(
484                     CharOperation.subarray(compoundName, 0, currentIndex),
485                     referenceBinding,
486                     ProblemReasons.NotFound);
487             }
488             return new ProblemReferenceBinding(
489                 CharOperation.subarray(compoundName, 0, currentIndex),
490                 referenceBinding,
491                 ProblemReasons.NotFound);
492         }
493         // binding is a ReferenceBinding
494
if (!binding.isValidBinding())
495             return new ProblemReferenceBinding(
496                 CharOperation.subarray(compoundName, 0, currentIndex),
497                 ((ReferenceBinding)binding).closestMatch(),
498                 binding.problemId());
499         if (invocationSite instanceof ASTNode) {
500             referenceBinding = (ReferenceBinding) binding;
501             ASTNode invocationNode = (ASTNode) invocationSite;
502             if (invocationNode.isTypeUseDeprecated(referenceBinding, this)) {
503                 problemReporter().deprecatedType(referenceBinding, invocationNode);
504             }
505         }
506     }
507     if ((mask & Binding.FIELD) != 0 && (binding instanceof FieldBinding)) {
508         // was looking for a field and found a field
509
FieldBinding field = (FieldBinding) binding;
510         if (!field.isStatic())
511             return new ProblemFieldBinding(
512                 field,
513                 field.declaringClass,
514                 CharOperation.concatWith(CharOperation.subarray(compoundName, 0, currentIndex), '.'),
515                 ProblemReasons.NonStaticReferenceInStaticContext);
516         return binding;
517     }
518     if ((mask & Binding.TYPE) != 0 && (binding instanceof ReferenceBinding)) {
519         // was looking for a type and found a type
520
return binding;
521     }
522
523     // handle the case when a field or type was asked for but we resolved the compoundName to a type or field
524
return new ProblemBinding(
525         CharOperation.subarray(compoundName, 0, currentIndex),
526         ProblemReasons.NotFound);
527 }
528
529 // Added for code assist... NOT Public API
530
public final Binding getBinding(char[][] compoundName, InvocationSite invocationSite) {
531     int currentIndex = 0;
532     int length = compoundName.length;
533     Binding binding =
534         getBinding(
535             compoundName[currentIndex++],
536             Binding.VARIABLE | Binding.TYPE | Binding.PACKAGE,
537             invocationSite,
538             true /*resolve*/);
539     if (!binding.isValidBinding())
540         return binding;
541
542     foundType : if (binding instanceof PackageBinding) {
543         while (currentIndex < length) {
544             PackageBinding packageBinding = (PackageBinding) binding;
545             binding = packageBinding.getTypeOrPackage(compoundName[currentIndex++]);
546             if (binding == null) {
547                 if (currentIndex == length) {
548                     // must be a type if its the last name, otherwise we have no idea if its a package or type
549
return new ProblemReferenceBinding(
550                         CharOperation.subarray(compoundName, 0, currentIndex),
551                         null,
552                         ProblemReasons.NotFound);
553                 }
554                 return new ProblemBinding(
555                     CharOperation.subarray(compoundName, 0, currentIndex),
556                     ProblemReasons.NotFound);
557             }
558             if (binding instanceof ReferenceBinding) {
559                 if (!binding.isValidBinding())
560                     return new ProblemReferenceBinding(
561                         CharOperation.subarray(compoundName, 0, currentIndex),
562                         ((ReferenceBinding)binding).closestMatch(),
563                         binding.problemId());
564                 if (!((ReferenceBinding) binding).canBeSeenBy(this))
565                     return new ProblemReferenceBinding(
566                         CharOperation.subarray(compoundName, 0, currentIndex),
567                         (ReferenceBinding) binding,
568                         ProblemReasons.NotVisible);
569                 break foundType;
570             }
571         }
572         return binding;
573     }
574
575     foundField : if (binding instanceof ReferenceBinding) {
576         while (currentIndex < length) {
577             ReferenceBinding typeBinding = (ReferenceBinding) binding;
578             char[] nextName = compoundName[currentIndex++];
579             if ((binding = findField(typeBinding, nextName, invocationSite, true /*resolve*/)) != null) {
580                 if (!binding.isValidBinding()) {
581                     return new ProblemFieldBinding(
582                         (FieldBinding) binding,
583                         ((FieldBinding) binding).declaringClass,
584                         CharOperation.concatWith(CharOperation.subarray(compoundName, 0, currentIndex), '.'),
585                         binding.problemId());
586                 }
587                 if (!((FieldBinding) binding).isStatic())
588                     return new ProblemFieldBinding(
589                         (FieldBinding) binding,
590                         ((FieldBinding) binding).declaringClass,
591                         CharOperation.concatWith(CharOperation.subarray(compoundName, 0, currentIndex), '.'),
592                         ProblemReasons.NonStaticReferenceInStaticContext);
593                 break foundField; // binding is now a field
594
}
595             if ((binding = findMemberType(nextName, typeBinding)) == null) {
596                 return new ProblemBinding(
597                     CharOperation.subarray(compoundName, 0, currentIndex),
598                     typeBinding,
599                     ProblemReasons.NotFound);
600             }
601             if (!binding.isValidBinding()) {
602                 return new ProblemReferenceBinding(
603                     CharOperation.subarray(compoundName, 0, currentIndex),
604                     ((ReferenceBinding)binding).closestMatch(),
605                     binding.problemId());
606             }
607         }
608         return binding;
609     }
610
611     VariableBinding variableBinding = (VariableBinding) binding;
612     while (currentIndex < length) {
613         TypeBinding typeBinding = variableBinding.type;
614         if (typeBinding == null) {
615             return new ProblemFieldBinding(
616                 null,
617                 null,
618                 CharOperation.concatWith(CharOperation.subarray(compoundName, 0, currentIndex), '.'),
619                 ProblemReasons.NotFound);
620         }
621         variableBinding = findField(typeBinding, compoundName[currentIndex++], invocationSite, true /*resolve*/);
622         if (variableBinding == null) {
623             return new ProblemFieldBinding(
624                 null,
625                 null,
626                 CharOperation.concatWith(CharOperation.subarray(compoundName, 0, currentIndex), '.'),
627                 ProblemReasons.NotFound);
628         }
629         if (!variableBinding.isValidBinding())
630             return variableBinding;
631     }
632     return variableBinding;
633 }
634
635 /*
636  * This retrieves the argument that maps to an enclosing instance of the suitable type,
637  * if not found then answers nil -- do not create one
638  *
639  * #implicitThis : the implicit this will be ok
640  * #((arg) this$n) : available as a constructor arg
641  * #((arg) this$n ... this$p) : available as as a constructor arg + a sequence of fields
642  * #((fieldDescr) this$n ... this$p) : available as a sequence of fields
643  * nil : not found
644  *
645  * Note that this algorithm should answer the shortest possible sequence when
646  * shortcuts are available:
647  * this$0 . this$0 . this$0
648  * instead of
649  * this$2 . this$1 . this$0 . this$1 . this$0
650  * thus the code generation will be more compact and runtime faster
651  */

652 public VariableBinding[] getEmulationPath(LocalVariableBinding outerLocalVariable) {
653     MethodScope currentMethodScope = this.methodScope();
654     SourceTypeBinding sourceType = currentMethodScope.enclosingSourceType();
655
656     // identity check
657
BlockScope variableScope = outerLocalVariable.declaringScope;
658     if (variableScope == null /*val$this$0*/ || currentMethodScope == variableScope.methodScope()) {
659         return new VariableBinding[] { outerLocalVariable };
660         // implicit this is good enough
661
}
662     // use synthetic constructor arguments if possible
663
if (currentMethodScope.isInsideInitializerOrConstructor()
664         && (sourceType.isNestedType())) {
665         SyntheticArgumentBinding syntheticArg;
666         if ((syntheticArg = ((NestedTypeBinding) sourceType).getSyntheticArgument(outerLocalVariable)) != null) {
667             return new VariableBinding[] { syntheticArg };
668         }
669     }
670     // use a synthetic field then
671
if (!currentMethodScope.isStatic) {
672         FieldBinding syntheticField;
673         if ((syntheticField = sourceType.getSyntheticField(outerLocalVariable)) != null) {
674             return new VariableBinding[] { syntheticField };
675         }
676     }
677     return null;
678 }
679
680 /*
681  * This retrieves the argument that maps to an enclosing instance of the suitable type,
682  * if not found then answers nil -- do not create one
683  *
684  * #implicitThis : the implicit this will be ok
685  * #((arg) this$n) : available as a constructor arg
686  * #((arg) this$n access$m... access$p) : available as as a constructor arg + a sequence of synthetic accessors to synthetic fields
687  * #((fieldDescr) this$n access#m... access$p) : available as a first synthetic field + a sequence of synthetic accessors to synthetic fields
688  * null : not found
689  * jls 15.9.2 + http://www.ergnosis.com/java-spec-report/java-language/jls-8.8.5.1-d.html
690  */

691 public Object JavaDoc[] getEmulationPath(ReferenceBinding targetEnclosingType, boolean onlyExactMatch, boolean denyEnclosingArgInConstructorCall) {
692     MethodScope currentMethodScope = this.methodScope();
693     SourceTypeBinding sourceType = currentMethodScope.enclosingSourceType();
694
695     // use 'this' if possible
696
if (!currentMethodScope.isStatic && !currentMethodScope.isConstructorCall) {
697         if (sourceType == targetEnclosingType || (!onlyExactMatch && sourceType.findSuperTypeWithSameErasure(targetEnclosingType) != null)) {
698             return BlockScope.EmulationPathToImplicitThis; // implicit this is good enough
699
}
700     }
701     if (!sourceType.isNestedType() || sourceType.isStatic()) { // no emulation from within non-inner types
702
if (currentMethodScope.isConstructorCall) {
703             return BlockScope.NoEnclosingInstanceInConstructorCall;
704         } else if (currentMethodScope.isStatic){
705             return BlockScope.NoEnclosingInstanceInStaticContext;
706         }
707         return null;
708     }
709     boolean insideConstructor = currentMethodScope.isInsideInitializerOrConstructor();
710     // use synthetic constructor arguments if possible
711
if (insideConstructor) {
712         SyntheticArgumentBinding syntheticArg;
713         if ((syntheticArg = ((NestedTypeBinding) sourceType).getSyntheticArgument(targetEnclosingType, onlyExactMatch)) != null) {
714             // reject allocation and super constructor call
715
if (denyEnclosingArgInConstructorCall
716                     && currentMethodScope.isConstructorCall
717                     && (sourceType == targetEnclosingType || (!onlyExactMatch && sourceType.findSuperTypeWithSameErasure(targetEnclosingType) != null))) {
718                 return BlockScope.NoEnclosingInstanceInConstructorCall;
719             }
720             return new Object JavaDoc[] { syntheticArg };
721         }
722     }
723
724     // use a direct synthetic field then
725
if (currentMethodScope.isStatic) {
726         return BlockScope.NoEnclosingInstanceInStaticContext;
727     }
728     if (sourceType.isAnonymousType()) {
729         ReferenceBinding enclosingType = sourceType.enclosingType();
730         if (enclosingType.isNestedType()) {
731             NestedTypeBinding nestedEnclosingType = (NestedTypeBinding) enclosingType;
732             SyntheticArgumentBinding enclosingArgument = nestedEnclosingType.getSyntheticArgument(nestedEnclosingType.enclosingType(), onlyExactMatch);
733             if (enclosingArgument != null) {
734                 FieldBinding syntheticField = sourceType.getSyntheticField(enclosingArgument);
735                 if (syntheticField != null) {
736                     if (syntheticField.type == targetEnclosingType || (!onlyExactMatch && ((ReferenceBinding)syntheticField.type).findSuperTypeWithSameErasure(targetEnclosingType) != null))
737                         return new Object JavaDoc[] { syntheticField };
738                 }
739             }
740         }
741     }
742     FieldBinding syntheticField = sourceType.getSyntheticField(targetEnclosingType, onlyExactMatch);
743     if (syntheticField != null) {
744         if (currentMethodScope.isConstructorCall){
745             return BlockScope.NoEnclosingInstanceInConstructorCall;
746         }
747         return new Object JavaDoc[] { syntheticField };
748     }
749
750     // could be reached through a sequence of enclosing instance link (nested members)
751
Object JavaDoc[] path = new Object JavaDoc[2]; // probably at least 2 of them
752
ReferenceBinding currentType = sourceType.enclosingType();
753     if (insideConstructor) {
754         path[0] = ((NestedTypeBinding) sourceType).getSyntheticArgument(currentType, onlyExactMatch);
755     } else {
756         if (currentMethodScope.isConstructorCall){
757             return BlockScope.NoEnclosingInstanceInConstructorCall;
758         }
759         path[0] = sourceType.getSyntheticField(currentType, onlyExactMatch);
760     }
761     if (path[0] != null) { // keep accumulating
762

763         int count = 1;
764         ReferenceBinding currentEnclosingType;
765         while ((currentEnclosingType = currentType.enclosingType()) != null) {
766
767             //done?
768
if (currentType == targetEnclosingType
769                 || (!onlyExactMatch && currentType.findSuperTypeWithSameErasure(targetEnclosingType) != null)) break;
770
771             if (currentMethodScope != null) {
772                 currentMethodScope = currentMethodScope.enclosingMethodScope();
773                 if (currentMethodScope != null && currentMethodScope.isConstructorCall){
774                     return BlockScope.NoEnclosingInstanceInConstructorCall;
775                 }
776                 if (currentMethodScope != null && currentMethodScope.isStatic){
777                     return BlockScope.NoEnclosingInstanceInStaticContext;
778                 }
779             }
780             
781             syntheticField = ((NestedTypeBinding) currentType).getSyntheticField(currentEnclosingType, onlyExactMatch);
782             if (syntheticField == null) break;
783
784             // append inside the path
785
if (count == path.length) {
786                 System.arraycopy(path, 0, (path = new Object JavaDoc[count + 1]), 0, count);
787             }
788             // private access emulation is necessary since synthetic field is private
789
path[count++] = ((SourceTypeBinding) syntheticField.declaringClass).addSyntheticMethod(syntheticField, true);
790             currentType = currentEnclosingType;
791         }
792         if (currentType == targetEnclosingType
793             || (!onlyExactMatch && currentType.findSuperTypeWithSameErasure(targetEnclosingType) != null)) {
794             return path;
795         }
796     }
797     return null;
798 }
799
800 /* Answer true if the variable name already exists within the receiver's scope.
801  */

802 public final boolean isDuplicateLocalVariable(char[] name) {
803     BlockScope current = this;
804     while (true) {
805         for (int i = 0; i < this.localIndex; i++) {
806             if (CharOperation.equals(name, current.locals[i].name))
807                 return true;
808         }
809         if (current.kind != Scope.BLOCK_SCOPE) return false;
810         current = (BlockScope)current.parent;
811     }
812 }
813
814 public int maxShiftedOffset() {
815     int max = -1;
816     if (this.shiftScopes != null){
817         for (int i = 0, length = this.shiftScopes.length; i < length; i++){
818             int subMaxOffset = this.shiftScopes[i].maxOffset;
819             if (subMaxOffset > max) max = subMaxOffset;
820         }
821     }
822     return max;
823 }
824
825 /* Answer the problem reporter to use for raising new problems.
826  *
827  * Note that as a side-effect, this updates the current reference context
828  * (unit, type or method) in case the problem handler decides it is necessary
829  * to abort.
830  */

831 public ProblemReporter problemReporter() {
832     return outerMostMethodScope().problemReporter();
833 }
834
835 /*
836  * Code responsible to request some more emulation work inside the invocation type, so as to supply
837  * correct synthetic arguments to any allocation of the target type.
838  */

839 public void propagateInnerEmulation(ReferenceBinding targetType, boolean isEnclosingInstanceSupplied) {
840     // no need to propagate enclosing instances, they got eagerly allocated already.
841

842     SyntheticArgumentBinding[] syntheticArguments;
843     if ((syntheticArguments = targetType.syntheticOuterLocalVariables()) != null) {
844         for (int i = 0, max = syntheticArguments.length; i < max; i++) {
845             SyntheticArgumentBinding syntheticArg = syntheticArguments[i];
846             // need to filter out the one that could match a supplied enclosing instance
847
if (!(isEnclosingInstanceSupplied
848                 && (syntheticArg.type == targetType.enclosingType()))) {
849                 this.emulateOuterAccess(syntheticArg.actualOuterLocalVariable);
850             }
851         }
852     }
853 }
854
855 /* Answer the reference type of this scope.
856  *
857  * It is the nearest enclosing type of this scope.
858  */

859 public TypeDeclaration referenceType() {
860     return methodScope().referenceType();
861 }
862
863 /*
864  * Answer the index of this scope relatively to its parent.
865  * For method scope, answers -1 (not a classScope relative position)
866  */

867 public int scopeIndex() {
868     if (this instanceof MethodScope) return -1;
869     BlockScope parentScope = (BlockScope)this.parent;
870     Scope[] parentSubscopes = parentScope.subscopes;
871     for (int i = 0, max = parentScope.subscopeCount; i < max; i++) {
872         if (parentSubscopes[i] == this) return i;
873     }
874     return -1;
875 }
876
877 // start position in this scope - for ordering scopes vs. variables
878
int startIndex() {
879     return this.startIndex;
880 }
881
882 public String JavaDoc toString() {
883     return toString(0);
884 }
885
886 public String JavaDoc toString(int tab) {
887     String JavaDoc s = basicToString(tab);
888     for (int i = 0; i < this.subscopeCount; i++)
889         if (this.subscopes[i] instanceof BlockScope)
890             s += ((BlockScope) this.subscopes[i]).toString(tab + 1) + "\n"; //$NON-NLS-1$
891
return s;
892 }
893 }
894
Popular Tags