View Javadoc
1   /*
2    * Copyright (c) 1994, 2003, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.  Oracle designates this
8    * particular file as subject to the "Classpath" exception as provided
9    * by Oracle in the LICENSE file that accompanied this code.
10   *
11   * This code is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14   * version 2 for more details (a copy is included in the LICENSE file that
15   * accompanied this code).
16   *
17   * You should have received a copy of the GNU General Public License version
18   * 2 along with this work; if not, write to the Free Software Foundation,
19   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20   *
21   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22   * or visit www.oracle.com if you need additional information or have any
23   * questions.
24   */
25  
26  package sun.tools.tree;
27  
28  import sun.tools.java.*;
29  import sun.tools.asm.Assembler;
30  
31  /**
32   * WARNING: The contents of this source file are not part of any
33   * supported API.  Code that depends on them does so at its own risk:
34   * they are subject to change or removal without notice.
35   */
36  public
37  class Context implements Constants {
38      Context prev;
39      Node node;
40      int varNumber;
41      LocalMember locals;
42      LocalMember classes;
43      MemberDefinition field;
44      int scopeNumber;
45      int frameNumber;
46  
47      /**
48       * Create the initial context for a method
49       * The incoming context is inherited from
50       */
51      public Context(Context ctx, MemberDefinition field) {
52          this.field = field;
53          if (ctx == null) {
54              this.frameNumber = 1;
55              this.scopeNumber = 2;
56              this.varNumber = 0;
57          } else {
58              this.prev = ctx;
59              this.locals = ctx.locals;
60              this.classes = ctx.classes;
61              if (field != null &&
62                    (field.isVariable() || field.isInitializer())) {
63                  // Variables and initializers are inlined into a constructor.
64                  // Model this by inheriting the frame number of the parent,
65                  // which will contain a "this" parameter.
66                  this.frameNumber = ctx.frameNumber;
67                  this.scopeNumber = ctx.scopeNumber + 1;
68              } else {
69                  this.frameNumber = ctx.scopeNumber + 1;
70                  this.scopeNumber = this.frameNumber + 1;
71              }
72              this.varNumber = ctx.varNumber;
73          }
74      }
75  
76      /**
77       * Create a new context, for initializing a class.
78       */
79      public Context(Context ctx, ClassDefinition c) {
80          this(ctx, (MemberDefinition)null);
81      }
82  
83      /**
84       * Create a new nested context, for a block statement
85       */
86      Context(Context ctx, Node node) {
87          if (ctx == null) {
88              this.frameNumber = 1;
89              this.scopeNumber = 2;
90              this.varNumber = 0;
91          } else {
92              this.prev = ctx;
93              this.locals = ctx.locals;
94              // Inherit local classes from surrounding block,
95              // just as for local variables.  Fixes 4074421.
96              this.classes = ctx.classes;
97              this.varNumber = ctx.varNumber;
98              this.field = ctx.field;
99              this.frameNumber = ctx.frameNumber;
100             this.scopeNumber = ctx.scopeNumber + 1;
101             this.node = node;
102         }
103     }
104 
105     public Context(Context ctx) {
106         this(ctx, (Node)null);
107     }
108 
109     /**
110      * Declare local
111      */
112     public int declare(Environment env, LocalMember local) {
113         //System.out.println(   "DECLARE= " + local.getName() + "=" + varNumber + ", read=" + local.readcount + ", write=" + local.writecount + ", hash=" + local.hashCode());
114         local.scopeNumber = scopeNumber;
115         if (this.field == null && idThis.equals(local.getName())) {
116             local.scopeNumber += 1; // Anticipate variable or initializer.
117         }
118         if (local.isInnerClass()) {
119             local.prev = classes;
120             classes = local;
121             return 0;
122         }
123 
124         // Originally the statement:
125         //
126         //     local.subModifiers(M_INLINEABLE);
127         //
128         // was here with the comment:
129         //
130         //     // prevent inlining across call sites
131         //
132         // This statement prevented constant local variables from
133         // inlining. It didn't seem to do anything useful.
134         //
135         // The statement has been removed and an assertion has been
136         // added which mandates that the only members which are marked
137         // with M_INLINEABLE are the ones for which isConstant() is true.
138         // (Fix for 4106244.)
139         //
140         // Addition to the above comment: they might also be
141         // final variables initialized with 'this', 'super', or other
142         // final identifiers.  See VarDeclarationStatement.inline().
143         // So I've removed the assertion.  The original subModifiers
144         // call appears to have been there to fix nested class translation
145         // breakage, which has been fixed in VarDeclarationStatement
146         // now instead.  (Fix for 4073244.)
147 
148         local.prev = locals;
149         locals = local;
150         local.number = varNumber;
151         varNumber += local.getType().stackSize();
152         return local.number;
153     }
154 
155     /**
156      * Get a local variable by name
157      */
158     public
159     LocalMember getLocalField(Identifier name) {
160         for (LocalMember f = locals ; f != null ; f = f.prev) {
161             if (name.equals(f.getName())) {
162                 return f;
163             }
164         }
165         return null;
166     }
167 
168     /**
169      * Get the scope number for a reference to a member of this class
170      * (Larger scope numbers are more deeply nested.)
171      * @see LocalMember#scopeNumber
172      */
173     public
174     int getScopeNumber(ClassDefinition c) {
175         for (Context ctx = this; ctx != null; ctx = ctx.prev) {
176             if (ctx.field == null)  continue;
177             if (ctx.field.getClassDefinition() == c) {
178                 return ctx.frameNumber;
179             }
180         }
181         return -1;
182     }
183 
184     private
185     MemberDefinition getFieldCommon(Environment env, Identifier name,
186                                    boolean apparentOnly) throws AmbiguousMember, ClassNotFound {
187         // Note:  This is structured as a pair of parallel lookups.
188         // If we were to redesign Context, we might prefer to walk
189         // along a single chain of scopes.
190 
191         LocalMember lf = getLocalField(name);
192         int ls = (lf == null) ? -2 : lf.scopeNumber;
193 
194         ClassDefinition thisClass = field.getClassDefinition();
195 
196         // Also look for a class member in a shallower scope.
197         for (ClassDefinition c = thisClass;
198              c != null;
199              c = c.getOuterClass()) {
200             MemberDefinition f = c.getVariable(env, name, thisClass);
201             if (f != null && getScopeNumber(c) > ls) {
202                 if (apparentOnly && f.getClassDefinition() != c) {
203                     continue;
204                 }
205                 return f;
206             }
207         }
208 
209         return lf;
210     }
211 
212     /**
213      * Assign a number to a class field.
214      * (This is used to track definite assignment of some blank finals.)
215      */
216     public int declareFieldNumber(MemberDefinition field) {
217         return declare(null, new LocalMember(field));
218     }
219 
220     /**
221      * Retrieve a number previously assigned by declareMember().
222      * Return -1 if there was no such assignment in this context.
223      */
224     public int getFieldNumber(MemberDefinition field) {
225         for (LocalMember f = locals ; f != null ; f = f.prev) {
226             if (f.getMember() == field) {
227                 return f.number;
228             }
229         }
230         return -1;
231     }
232 
233     /**
234      * Return the local field or member field corresponding to a number.
235      * Return null if there is no such field.
236      */
237     public MemberDefinition getElement(int number) {
238         for (LocalMember f = locals ; f != null ; f = f.prev) {
239             if (f.number == number) {
240                 MemberDefinition field = f.getMember();
241                 return (field != null) ? field : f;
242             }
243         }
244         return null;
245     }
246 
247     /**
248      * Get a local class by name
249      */
250     public
251     LocalMember getLocalClass(Identifier name) {
252         for (LocalMember f = classes ; f != null ; f = f.prev) {
253             if (name.equals(f.getName())) {
254                 return f;
255             }
256         }
257         return null;
258     }
259 
260     private
261     MemberDefinition getClassCommon(Environment env, Identifier name,
262                                    boolean apparentOnly) throws ClassNotFound {
263         LocalMember lf = getLocalClass(name);
264         int ls = (lf == null) ? -2 : lf.scopeNumber;
265 
266         // Also look for a class member in a shallower scope.
267         for (ClassDefinition c = field.getClassDefinition();
268              c != null;
269              c = c.getOuterClass()) {
270             // QUERY: We may need to get the inner class from a
271             // superclass of 'c'.  This call is prepared to
272             // resolve the superclass if necessary.  Can we arrange
273             // to assure that it is always previously resolved?
274             // This is one of a small number of problematic calls that
275             // requires 'getSuperClass' to resolve superclasses on demand.
276             // See 'ClassDefinition.getInnerClass(env, nm)'.
277             MemberDefinition f = c.getInnerClass(env, name);
278             if (f != null && getScopeNumber(c) > ls) {
279                 if (apparentOnly && f.getClassDefinition() != c) {
280                     continue;
281                 }
282                 return f;
283             }
284         }
285 
286         return lf;
287     }
288 
289     /**
290      * Get either a local variable, or a field in a current class
291      */
292     public final
293     MemberDefinition getField(Environment env, Identifier name) throws AmbiguousMember, ClassNotFound {
294         return getFieldCommon(env, name, false);
295     }
296 
297     /**
298      * Like getField, except that it skips over inherited fields.
299      * Used for error checking.
300      */
301     public final
302     MemberDefinition getApparentField(Environment env, Identifier name) throws AmbiguousMember, ClassNotFound {
303         return getFieldCommon(env, name, true);
304     }
305 
306     /**
307      * Check if the given field is active in this context.
308      */
309     public boolean isInScope(LocalMember field) {
310         for (LocalMember f = locals ; f != null ; f = f.prev) {
311             if (field == f) {
312                 return true;
313             }
314         }
315         return false;
316     }
317 
318     /**
319      * Notice a reference (usually an uplevel one).
320      * Update the references list of every enclosing class
321      * which is enclosed by the scope of the target.
322      * Update decisions about which uplevels to make into fields.
323      * Return the uplevel reference descriptor, or null if it's local.
324      * <p>
325      * The target must be in scope in this context.
326      * So, call this method only from the check phase.
327      * (In other phases, the context may be less complete.)
328      * <p>
329      * This can and should be called both before and after classes are frozen.
330      * It should be a no-op, and will raise a compiler error if not.
331      */
332     public UplevelReference noteReference(Environment env, LocalMember target) {
333         int targetScopeNumber = !isInScope(target) ? -1 : target.scopeNumber;
334 
335         // Walk outward visiting each scope.
336         // Note each distinct frame (i.e., enclosing method).
337         // For each frame in which the variable is uplevel,
338         // record the event in the references list of the enclosing class.
339         UplevelReference res = null;
340         int currentFrameNumber = -1;
341         for (Context refctx = this; refctx != null; refctx = refctx.prev) {
342             if (currentFrameNumber == refctx.frameNumber) {
343                 continue;       // we're processing frames, not contexts
344             }
345             currentFrameNumber = refctx.frameNumber;
346             if (targetScopeNumber >= currentFrameNumber) {
347                 break;          // the target is native to this frame
348             }
349 
350             // process a frame which is using this variable as an uplevel
351             ClassDefinition refc = refctx.field.getClassDefinition();
352             UplevelReference r = refc.getReference(target);
353             r.noteReference(env, refctx);
354 
355             // remember the reference pertaining to the innermost frame
356             if (res == null) {
357                 res = r;
358             }
359         }
360         return res;
361     }
362 
363     /**
364      * Implement a reference (usually an uplevel one).
365      * Call noteReference() first, to make sure the reference
366      * lists are up to date.
367      * <p>
368      * The resulting expression tree does not need checking;
369      * it can be code-generated right away.
370      * If the reference is not uplevel, the result is an IDENT or THIS.
371      */
372     public Expression makeReference(Environment env, LocalMember target) {
373         UplevelReference r = noteReference(env, target);
374 
375         // Now create a referencing expression.
376         if (r != null) {
377             return r.makeLocalReference(env, this);
378         } else if (idThis.equals(target.getName())) {
379             return new ThisExpression(0, target);
380         } else {
381             return new IdentifierExpression(0, target);
382         }
383     }
384 
385     /**
386      * Return a local expression which can serve as the base reference
387      * for the given field.  If the field is a constructor, return an
388      * expression for the implicit enclosing instance argument.
389      * <p>
390      * Return null if there is no need for such an argument,
391      * or if there was an error.
392      */
393     public Expression findOuterLink(Environment env, long where,
394                                     MemberDefinition f) {
395         // reqc is the base pointer type required to use f
396         ClassDefinition fc = f.getClassDefinition();
397         ClassDefinition reqc = f.isStatic() ? null
398                              : !f.isConstructor() ? fc
399                              : fc.isTopLevel() ? null
400                              : fc.getOuterClass();
401         if (reqc == null) {
402             return null;
403         }
404         return findOuterLink(env, where, reqc, f, false);
405     }
406 
407     private static boolean match(Environment env,
408                                  ClassDefinition thisc, ClassDefinition reqc) {
409         try {
410             return thisc == reqc
411                 || reqc.implementedBy(env, thisc.getClassDeclaration());
412         } catch (ClassNotFound ee) {
413             return false;
414         }
415     }
416 
417     public Expression findOuterLink(Environment env, long where,
418                                     ClassDefinition reqc,
419                                     MemberDefinition f,
420                                     boolean needExactMatch) {
421         if (field.isStatic()) {
422             if (f == null) {
423                 // say something like: undefined variable A.this
424                 Identifier nm = reqc.getName().getFlatName().getName();
425                 env.error(where, "undef.var", Identifier.lookup(nm,idThis));
426             } else if (f.isConstructor()) {
427                 env.error(where, "no.outer.arg", reqc, f.getClassDeclaration());
428             } else if (f.isMethod()) {
429                 env.error(where, "no.static.meth.access",
430                           f, f.getClassDeclaration());
431             } else {
432                 env.error(where, "no.static.field.access", f.getName(),
433                           f.getClassDeclaration());
434             }
435             // This is an attempt at error recovery.
436             // Unfortunately, the constructor may throw
437             // a null pointer exception after failing to resolve
438             // 'idThis'.  Since an error message has already been
439             // issued previously, this exception is caught and
440             // silently ignored.  Ideally, we should avoid throwing
441             // the exception.
442             Expression e = new ThisExpression(where, this);
443             e.type = reqc.getType();
444             return e;
445         }
446 
447         // use lp to scan for current instances (locals named "this")
448         LocalMember lp = locals;
449 
450         // thise is a link expression being built up
451         Expression thise = null;
452 
453         // root is the local variable (idThis) at the far left of thise
454         LocalMember root = null;
455 
456         // thisc is the class of the link expression thise
457         ClassDefinition thisc = null;
458 
459         // conCls is the class of the "this", in a constructor
460         ClassDefinition conCls = null;
461         if (field.isConstructor()) {
462             conCls = field.getClassDefinition();
463         }
464 
465         if (!field.isMethod()) {
466             thisc = field.getClassDefinition();
467             thise = new ThisExpression(where, this);
468         }
469 
470         while (true) {
471             if (thise == null) {
472                 // start fresh from lp
473                 while (lp != null && !idThis.equals(lp.getName())) {
474                     lp = lp.prev;
475                 }
476                 if (lp == null) {
477                     break;
478                 }
479                 thise = new ThisExpression(where, lp);
480                 thisc = lp.getClassDefinition();
481                 root = lp;
482                 lp = lp.prev;
483             }
484 
485             // Require exact class identity when called with
486             // 'needExactMatch' true.  This is done when checking
487             // the '<class>.this' syntax.  Fixes 4102393 and 4133457.
488             if (thisc == reqc ||
489                 (!needExactMatch && match(env, thisc, reqc))) {
490                 break;
491             }
492 
493             // move out one step, if the current instance has an outer link
494 
495             MemberDefinition outerMember = thisc.findOuterMember();
496             if (outerMember == null) {
497                 thise = null;
498                 continue;       // try to find more help in lp
499             }
500             ClassDefinition prevc = thisc;
501             thisc = prevc.getOuterClass();
502 
503             if (prevc == conCls) {
504                 // Must pick up "this$C" from the constructor argument,
505                 // not from "this.this$C", since the latter may not be
506                 // initialized properly.  (This way is cheaper too.)
507                 Identifier nm = outerMember.getName();
508                 IdentifierExpression arg = new IdentifierExpression(where, nm);
509                 arg.bind(env, this);
510                 thise = arg;
511             } else {
512                 thise = new FieldExpression(where, thise, outerMember);
513             }
514         }
515         if (thise != null) {
516             // mark crossed scopes
517             // ?????
518             //ensureAvailable(root);
519             return thise;
520         }
521 
522         if (f == null) {
523             // say something like: undefined variable A.this
524             Identifier nm = reqc.getName().getFlatName().getName();
525             env.error(where, "undef.var", Identifier.lookup(nm,idThis));
526         } else if (f.isConstructor()) {
527             env.error(where, "no.outer.arg", reqc, f.getClassDefinition());
528         } else {
529             env.error(where, "no.static.field.access", f, field);
530         }
531 
532         // avoid floodgating:
533         Expression e = new ThisExpression(where, this);
534         e.type = reqc.getType();
535         return e;
536     }
537 
538     /**
539      * Is there a "this" of type reqc in scope?
540      */
541     public static boolean outerLinkExists(Environment env,
542                                           ClassDefinition reqc,
543                                           ClassDefinition thisc) {
544         while (!match(env, thisc, reqc)) {
545             if (thisc.isTopLevel()) {
546                 return false;
547             }
548             thisc = thisc.getOuterClass();
549         }
550         return true;
551     }
552 
553     /**
554      * From which enclosing class do members of this type come?
555      */
556     public ClassDefinition findScope(Environment env, ClassDefinition reqc) {
557         ClassDefinition thisc = field.getClassDefinition();
558         while (thisc != null && !match(env, thisc, reqc)) {
559             thisc = thisc.getOuterClass();
560         }
561         return thisc;
562     }
563 
564     /**
565      * Resolve a type name from within a local scope.
566      * @see Environment#resolveName
567      */
568     Identifier resolveName(Environment env, Identifier name) {
569         // This logic is pretty much exactly parallel to that of
570         // Environment.resolveName().
571         if (name.isQualified()) {
572             // Try to resolve the first identifier component,
573             // because inner class names take precedence over
574             // package prefixes.  (Cf. Environment.resolveName.)
575             Identifier rhead = resolveName(env, name.getHead());
576 
577             if (rhead.hasAmbigPrefix()) {
578                 // The first identifier component refers to an
579                 // ambiguous class.  Limp on.  We throw away the
580                 // rest of the classname as it is irrelevant.
581                 // (part of solution for 4059855).
582                 return rhead;
583             }
584 
585             if (!env.classExists(rhead)) {
586                 return env.resolvePackageQualifiedName(name);
587             }
588             try {
589                 return env.getClassDefinition(rhead).
590                     resolveInnerClass(env, name.getTail());
591             } catch (ClassNotFound ee) {
592                 // return partially-resolved name someone else can fail on
593                 return Identifier.lookupInner(rhead, name.getTail());
594             }
595         }
596 
597         // Look for an unqualified name in enclosing scopes.
598         try {
599             MemberDefinition f = getClassCommon(env, name, false);
600             if (f != null) {
601                 return f.getInnerClass().getName();
602             }
603         } catch (ClassNotFound ee) {
604             // a missing superclass, or something catastrophic
605         }
606 
607         // look in imports, etc.
608         return env.resolveName(name);
609     }
610 
611     /**
612      * Return the name of a lexically apparent type,
613      * skipping inherited members, and ignoring
614      * the current pacakge and imports.
615      * This is used for error checking.
616      */
617     public
618     Identifier getApparentClassName(Environment env, Identifier name) {
619         if (name.isQualified()) {
620             // Try to resolve the first identifier component,
621             // because inner class names take precedence over
622             // package prefixes.  (Cf. Environment.resolveName.)
623             Identifier rhead = getApparentClassName(env, name.getHead());
624             return (rhead == null) ? idNull
625                 : Identifier.lookup(rhead,
626                                     name.getTail());
627         }
628 
629         // Look for an unqualified name in enclosing scopes.
630         try {
631             MemberDefinition f = getClassCommon(env, name, true);
632             if (f != null) {
633                 return f.getInnerClass().getName();
634             }
635         } catch (ClassNotFound ee) {
636             // a missing superclass, or something catastrophic
637         }
638 
639         // the enclosing class name is the only apparent package member:
640         Identifier topnm = field.getClassDefinition().getTopClass().getName();
641         if (topnm.getName().equals(name)) {
642             return topnm;
643         }
644         return idNull;
645     }
646 
647     /**
648      * Raise an error if a blank final was definitely unassigned
649      * on entry to a loop, but has possibly been assigned on the
650      * back-branch.  If this is the case, the loop may be assigning
651      * it multiple times.
652      */
653     public void checkBackBranch(Environment env, Statement loop,
654                                 Vset vsEntry, Vset vsBack) {
655         for (LocalMember f = locals ; f != null ; f = f.prev) {
656             if (f.isBlankFinal()
657                 && vsEntry.testVarUnassigned(f.number)
658                 && !vsBack.testVarUnassigned(f.number)) {
659                 env.error(loop.where, "assign.to.blank.final.in.loop",
660                           f.getName());
661             }
662         }
663     }
664 
665     /**
666      * Check if a field can reach another field (only considers
667      * forward references, not the access modifiers).
668      */
669     public boolean canReach(Environment env, MemberDefinition f) {
670         return field.canReach(env, f);
671     }
672 
673     /**
674      * Get the context that corresponds to a label, return null if
675      * not found.
676      */
677     public
678     Context getLabelContext(Identifier lbl) {
679         for (Context ctx = this ; ctx != null ; ctx = ctx.prev) {
680             if ((ctx.node != null) && (ctx.node instanceof Statement)) {
681                 if (((Statement)(ctx.node)).hasLabel(lbl))
682                     return ctx;
683             }
684         }
685         return null;
686     }
687 
688     /**
689      * Get the destination context of a break
690      */
691     public
692     Context getBreakContext(Identifier lbl) {
693         if (lbl != null) {
694             return getLabelContext(lbl);
695         }
696         for (Context ctx = this ; ctx != null ; ctx = ctx.prev) {
697             if (ctx.node != null) {
698                 switch (ctx.node.op) {
699                   case SWITCH:
700                   case FOR:
701                   case DO:
702                   case WHILE:
703                     return ctx;
704                 }
705             }
706         }
707         return null;
708     }
709 
710     /**
711      * Get the destination context of a continue
712      */
713     public
714     Context getContinueContext(Identifier lbl) {
715         if (lbl != null) {
716             return getLabelContext(lbl);
717         }
718         for (Context ctx = this ; ctx != null ; ctx = ctx.prev) {
719             if (ctx.node != null) {
720                 switch (ctx.node.op) {
721                   case FOR:
722                   case DO:
723                   case WHILE:
724                     return ctx;
725                 }
726             }
727         }
728         return null;
729     }
730 
731     /**
732      * Get the destination context of a return (the method body)
733      */
734     public
735     CheckContext getReturnContext() {
736         for (Context ctx = this ; ctx != null ; ctx = ctx.prev) {
737             // The METHOD node is set up by Statement.checkMethod().
738             if (ctx.node != null && ctx.node.op == METHOD) {
739                 return (CheckContext)ctx;
740             }
741         }
742         return null;
743     }
744 
745     /**
746      * Get the context of the innermost surrounding try-block.
747      * Consider only try-blocks contained within the same method.
748      * (There could be others when searching from within a method
749      * of a local class, but they are irrelevant to our purpose.)
750      * This is used for recording DA/DU information preceding
751      * all abnormal transfers of control: break, continue, return,
752      * and throw.
753      */
754     public
755     CheckContext getTryExitContext() {
756         for (Context ctx = this;
757              ctx != null && ctx.node != null && ctx.node.op != METHOD;
758              ctx = ctx.prev) {
759             if (ctx.node.op == TRY) {
760                 return (CheckContext)ctx;
761             }
762         }
763         return null;
764     }
765 
766     /**
767      * Get the nearest inlined context
768      */
769     Context getInlineContext() {
770         for (Context ctx = this ; ctx != null ; ctx = ctx.prev) {
771             if (ctx.node != null) {
772                 switch (ctx.node.op) {
773                   case INLINEMETHOD:
774                   case INLINENEWINSTANCE:
775                     return ctx;
776                 }
777             }
778         }
779         return null;
780     }
781 
782     /**
783      * Get the context of a field that is being inlined
784      */
785     Context getInlineMemberContext(MemberDefinition field) {
786         for (Context ctx = this ; ctx != null ; ctx = ctx.prev) {
787             if (ctx.node != null) {
788                 switch (ctx.node.op) {
789                   case INLINEMETHOD:
790                     if (((InlineMethodExpression)ctx.node).field.equals(field)) {
791                         return ctx;
792                     }
793                     break;
794                   case INLINENEWINSTANCE:
795                     if (((InlineNewInstanceExpression)ctx.node).field.equals(field)) {
796                         return ctx;
797                     }
798                 }
799             }
800         }
801         return null;
802     }
803 
804     /**
805      * Remove variables from the vset set  that are no longer part of
806      * this context.
807      */
808     public final Vset removeAdditionalVars(Vset vset) {
809         return vset.removeAdditionalVars(varNumber);
810     }
811 
812     public final int getVarNumber() {
813         return varNumber;
814     }
815 
816     /**
817      * Return the number of the innermost current instance reference.
818      */
819     public int getThisNumber() {
820         LocalMember thisf = getLocalField(idThis);
821         if (thisf != null
822             && thisf.getClassDefinition() == field.getClassDefinition()) {
823             return thisf.number;
824         }
825         // this is a variable; there is no "this" (should not happen)
826         return varNumber;
827     }
828 
829     /**
830      * Return the field containing the present context.
831      */
832     public final MemberDefinition getField() {
833         return field;
834     }
835 
836     /**
837      * Extend an environment with the given context.
838      * The resulting environment behaves the same as
839      * the given one, except that resolveName() takes
840      * into account local class names in this context.
841      */
842     public static Environment newEnvironment(Environment env, Context ctx) {
843         return new ContextEnvironment(env, ctx);
844     }
845 }
846 
847 final
848 class ContextEnvironment extends Environment {
849     Context ctx;
850     Environment innerEnv;
851 
852     ContextEnvironment(Environment env, Context ctx) {
853         super(env, env.getSource());
854         this.ctx = ctx;
855         this.innerEnv = env;
856     }
857 
858     public Identifier resolveName(Identifier name) {
859         return ctx.resolveName(innerEnv, name);
860     }
861 }