added -J option to preserve unmodified files in preexisting jarfile
[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.problem.ProblemReporter;
18
19 public class BlockScope extends Scope {
20
21         // Local variable management
22         public LocalVariableBinding[] locals;
23         public int localIndex; // position for next variable
24         public int startIndex;  // start position in this scope - for ordering scopes vs. variables
25         public int offset; // for variable allocation throughout scopes
26         public int maxOffset; // for variable allocation throughout scopes
27
28         // finally scopes must be shifted behind respective try&catch scope(s) so as to avoid
29         // collisions of secret variables (return address, save value).
30         public BlockScope[] shiftScopes; 
31
32         public final static VariableBinding[] EmulationPathToImplicitThis = {};
33         public final static VariableBinding[] NoEnclosingInstanceInConstructorCall = {};
34         public final static VariableBinding[] NoEnclosingInstanceInStaticContext = {};
35
36         public Scope[] subscopes = new Scope[1]; // need access from code assist
37         public int subscopeCount = 0; // need access from code assist
38
39         // record the current case statement being processed (for entire switch case block).
40         public CaseStatement switchCase; // from 1.4 on, local types should not be accessed across switch case blocks (52221)
41
42         protected BlockScope(int kind, Scope parent) {
43
44                 super(kind, parent);
45         }
46
47         public BlockScope(BlockScope parent) {
48
49                 this(parent, true);
50         }
51
52         public BlockScope(BlockScope parent, boolean addToParentScope) {
53
54                 this(BLOCK_SCOPE, parent);
55                 locals = new LocalVariableBinding[5];
56                 if (addToParentScope) parent.addSubscope(this);
57                 this.startIndex = parent.localIndex;
58         }
59
60         public BlockScope(BlockScope parent, int variableCount) {
61
62                 this(BLOCK_SCOPE, parent);
63                 locals = new LocalVariableBinding[variableCount];
64                 parent.addSubscope(this);
65                 this.startIndex = parent.localIndex;
66         }
67
68         /* Create the class scope & binding for the anonymous type.
69          */
70         public final void addAnonymousType(
71                 TypeDeclaration anonymousType,
72                 ReferenceBinding superBinding) {
73
74                 ClassScope anonymousClassScope = new ClassScope(this, anonymousType);
75                 anonymousClassScope.buildAnonymousTypeBinding(
76                         enclosingSourceType(),
77                         superBinding);
78         }
79
80         /* Create the class scope & binding for the local type.
81          */
82         public final void addLocalType(TypeDeclaration localType) {
83
84                 // check that the localType does not conflict with an enclosing type
85                 ReferenceBinding type = enclosingSourceType();
86                 do {
87                         if (CharOperation.equals(type.sourceName, localType.name)) {
88                                 problemReporter().hidingEnclosingType(localType);
89                                 return;
90                         }
91                         type = type.enclosingType();
92                 } while (type != null);
93
94                 // check that the localType does not conflict with another sibling local type
95                 Scope scope = this;
96                 do {
97                         if (((BlockScope) scope).findLocalType(localType.name) != null) {
98                                 problemReporter().duplicateNestedType(localType);
99                                 return;
100                         }
101                 } while ((scope = scope.parent) instanceof BlockScope);
102
103                 ClassScope localTypeScope = new ClassScope(this, localType);
104                 addSubscope(localTypeScope);
105                 localTypeScope.buildLocalTypeBinding(enclosingSourceType());
106         }
107
108         /* Insert a local variable into a given scope, updating its position
109          * and checking there are not too many locals or arguments allocated.
110          */
111         public final void addLocalVariable(LocalVariableBinding binding) {
112
113                 checkAndSetModifiersForVariable(binding);
114
115                 // insert local in scope
116                 if (localIndex == locals.length)
117                         System.arraycopy(
118                                 locals,
119                                 0,
120                                 (locals = new LocalVariableBinding[localIndex * 2]),
121                                 0,
122                                 localIndex);
123                 locals[localIndex++] = binding;
124
125                 // update local variable binding 
126                 binding.declaringScope = this;
127                 binding.id = this.outerMostMethodScope().analysisIndex++;
128                 // share the outermost method scope analysisIndex
129         }
130
131         public void addSubscope(Scope childScope) {
132                 if (subscopeCount == subscopes.length)
133                         System.arraycopy(
134                                 subscopes,
135                                 0,
136                                 (subscopes = new Scope[subscopeCount * 2]),
137                                 0,
138                                 subscopeCount);
139                 subscopes[subscopeCount++] = childScope;
140         }
141
142         /* Answer true if the receiver is suitable for assigning final blank fields.
143          *
144          * in other words, it is inside an initializer, a constructor or a clinit 
145          */
146         public final boolean allowBlankFinalFieldAssignment(FieldBinding binding) {
147
148                 if (enclosingSourceType() != binding.declaringClass)
149                         return false;
150
151                 MethodScope methodScope = methodScope();
152                 if (methodScope.isStatic != binding.isStatic())
153                         return false;
154                 return methodScope.isInsideInitializer() // inside initializer
155                                 || ((AbstractMethodDeclaration) methodScope.referenceContext)
156                                         .isInitializationMethod(); // inside constructor or clinit
157         }
158         String basicToString(int tab) {
159                 String newLine = "\n"; //$NON-NLS-1$
160                 for (int i = tab; --i >= 0;)
161                         newLine += "\t"; //$NON-NLS-1$
162
163                 String s = newLine + "--- Block Scope ---"; //$NON-NLS-1$
164                 newLine += "\t"; //$NON-NLS-1$
165                 s += newLine + "locals:"; //$NON-NLS-1$
166                 for (int i = 0; i < localIndex; i++)
167                         s += newLine + "\t" + locals[i].toString(); //$NON-NLS-1$
168                 s += newLine + "startIndex = " + startIndex; //$NON-NLS-1$
169                 return s;
170         }
171
172         private void checkAndSetModifiersForVariable(LocalVariableBinding varBinding) {
173
174                 int modifiers = varBinding.modifiers;
175                 if ((modifiers & AccAlternateModifierProblem) != 0 && varBinding.declaration != null){
176                         problemReporter().duplicateModifierForVariable(varBinding.declaration, this instanceof MethodScope);
177                 }
178                 int realModifiers = modifiers & AccJustFlag;
179                 
180                 int unexpectedModifiers = ~AccFinal;
181                 if ((realModifiers & unexpectedModifiers) != 0 && varBinding.declaration != null){ 
182                         problemReporter().illegalModifierForVariable(varBinding.declaration, this instanceof MethodScope);
183                 }
184                 varBinding.modifiers = modifiers;
185         }
186
187         /* Compute variable positions in scopes given an initial position offset
188          * ignoring unused local variables.
189          * 
190          * No argument is expected here (ilocal is the first non-argument local of the outermost scope)
191          * Arguments are managed by the MethodScope method
192          */
193         void computeLocalVariablePositions(int ilocal, int initOffset, CodeStream codeStream) {
194
195                 this.offset = initOffset;
196                 this.maxOffset = initOffset;
197
198                 // local variable init
199                 int maxLocals = this.localIndex;
200                 boolean hasMoreVariables = ilocal < maxLocals;
201
202                 // scope init
203                 int iscope = 0, maxScopes = this.subscopeCount;
204                 boolean hasMoreScopes = maxScopes > 0;
205
206                 // iterate scopes and variables in parallel
207                 while (hasMoreVariables || hasMoreScopes) {
208                         if (hasMoreScopes
209                                 && (!hasMoreVariables || (subscopes[iscope].startIndex() <= ilocal))) {
210                                 // consider subscope first
211                                 if (subscopes[iscope] instanceof BlockScope) {
212                                         BlockScope subscope = (BlockScope) subscopes[iscope];
213                                         int subOffset = subscope.shiftScopes == null ? this.offset : subscope.maxShiftedOffset();
214                                         subscope.computeLocalVariablePositions(0, subOffset, codeStream);
215                                         if (subscope.maxOffset > this.maxOffset)
216                                                 this.maxOffset = subscope.maxOffset;
217                                 }
218                                 hasMoreScopes = ++iscope < maxScopes;
219                         } else {
220                                 
221                                 // consider variable first
222                                 LocalVariableBinding local = locals[ilocal]; // if no local at all, will be locals[ilocal]==null
223                                 
224                                 // check if variable is actually used, and may force it to be preserved
225                                 boolean generateCurrentLocalVar = (local.useFlag == LocalVariableBinding.USED && !local.isConstantValue());
226                                         
227                                 // do not report fake used variable
228                                 if (local.useFlag == LocalVariableBinding.UNUSED
229                                         && (local.declaration != null) // unused (and non secret) local
230                                         && ((local.declaration.bits & ASTNode.IsLocalDeclarationReachableMASK) != 0)) { // declaration is reachable
231                                                 
232                                         if (!(local.declaration instanceof Argument))  // do not report unused catch arguments
233                                                 this.problemReporter().unusedLocalVariable(local.declaration);
234                                 }
235                                 
236                                 // could be optimized out, but does need to preserve unread variables ?
237                                 if (!generateCurrentLocalVar) {
238                                         if (local.declaration != null && environment().options.preserveAllLocalVariables) {
239                                                 generateCurrentLocalVar = true; // force it to be preserved in the generated code
240                                                 local.useFlag = LocalVariableBinding.USED;
241                                         }
242                                 }
243                                 
244                                 // allocate variable
245                                 if (generateCurrentLocalVar) {
246
247                                         if (local.declaration != null) {
248                                                 codeStream.record(local); // record user-defined local variables for attribute generation
249                                         }
250                                         // assign variable position
251                                         local.resolvedPosition = this.offset;
252
253                                         if ((local.type == LongBinding) || (local.type == DoubleBinding)) {
254                                                 this.offset += 2;
255                                         } else {
256                                                 this.offset++;
257                                         }
258                                         if (this.offset > 0xFFFF) { // no more than 65535 words of locals
259                                                 this.problemReporter().noMoreAvailableSpaceForLocal(
260                                                         local, 
261                                                         local.declaration == null ? (ASTNode)this.methodScope().referenceContext : local.declaration);
262                                         }
263                                 } else {
264                                         local.resolvedPosition = -1; // not generated
265                                 }
266                                 hasMoreVariables = ++ilocal < maxLocals;
267                         }
268                 }
269                 if (this.offset > this.maxOffset)
270                         this.maxOffset = this.offset;
271         }
272
273         /*
274          *      Record the suitable binding denoting a synthetic field or constructor argument,
275          * mapping to the actual outer local variable in the scope context.
276          * Note that this may not need any effect, in case the outer local variable does not
277          * need to be emulated and can directly be used as is (using its back pointer to its
278          * declaring scope).
279          */
280         public void emulateOuterAccess(LocalVariableBinding outerLocalVariable) {
281
282                 MethodScope currentMethodScope;
283                 if ((currentMethodScope = this.methodScope())
284                         != outerLocalVariable.declaringScope.methodScope()) {
285                         NestedTypeBinding currentType = (NestedTypeBinding) this.enclosingSourceType();
286
287                         //do nothing for member types, pre emulation was performed already
288                         if (!currentType.isLocalType()) {
289                                 return;
290                         }
291                         // must also add a synthetic field if we're not inside a constructor
292                         if (!currentMethodScope.isInsideInitializerOrConstructor()) {
293                                 currentType.addSyntheticArgumentAndField(outerLocalVariable);
294                         } else {
295                                 currentType.addSyntheticArgument(outerLocalVariable);
296                         }
297                 }
298         }
299
300         /* Note that it must never produce a direct access to the targetEnclosingType,
301          * but instead a field sequence (this$2.this$1.this$0) so as to handle such a test case:
302          *
303          * class XX {
304          *      void foo() {
305          *              class A {
306          *                      class B {
307          *                              class C {
308          *                                      boolean foo() {
309          *                                              return (Object) A.this == (Object) B.this;
310          *                                      }
311          *                              }
312          *                      }
313          *              }
314          *              new A().new B().new C();
315          *      }
316          * }
317          * where we only want to deal with ONE enclosing instance for C (could not figure out an A for C)
318          */
319         public final ReferenceBinding findLocalType(char[] name) {
320
321                 long compliance = environment().options.complianceLevel;
322                 for (int i = 0, length = subscopeCount; i < length; i++) {
323                         if (subscopes[i] instanceof ClassScope) {
324                                 LocalTypeBinding sourceType = (LocalTypeBinding)((ClassScope) subscopes[i]).referenceContext.binding;
325                                 // from 1.4 on, local types should not be accessed across switch case blocks (52221)                            
326                                 if (compliance >= ClassFileConstants.JDK1_4 && sourceType.switchCase != this.switchCase) continue;
327                                 if (CharOperation.equals(sourceType.sourceName(), name))
328                                         return sourceType;
329                         }
330                 }
331                 return null;
332         }
333
334         public LocalVariableBinding findVariable(char[] variable) {
335
336                 int varLength = variable.length;
337                 for (int i = 0, length = locals.length; i < length; i++) {
338                         LocalVariableBinding local = locals[i];
339                         if (local == null)
340                                 return null;
341                         if (local.name.length == varLength && CharOperation.equals(local.name, variable))
342                                 return local;
343                 }
344                 return null;
345         }
346         /* API
347          * flag is a mask of the following values VARIABLE (= FIELD or LOCAL), TYPE.
348          * Only bindings corresponding to the mask will be answered.
349          *
350          *      if the VARIABLE mask is set then
351          *              If the first name provided is a field (or local) then the field (or local) is answered
352          *              Otherwise, package names and type names are consumed until a field is found.
353          *              In this case, the field is answered.
354          *
355          *      if the TYPE mask is set,
356          *              package names and type names are consumed until the end of the input.
357          *              Only if all of the input is consumed is the type answered
358          *
359          *      All other conditions are errors, and a problem binding is returned.
360          *      
361          *      NOTE: If a problem binding is returned, senders should extract the compound name
362          *      from the binding & not assume the problem applies to the entire compoundName.
363          *
364          *      The VARIABLE mask has precedence over the TYPE mask.
365          *
366          *      InvocationSite implements
367          *              isSuperAccess(); this is used to determine if the discovered field is visible.
368          *              setFieldIndex(int); this is used to record the number of names that were consumed.
369          *
370          *      For example, getBinding({"foo","y","q", VARIABLE, site) will answer
371          *      the binding for the field or local named "foo" (or an error binding if none exists).
372          *      In addition, setFieldIndex(1) will be sent to the invocation site.
373          *      If a type named "foo" exists, it will not be detected (and an error binding will be answered)
374          *
375          *      IMPORTANT NOTE: This method is written under the assumption that compoundName is longer than length 1.
376          */
377         public Binding getBinding(char[][] compoundName, int mask, InvocationSite invocationSite, boolean needResolve) {
378
379                 Binding binding = getBinding(compoundName[0], mask | Binding.TYPE | Binding.PACKAGE, invocationSite, needResolve);
380                 invocationSite.setFieldIndex(1);
381                 if (binding instanceof VariableBinding) return binding;
382                 CompilationUnitScope unitScope = compilationUnitScope();
383                 unitScope.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                                 unitScope.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 & Binding.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 & Binding.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 & Binding.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 & Binding.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                                 Binding.VARIABLE | Binding.TYPE | Binding.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 && sourceType.findSuperTypeErasingTo(targetEnclosingType) != null)) {
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 && sourceType.findSuperTypeErasingTo(targetEnclosingType) != null))) {
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 && currentType.findSuperTypeErasingTo(targetEnclosingType) != null))        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 && currentType.findSuperTypeErasingTo(targetEnclosingType) != null)) {
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 }