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
26package sun.tools.tree;
27
28import sun.tools.java.*;
29import 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 */
36public
37class 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
847final
848class 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}
862