removed Makefile; lifted repo/org.ibex.tool/src/ to src/
[org.ibex.tool.git] / src / org / eclipse / jdt / internal / compiler / lookup / BlockScope.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2004 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials 
4  * are made available under the terms of the Common Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/cpl-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 final static VariableBinding[] EmulationPathToImplicitThis = {};
34         public final static VariableBinding[] NoEnclosingInstanceInConstructorCall = {};
35         public final static VariableBinding[] NoEnclosingInstanceInStaticContext = {};
36
37         public Scope[] subscopes = new Scope[1]; // need access from code assist
38         public int subscopeCount = 0; // need access from code assist
39
40         // record the current case statement being processed (for entire switch case block).
41         public CaseStatement switchCase; // from 1.4 on, local types should not be accessed across switch case blocks (52221)
42
43         protected BlockScope(int kind, Scope parent) {
44
45                 super(kind, parent);
46         }
47
48         public BlockScope(BlockScope parent) {
49
50                 this(parent, true);
51         }
52
53         public BlockScope(BlockScope parent, boolean addToParentScope) {
54
55                 this(BLOCK_SCOPE, parent);
56                 locals = new LocalVariableBinding[5];
57                 if (addToParentScope) parent.addSubscope(this);
58                 this.startIndex = parent.localIndex;
59         }
60
61         public BlockScope(BlockScope parent, int variableCount) {
62
63                 this(BLOCK_SCOPE, parent);
64                 locals = new LocalVariableBinding[variableCount];
65                 parent.addSubscope(this);
66                 this.startIndex = parent.localIndex;
67         }
68
69         /* Create the class scope & binding for the anonymous type.
70          */
71         public final void addAnonymousType(
72                 TypeDeclaration anonymousType,
73                 ReferenceBinding superBinding) {
74
75                 ClassScope anonymousClassScope = new ClassScope(this, anonymousType);
76                 anonymousClassScope.buildAnonymousTypeBinding(
77                         enclosingSourceType(),
78                         superBinding);
79         }
80
81         /* Create the class scope & binding for the local type.
82          */
83         public final void addLocalType(TypeDeclaration localType) {
84
85                 // check that the localType does not conflict with an enclosing type
86                 ReferenceBinding type = enclosingSourceType();
87                 do {
88                         if (CharOperation.equals(type.sourceName, localType.name)) {
89                                 problemReporter().hidingEnclosingType(localType);
90                                 return;
91                         }
92                         type = type.enclosingType();
93                 } while (type != null);
94
95                 // check that the localType does not conflict with another sibling local type
96                 Scope scope = this;
97                 do {
98                         if (((BlockScope) scope).findLocalType(localType.name) != null) {
99                                 problemReporter().duplicateNestedType(localType);
100                                 return;
101                         }
102                 } while ((scope = scope.parent) instanceof BlockScope);
103
104                 ClassScope localTypeScope = new ClassScope(this, localType);
105                 addSubscope(localTypeScope);
106                 localTypeScope.buildLocalTypeBinding(enclosingSourceType());
107         }
108
109         /* Insert a local variable into a given scope, updating its position
110          * and checking there are not too many locals or arguments allocated.
111          */
112         public final void addLocalVariable(LocalVariableBinding binding) {
113
114                 checkAndSetModifiersForVariable(binding);
115
116                 // insert local in scope
117                 if (localIndex == locals.length)
118                         System.arraycopy(
119                                 locals,
120                                 0,
121                                 (locals = new LocalVariableBinding[localIndex * 2]),
122                                 0,
123                                 localIndex);
124                 locals[localIndex++] = binding;
125
126                 // update local variable binding 
127                 binding.declaringScope = this;
128                 binding.id = this.outerMostMethodScope().analysisIndex++;
129                 // share the outermost method scope analysisIndex
130         }
131
132         public void addSubscope(Scope childScope) {
133                 if (subscopeCount == subscopes.length)
134                         System.arraycopy(
135                                 subscopes,
136                                 0,
137                                 (subscopes = new Scope[subscopeCount * 2]),
138                                 0,
139                                 subscopeCount);
140                 subscopes[subscopeCount++] = childScope;
141         }
142
143         /* Answer true if the receiver is suitable for assigning final blank fields.
144          *
145          * in other words, it is inside an initializer, a constructor or a clinit 
146          */
147         public final boolean allowBlankFinalFieldAssignment(FieldBinding binding) {
148
149                 if (enclosingSourceType() != binding.declaringClass)
150                         return false;
151
152                 MethodScope methodScope = methodScope();
153                 if (methodScope.isStatic != binding.isStatic())
154                         return false;
155                 return methodScope.isInsideInitializer() // inside initializer
156                                 || ((AbstractMethodDeclaration) methodScope.referenceContext)
157                                         .isInitializationMethod(); // inside constructor or clinit
158         }
159         String basicToString(int tab) {
160                 String newLine = "\n"; //$NON-NLS-1$
161                 for (int i = tab; --i >= 0;)
162                         newLine += "\t"; //$NON-NLS-1$
163
164                 String s = newLine + "--- Block Scope ---"; //$NON-NLS-1$
165                 newLine += "\t"; //$NON-NLS-1$
166                 s += newLine + "locals:"; //$NON-NLS-1$
167                 for (int i = 0; i < localIndex; i++)
168                         s += newLine + "\t" + locals[i].toString(); //$NON-NLS-1$
169                 s += newLine + "startIndex = " + startIndex; //$NON-NLS-1$
170                 return s;
171         }
172
173         private void checkAndSetModifiersForVariable(LocalVariableBinding varBinding) {
174
175                 int modifiers = varBinding.modifiers;
176                 if ((modifiers & AccAlternateModifierProblem) != 0 && varBinding.declaration != null){
177                         problemReporter().duplicateModifierForVariable(varBinding.declaration, this instanceof MethodScope);
178                 }
179                 int realModifiers = modifiers & AccJustFlag;
180                 
181                 int unexpectedModifiers = ~AccFinal;
182                 if ((realModifiers & unexpectedModifiers) != 0 && varBinding.declaration != null){ 
183                         problemReporter().illegalModifierForVariable(varBinding.declaration, this instanceof MethodScope);
184                 }
185                 varBinding.modifiers = modifiers;
186         }
187
188         /* Compute variable positions in scopes given an initial position offset
189          * ignoring unused local variables.
190          * 
191          * No argument is expected here (ilocal is the first non-argument local of the outermost scope)
192          * Arguments are managed by the MethodScope method
193          */
194         void computeLocalVariablePositions(int ilocal, int initOffset, CodeStream codeStream) {
195
196                 this.offset = initOffset;
197                 this.maxOffset = initOffset;
198
199                 // local variable init
200                 int maxLocals = this.localIndex;
201                 boolean hasMoreVariables = ilocal < maxLocals;
202
203                 // scope init
204                 int iscope = 0, maxScopes = this.subscopeCount;
205                 boolean hasMoreScopes = maxScopes > 0;
206
207                 // iterate scopes and variables in parallel
208                 while (hasMoreVariables || hasMoreScopes) {
209                         if (hasMoreScopes
210                                 && (!hasMoreVariables || (subscopes[iscope].startIndex() <= ilocal))) {
211                                 // consider subscope first
212                                 if (subscopes[iscope] instanceof BlockScope) {
213                                         BlockScope subscope = (BlockScope) subscopes[iscope];
214                                         int subOffset = subscope.shiftScopes == null ? this.offset : subscope.maxShiftedOffset();
215                                         subscope.computeLocalVariablePositions(0, subOffset, codeStream);
216                                         if (subscope.maxOffset > this.maxOffset)
217                                                 this.maxOffset = subscope.maxOffset;
218                                 }
219                                 hasMoreScopes = ++iscope < maxScopes;
220                         } else {
221                                 
222                                 // consider variable first
223                                 LocalVariableBinding local = locals[ilocal]; // if no local at all, will be locals[ilocal]==null
224                                 
225                                 // check if variable is actually used, and may force it to be preserved
226                                 boolean generateCurrentLocalVar = (local.useFlag == LocalVariableBinding.USED && (local.constant == Constant.NotAConstant));
227                                         
228                                 // do not report fake used variable
229                                 if (local.useFlag == LocalVariableBinding.UNUSED
230                                         && (local.declaration != null) // unused (and non secret) local
231                                         && ((local.declaration.bits & ASTNode.IsLocalDeclarationReachableMASK) != 0)) { // declaration is reachable
232                                                 
233                                         if (!(local.declaration instanceof Argument))  // do not report unused catch arguments
234                                                 this.problemReporter().unusedLocalVariable(local.declaration);
235                                 }
236                                 
237                                 // could be optimized out, but does need to preserve unread variables ?
238                                 if (!generateCurrentLocalVar) {
239                                         if (local.declaration != null && environment().options.preserveAllLocalVariables) {
240                                                 generateCurrentLocalVar = true; // force it to be preserved in the generated code
241                                                 local.useFlag = LocalVariableBinding.USED;
242                                         }
243                                 }
244                                 
245                                 // allocate variable
246                                 if (generateCurrentLocalVar) {
247
248                                         if (local.declaration != null) {
249                                                 codeStream.record(local); // record user-defined local variables for attribute generation
250                                         }
251                                         // assign variable position
252                                         local.resolvedPosition = this.offset;
253
254                                         if ((local.type == LongBinding) || (local.type == DoubleBinding)) {
255                                                 this.offset += 2;
256                                         } else {
257                                                 this.offset++;
258                                         }
259                                         if (this.offset > 0xFFFF) { // no more than 65535 words of locals
260                                                 this.problemReporter().noMoreAvailableSpaceForLocal(
261                                                         local, 
262                                                         local.declaration == null ? (ASTNode)this.methodScope().referenceContext : local.declaration);
263                                         }
264                                 } else {
265                                         local.resolvedPosition = -1; // not generated
266                                 }
267                                 hasMoreVariables = ++ilocal < maxLocals;
268                         }
269                 }
270                 if (this.offset > this.maxOffset)
271                         this.maxOffset = this.offset;
272         }
273
274         /*
275          *      Record the suitable binding denoting a synthetic field or constructor argument,
276          * mapping to the actual outer local variable in the scope context.
277          * Note that this may not need any effect, in case the outer local variable does not
278          * need to be emulated and can directly be used as is (using its back pointer to its
279          * declaring scope).
280          */
281         public void emulateOuterAccess(LocalVariableBinding outerLocalVariable) {
282
283                 MethodScope currentMethodScope;
284                 if ((currentMethodScope = this.methodScope())
285                         != outerLocalVariable.declaringScope.methodScope()) {
286                         NestedTypeBinding currentType = (NestedTypeBinding) this.enclosingSourceType();
287
288                         //do nothing for member types, pre emulation was performed already
289                         if (!currentType.isLocalType()) {
290                                 return;
291                         }
292                         // must also add a synthetic field if we're not inside a constructor
293                         if (!currentMethodScope.isInsideInitializerOrConstructor()) {
294                                 currentType.addSyntheticArgumentAndField(outerLocalVariable);
295                         } else {
296                                 currentType.addSyntheticArgument(outerLocalVariable);
297                         }
298                 }
299         }
300
301         /* Note that it must never produce a direct access to the targetEnclosingType,
302          * but instead a field sequence (this$2.this$1.this$0) so as to handle such a test case:
303          *
304          * class XX {
305          *      void foo() {
306          *              class A {
307          *                      class B {
308          *                              class C {
309          *                                      boolean foo() {
310          *                                              return (Object) A.this == (Object) B.this;
311          *                                      }
312          *                              }
313          *                      }
314          *              }
315          *              new A().new B().new C();
316          *      }
317          * }
318          * where we only want to deal with ONE enclosing instance for C (could not figure out an A for C)
319          */
320         public final ReferenceBinding findLocalType(char[] name) {
321
322             long compliance = environment().options.complianceLevel;
323                 for (int i = 0, length = subscopeCount; i < length; i++) {
324                         if (subscopes[i] instanceof ClassScope) {
325                                 LocalTypeBinding sourceType = (LocalTypeBinding)((ClassScope) subscopes[i]).referenceContext.binding;
326                                 // from 1.4 on, local types should not be accessed across switch case blocks (52221)                            
327                                 if (compliance >= ClassFileConstants.JDK1_4 && sourceType.switchCase != this.switchCase) continue;
328                                 if (CharOperation.equals(sourceType.sourceName(), name))
329                                         return sourceType;
330                         }
331                 }
332                 return null;
333         }
334
335         public LocalVariableBinding findVariable(char[] variable) {
336
337                 int varLength = variable.length;
338                 for (int i = 0, length = locals.length; i < length; i++) {
339                         LocalVariableBinding local = locals[i];
340                         if (local == null)
341                                 return null;
342                         if (local.name.length == varLength && CharOperation.equals(local.name, variable))
343                                 return local;
344                 }
345                 return null;
346         }
347         /* API
348          * flag is a mask of the following values VARIABLE (= FIELD or LOCAL), TYPE.
349          * Only bindings corresponding to the mask will be answered.
350          *
351          *      if the VARIABLE mask is set then
352          *              If the first name provided is a field (or local) then the field (or local) is answered
353          *              Otherwise, package names and type names are consumed until a field is found.
354          *              In this case, the field is answered.
355          *
356          *      if the TYPE mask is set,
357          *              package names and type names are consumed until the end of the input.
358          *              Only if all of the input is consumed is the type answered
359          *
360          *      All other conditions are errors, and a problem binding is returned.
361          *      
362          *      NOTE: If a problem binding is returned, senders should extract the compound name
363          *      from the binding & not assume the problem applies to the entire compoundName.
364          *
365          *      The VARIABLE mask has precedence over the TYPE mask.
366          *
367          *      InvocationSite implements
368          *              isSuperAccess(); this is used to determine if the discovered field is visible.
369          *              setFieldIndex(int); this is used to record the number of names that were consumed.
370          *
371          *      For example, getBinding({"foo","y","q", VARIABLE, site) will answer
372          *      the binding for the field or local named "foo" (or an error binding if none exists).
373          *      In addition, setFieldIndex(1) will be sent to the invocation site.
374          *      If a type named "foo" exists, it will not be detected (and an error binding will be answered)
375          *
376          *      IMPORTANT NOTE: This method is written under the assumption that compoundName is longer than length 1.
377          */
378         public Binding getBinding(char[][] compoundName, int mask, InvocationSite invocationSite, boolean needResolve) {
379
380                 Binding binding = getBinding(compoundName[0], mask | TYPE | PACKAGE, invocationSite, needResolve);
381                 invocationSite.setFieldIndex(1);
382                 if (binding instanceof VariableBinding) return binding;
383                 compilationUnitScope().recordSimpleReference(compoundName[0]);
384                 if (!binding.isValidBinding()) return binding;
385
386                 int length = compoundName.length;
387                 int currentIndex = 1;
388                 foundType : if (binding instanceof PackageBinding) {
389                         PackageBinding packageBinding = (PackageBinding) binding;
390                         while (currentIndex < length) {
391                                 compilationUnitScope().recordReference(packageBinding.compoundName, compoundName[currentIndex]);
392                                 binding = packageBinding.getTypeOrPackage(compoundName[currentIndex++]);
393                                 invocationSite.setFieldIndex(currentIndex);
394                                 if (binding == null) {
395                                         if (currentIndex == length) {
396                                                 // must be a type if its the last name, otherwise we have no idea if its a package or type
397                                                 return new ProblemReferenceBinding(
398                                                         CharOperation.subarray(compoundName, 0, currentIndex),
399                                                         NotFound);
400                                         }
401                                         return new ProblemBinding(
402                                                 CharOperation.subarray(compoundName, 0, currentIndex),
403                                                 NotFound);
404                                 }
405                                 if (binding instanceof ReferenceBinding) {
406                                         if (!binding.isValidBinding())
407                                                 return new ProblemReferenceBinding(
408                                                         CharOperation.subarray(compoundName, 0, currentIndex),
409                                                         binding.problemId());
410                                         if (!((ReferenceBinding) binding).canBeSeenBy(this))
411                                                 return new ProblemReferenceBinding(
412                                                         CharOperation.subarray(compoundName, 0, currentIndex),
413                                                         (ReferenceBinding) binding,
414                                                         NotVisible);
415                                         break foundType;
416                                 }
417                                 packageBinding = (PackageBinding) binding;
418                         }
419
420                         // It is illegal to request a PACKAGE from this method.
421                         return new ProblemReferenceBinding(
422                                 CharOperation.subarray(compoundName, 0, currentIndex),
423                                 NotFound);
424                 }
425
426                 // know binding is now a ReferenceBinding
427                 while (currentIndex < length) {
428                         ReferenceBinding typeBinding = (ReferenceBinding) binding;
429                         char[] nextName = compoundName[currentIndex++];
430                         invocationSite.setFieldIndex(currentIndex);
431                         invocationSite.setActualReceiverType(typeBinding);
432                         if ((mask & FIELD) != 0 && (binding = findField(typeBinding, nextName, invocationSite, true /*resolve*/)) != null) {
433                                 if (!binding.isValidBinding())
434                                         return new ProblemFieldBinding(
435                                                 ((FieldBinding) binding).declaringClass,
436                                                 CharOperation.subarray(compoundName, 0, currentIndex),
437                                                 binding.problemId());
438                                 break; // binding is now a field
439                         }
440                         if ((binding = findMemberType(nextName, typeBinding)) == null) {
441                                 if ((mask & FIELD) != 0) {
442                                         return new ProblemBinding(
443                                                 CharOperation.subarray(compoundName, 0, currentIndex),
444                                                 typeBinding,
445                                                 NotFound);
446                                 } 
447                                 return new ProblemReferenceBinding(
448                                         CharOperation.subarray(compoundName, 0, currentIndex),
449                                         typeBinding,
450                                         NotFound);
451                         }
452                         if (!binding.isValidBinding())
453                                 return new ProblemReferenceBinding(
454                                         CharOperation.subarray(compoundName, 0, currentIndex),
455                                         binding.problemId());
456                 }
457                 if ((mask & FIELD) != 0 && (binding instanceof FieldBinding)) {
458                         // was looking for a field and found a field
459                         FieldBinding field = (FieldBinding) binding;
460                         if (!field.isStatic())
461                                 return new ProblemFieldBinding(
462                                         field.declaringClass,
463                                         CharOperation.subarray(compoundName, 0, currentIndex),
464                                         NonStaticReferenceInStaticContext);
465                         return binding;
466                 }
467                 if ((mask & TYPE) != 0 && (binding instanceof ReferenceBinding)) {
468                         // was looking for a type and found a type
469                         return binding;
470                 }
471
472                 // handle the case when a field or type was asked for but we resolved the compoundName to a type or field
473                 return new ProblemBinding(
474                         CharOperation.subarray(compoundName, 0, currentIndex),
475                         NotFound);
476         }
477
478         // Added for code assist... NOT Public API
479         public final Binding getBinding(
480                 char[][] compoundName,
481                 InvocationSite invocationSite) {
482                 int currentIndex = 0;
483                 int length = compoundName.length;
484                 Binding binding =
485                         getBinding(
486                                 compoundName[currentIndex++],
487                                 VARIABLE | TYPE | PACKAGE,
488                                 invocationSite, 
489                                 true /*resolve*/);
490                 if (!binding.isValidBinding())
491                         return binding;
492
493                 foundType : if (binding instanceof PackageBinding) {
494                         while (currentIndex < length) {
495                                 PackageBinding packageBinding = (PackageBinding) binding;
496                                 binding = packageBinding.getTypeOrPackage(compoundName[currentIndex++]);
497                                 if (binding == null) {
498                                         if (currentIndex == length) {
499                                                 // must be a type if its the last name, otherwise we have no idea if its a package or type
500                                                 return new ProblemReferenceBinding(
501                                                         CharOperation.subarray(compoundName, 0, currentIndex),
502                                                         NotFound);
503                                         }
504                                         return new ProblemBinding(
505                                                 CharOperation.subarray(compoundName, 0, currentIndex),
506                                                 NotFound);
507                                 }
508                                 if (binding instanceof ReferenceBinding) {
509                                         if (!binding.isValidBinding())
510                                                 return new ProblemReferenceBinding(
511                                                         CharOperation.subarray(compoundName, 0, currentIndex),
512                                                         binding.problemId());
513                                         if (!((ReferenceBinding) binding).canBeSeenBy(this))
514                                                 return new ProblemReferenceBinding(
515                                                         CharOperation.subarray(compoundName, 0, currentIndex),
516                                                         (ReferenceBinding) binding, 
517                                                         NotVisible);
518                                         break foundType;
519                                 }
520                         }
521                         return binding;
522                 }
523
524                 foundField : if (binding instanceof ReferenceBinding) {
525                         while (currentIndex < length) {
526                                 ReferenceBinding typeBinding = (ReferenceBinding) binding;
527                                 char[] nextName = compoundName[currentIndex++];
528                                 if ((binding = findField(typeBinding, nextName, invocationSite, true /*resolve*/)) != null) {
529                                         if (!binding.isValidBinding())
530                                                 return new ProblemFieldBinding(
531                                                         ((FieldBinding) binding).declaringClass,
532                                                         CharOperation.subarray(compoundName, 0, currentIndex),
533                                                         binding.problemId());
534                                         if (!((FieldBinding) binding).isStatic())
535                                                 return new ProblemFieldBinding(
536                                                         ((FieldBinding) binding).declaringClass,
537                                                         CharOperation.subarray(compoundName, 0, currentIndex),
538                                                         NonStaticReferenceInStaticContext);
539                                         break foundField; // binding is now a field
540                                 }
541                                 if ((binding = findMemberType(nextName, typeBinding)) == null)
542                                         return new ProblemBinding(
543                                                 CharOperation.subarray(compoundName, 0, currentIndex),
544                                                 typeBinding,
545                                                 NotFound);
546                                 if (!binding.isValidBinding())
547                                         return new ProblemReferenceBinding(
548                                                 CharOperation.subarray(compoundName, 0, currentIndex),
549                                                 binding.problemId());
550                         }
551                         return binding;
552                 }
553
554                 VariableBinding variableBinding = (VariableBinding) binding;
555                 while (currentIndex < length) {
556                         TypeBinding typeBinding = variableBinding.type;
557                         if (typeBinding == null)
558                                 return new ProblemFieldBinding(
559                                         null,
560                                         CharOperation.subarray(compoundName, 0, currentIndex + 1),
561                                         NotFound);
562                         variableBinding =
563                                 findField(typeBinding, compoundName[currentIndex++], invocationSite, true /*resolve*/);
564                         if (variableBinding == null)
565                                 return new ProblemFieldBinding(
566                                         null,
567                                         CharOperation.subarray(compoundName, 0, currentIndex),
568                                         NotFound);
569                         if (!variableBinding.isValidBinding())
570                                 return variableBinding;
571                 }
572                 return variableBinding;
573         }
574
575         /*
576          * This retrieves the argument that maps to an enclosing instance of the suitable type,
577          *      if not found then answers nil -- do not create one
578          *      
579          *              #implicitThis                                           : the implicit this will be ok
580          *              #((arg) this$n)                                         : available as a constructor arg
581          *              #((arg) this$n ... this$p)                      : available as as a constructor arg + a sequence of fields
582          *              #((fieldDescr) this$n ... this$p)       : available as a sequence of fields
583          *              nil                                                                                                     : not found
584          *
585          *      Note that this algorithm should answer the shortest possible sequence when
586          *              shortcuts are available:
587          *                              this$0 . this$0 . this$0
588          *              instead of
589          *                              this$2 . this$1 . this$0 . this$1 . this$0
590          *              thus the code generation will be more compact and runtime faster
591          */
592         public VariableBinding[] getEmulationPath(LocalVariableBinding outerLocalVariable) {
593
594                 MethodScope currentMethodScope = this.methodScope();
595                 SourceTypeBinding sourceType = currentMethodScope.enclosingSourceType();
596
597                 // identity check
598                 if (currentMethodScope == outerLocalVariable.declaringScope.methodScope()) {
599                         return new VariableBinding[] { outerLocalVariable };
600                         // implicit this is good enough
601                 }
602                 // use synthetic constructor arguments if possible
603                 if (currentMethodScope.isInsideInitializerOrConstructor()
604                         && (sourceType.isNestedType())) {
605                         SyntheticArgumentBinding syntheticArg;
606                         if ((syntheticArg = ((NestedTypeBinding) sourceType).getSyntheticArgument(outerLocalVariable)) != null) {
607                                 return new VariableBinding[] { syntheticArg };
608                         }
609                 }
610                 // use a synthetic field then
611                 if (!currentMethodScope.isStatic) {
612                         FieldBinding syntheticField;
613                         if ((syntheticField = sourceType.getSyntheticField(outerLocalVariable)) != null) {
614                                 return new VariableBinding[] { syntheticField };
615                         }
616                 }
617                 return null;
618         }
619
620         /*
621          * This retrieves the argument that maps to an enclosing instance of the suitable type,
622          *      if not found then answers nil -- do not create one
623          *
624          *              #implicitThis                                                                                                           :  the implicit this will be ok
625          *              #((arg) this$n)                                                                                                 : available as a constructor arg
626          *      #((arg) this$n access$m... access$p)            : available as as a constructor arg + a sequence of synthetic accessors to synthetic fields
627          *      #((fieldDescr) this$n access#m... access$p)     : available as a first synthetic field + a sequence of synthetic accessors to synthetic fields
628          *      null                                                                                                                                    : not found
629          *      jls 15.9.2 + http://www.ergnosis.com/java-spec-report/java-language/jls-8.8.5.1-d.html
630          */
631         public Object[] getEmulationPath(
632                         ReferenceBinding targetEnclosingType, 
633                         boolean onlyExactMatch,
634                         boolean ignoreEnclosingArgInConstructorCall) {
635                                 
636                 MethodScope currentMethodScope = this.methodScope();
637                 SourceTypeBinding sourceType = currentMethodScope.enclosingSourceType();
638
639                 // use 'this' if possible
640                 if (!currentMethodScope.isConstructorCall && !currentMethodScope.isStatic) {
641                         if (sourceType == targetEnclosingType || (!onlyExactMatch && targetEnclosingType.isSuperclassOf(sourceType))) {
642                                 return EmulationPathToImplicitThis; // implicit this is good enough
643                         }
644                 }
645                 if (!sourceType.isNestedType() || sourceType.isStatic()) { // no emulation from within non-inner types
646                         if (currentMethodScope.isConstructorCall) {
647                                 return NoEnclosingInstanceInConstructorCall;
648                         } else if (currentMethodScope.isStatic){
649                                 return NoEnclosingInstanceInStaticContext;
650                         }
651                         return null;
652                 }
653                 boolean insideConstructor = currentMethodScope.isInsideInitializerOrConstructor();
654                 // use synthetic constructor arguments if possible
655                 if (insideConstructor) {
656                         SyntheticArgumentBinding syntheticArg;
657                         if ((syntheticArg = ((NestedTypeBinding) sourceType).getSyntheticArgument(targetEnclosingType, onlyExactMatch)) != null) {
658                                 // reject allocation and super constructor call
659                                 if (ignoreEnclosingArgInConstructorCall 
660                                                 && currentMethodScope.isConstructorCall 
661                                                 && (sourceType == targetEnclosingType || (!onlyExactMatch && targetEnclosingType.isSuperclassOf(sourceType)))) {
662                                         return NoEnclosingInstanceInConstructorCall;
663                                 }
664                                 return new Object[] { syntheticArg };
665                         }
666                 }
667
668                 // use a direct synthetic field then
669                 if (currentMethodScope.isStatic) {
670                         return NoEnclosingInstanceInStaticContext;
671                 }
672                 FieldBinding syntheticField = sourceType.getSyntheticField(targetEnclosingType, onlyExactMatch);
673                 if (syntheticField != null) {
674                         if (currentMethodScope.isConstructorCall){
675                                 return NoEnclosingInstanceInConstructorCall;
676                         }
677                         return new Object[] { syntheticField };
678                 }
679                 // could be reached through a sequence of enclosing instance link (nested members)
680                 Object[] path = new Object[2]; // probably at least 2 of them
681                 ReferenceBinding currentType = sourceType.enclosingType();
682                 if (insideConstructor) {
683                         path[0] = ((NestedTypeBinding) sourceType).getSyntheticArgument(currentType, onlyExactMatch);
684                 } else {
685                         if (currentMethodScope.isConstructorCall){
686                                 return NoEnclosingInstanceInConstructorCall;
687                         }
688                         path[0] = sourceType.getSyntheticField(currentType, onlyExactMatch);
689                 }
690                 if (path[0] != null) { // keep accumulating
691                         
692                         int count = 1;
693                         ReferenceBinding currentEnclosingType;
694                         while ((currentEnclosingType = currentType.enclosingType()) != null) {
695
696                                 //done?
697                                 if (currentType == targetEnclosingType
698                                         || (!onlyExactMatch && targetEnclosingType.isSuperclassOf(currentType)))        break;
699
700                                 if (currentMethodScope != null) {
701                                         currentMethodScope = currentMethodScope.enclosingMethodScope();
702                                         if (currentMethodScope != null && currentMethodScope.isConstructorCall){
703                                                 return NoEnclosingInstanceInConstructorCall;
704                                         }
705                                         if (currentMethodScope != null && currentMethodScope.isStatic){
706                                                 return NoEnclosingInstanceInStaticContext;
707                                         }
708                                 }
709                                 
710                                 syntheticField = ((NestedTypeBinding) currentType).getSyntheticField(currentEnclosingType, onlyExactMatch);
711                                 if (syntheticField == null) break;
712
713                                 // append inside the path
714                                 if (count == path.length) {
715                                         System.arraycopy(path, 0, (path = new Object[count + 1]), 0, count);
716                                 }
717                                 // private access emulation is necessary since synthetic field is private
718                                 path[count++] = ((SourceTypeBinding) syntheticField.declaringClass).addSyntheticMethod(syntheticField, true);
719                                 currentType = currentEnclosingType;
720                         }
721                         if (currentType == targetEnclosingType
722                                 || (!onlyExactMatch && targetEnclosingType.isSuperclassOf(currentType))) {
723                                 return path;
724                         }
725                 }
726                 return null;
727         }
728
729         /* Answer true if the variable name already exists within the receiver's scope.
730          */
731         public final boolean isDuplicateLocalVariable(char[] name) {
732                 BlockScope current = this;
733                 while (true) {
734                         for (int i = 0; i < localIndex; i++) {
735                                 if (CharOperation.equals(name, current.locals[i].name))
736                                         return true;
737                         }
738                         if (current.kind != BLOCK_SCOPE) return false;
739                         current = (BlockScope)current.parent;
740                 }
741         }
742
743         public int maxShiftedOffset() {
744                 int max = -1;
745                 if (this.shiftScopes != null){
746                         for (int i = 0, length = this.shiftScopes.length; i < length; i++){
747                                 int subMaxOffset = this.shiftScopes[i].maxOffset;
748                                 if (subMaxOffset > max) max = subMaxOffset;
749                         }
750                 }
751                 return max;
752         }
753         
754         /* Answer the problem reporter to use for raising new problems.
755          *
756          * Note that as a side-effect, this updates the current reference context
757          * (unit, type or method) in case the problem handler decides it is necessary
758          * to abort.
759          */
760         public ProblemReporter problemReporter() {
761
762                 return outerMostMethodScope().problemReporter();
763         }
764
765         /*
766          * Code responsible to request some more emulation work inside the invocation type, so as to supply
767          * correct synthetic arguments to any allocation of the target type.
768          */
769         public void propagateInnerEmulation(ReferenceBinding targetType, boolean isEnclosingInstanceSupplied) {
770
771                 // no need to propagate enclosing instances, they got eagerly allocated already.
772                 
773                 SyntheticArgumentBinding[] syntheticArguments;
774                 if ((syntheticArguments = targetType.syntheticOuterLocalVariables()) != null) {
775                         for (int i = 0, max = syntheticArguments.length; i < max; i++) {
776                                 SyntheticArgumentBinding syntheticArg = syntheticArguments[i];
777                                 // need to filter out the one that could match a supplied enclosing instance
778                                 if (!(isEnclosingInstanceSupplied
779                                         && (syntheticArg.type == targetType.enclosingType()))) {
780                                         this.emulateOuterAccess(syntheticArg.actualOuterLocalVariable);
781                                 }
782                         }
783                 }
784         }
785
786         /* Answer the reference type of this scope.
787          *
788          * It is the nearest enclosing type of this scope.
789          */
790         public TypeDeclaration referenceType() {
791
792                 return methodScope().referenceType();
793         }
794
795         /*
796          * Answer the index of this scope relatively to its parent.
797          * For method scope, answers -1 (not a classScope relative position)
798          */
799         public int scopeIndex() {
800                 if (this instanceof MethodScope) return -1;
801                 BlockScope parentScope = (BlockScope)parent;
802                 Scope[] parentSubscopes = parentScope.subscopes;
803                 for (int i = 0, max = parentScope.subscopeCount; i < max; i++) {
804                         if (parentSubscopes[i] == this) return i;
805                 }
806                 return -1;
807         }
808         
809         // start position in this scope - for ordering scopes vs. variables
810         int startIndex() {
811                 return startIndex;
812         }
813
814         public String toString() {
815                 return toString(0);
816         }
817
818         public String toString(int tab) {
819
820                 String s = basicToString(tab);
821                 for (int i = 0; i < subscopeCount; i++)
822                         if (subscopes[i] instanceof BlockScope)
823                                 s += ((BlockScope) subscopes[i]).toString(tab + 1) + "\n"; //$NON-NLS-1$
824                 return s;
825         }
826 }