861 lines · 29.2 KB · Java
RawDownload
/* * Copyright (c) 1994, 2003, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.tools.tree; import sun.tools.java.*;import sun.tools.asm.Assembler; /** * WARNING: The contents of this source file are not part of any * supported API.  Code that depends on them does so at its own risk: * they are subject to change or removal without notice. */publicclass Context implements Constants {    Context prev;    Node node;    int varNumber;    LocalMember locals;    LocalMember classes;    MemberDefinition field;    int scopeNumber;    int frameNumber;     /**     * Create the initial context for a method     * The incoming context is inherited from     */    public Context(Context ctx, MemberDefinition field) {        this.field = field;        if (ctx == null) {            this.frameNumber = 1;            this.scopeNumber = 2;            this.varNumber = 0;        } else {            this.prev = ctx;            this.locals = ctx.locals;            this.classes = ctx.classes;            if (field != null &&                  (field.isVariable() || field.isInitializer())) {                // Variables and initializers are inlined into a constructor.                // Model this by inheriting the frame number of the parent,                // which will contain a "this" parameter.                this.frameNumber = ctx.frameNumber;                this.scopeNumber = ctx.scopeNumber + 1;            } else {                this.frameNumber = ctx.scopeNumber + 1;                this.scopeNumber = this.frameNumber + 1;            }            this.varNumber = ctx.varNumber;        }    }     /**     * Create a new context, for initializing a class.     */    public Context(Context ctx, ClassDefinition c) {        this(ctx, (MemberDefinition)null);    }     /**     * Create a new nested context, for a block statement     */    Context(Context ctx, Node node) {        if (ctx == null) {            this.frameNumber = 1;            this.scopeNumber = 2;            this.varNumber = 0;        } else {            this.prev = ctx;            this.locals = ctx.locals;            // Inherit local classes from surrounding block,            // just as for local variables.  Fixes 4074421.            this.classes = ctx.classes;            this.varNumber = ctx.varNumber;            this.field = ctx.field;            this.frameNumber = ctx.frameNumber;            this.scopeNumber = ctx.scopeNumber + 1;            this.node = node;        }    }     public Context(Context ctx) {        this(ctx, (Node)null);    }     /**     * Declare local     */    public int declare(Environment env, LocalMember local) {        //System.out.println(   "DECLARE= " + local.getName() + "=" + varNumber + ", read=" + local.readcount + ", write=" + local.writecount + ", hash=" + local.hashCode());        local.scopeNumber = scopeNumber;        if (this.field == null && idThis.equals(local.getName())) {            local.scopeNumber += 1; // Anticipate variable or initializer.        }        if (local.isInnerClass()) {            local.prev = classes;            classes = local;            return 0;        }         // Originally the statement:        //        //     local.subModifiers(M_INLINEABLE);        //        // was here with the comment:        //        //     // prevent inlining across call sites        //        // This statement prevented constant local variables from        // inlining. It didn't seem to do anything useful.        //        // The statement has been removed and an assertion has been        // added which mandates that the only members which are marked        // with M_INLINEABLE are the ones for which isConstant() is true.        // (Fix for 4106244.)        //        // Addition to the above comment: they might also be        // final variables initialized with 'this', 'super', or other        // final identifiers.  See VarDeclarationStatement.inline().        // So I've removed the assertion.  The original subModifiers        // call appears to have been there to fix nested class translation        // breakage, which has been fixed in VarDeclarationStatement        // now instead.  (Fix for 4073244.)         local.prev = locals;        locals = local;        local.number = varNumber;        varNumber += local.getType().stackSize();        return local.number;    }     /**     * Get a local variable by name     */    public    LocalMember getLocalField(Identifier name) {        for (LocalMember f = locals ; f != null ; f = f.prev) {            if (name.equals(f.getName())) {                return f;            }        }        return null;    }     /**     * Get the scope number for a reference to a member of this class     * (Larger scope numbers are more deeply nested.)     * @see LocalMember#scopeNumber     */    public    int getScopeNumber(ClassDefinition c) {        for (Context ctx = this; ctx != null; ctx = ctx.prev) {            if (ctx.field == null)  continue;            if (ctx.field.getClassDefinition() == c) {                return ctx.frameNumber;            }        }        return -1;    }     private    MemberDefinition getFieldCommon(Environment env, Identifier name,                                   boolean apparentOnly) throws AmbiguousMember, ClassNotFound {        // Note:  This is structured as a pair of parallel lookups.        // If we were to redesign Context, we might prefer to walk        // along a single chain of scopes.         LocalMember lf = getLocalField(name);        int ls = (lf == null) ? -2 : lf.scopeNumber;         ClassDefinition thisClass = field.getClassDefinition();         // Also look for a class member in a shallower scope.        for (ClassDefinition c = thisClass;             c != null;             c = c.getOuterClass()) {            MemberDefinition f = c.getVariable(env, name, thisClass);            if (f != null && getScopeNumber(c) > ls) {                if (apparentOnly && f.getClassDefinition() != c) {                    continue;                }                return f;            }        }         return lf;    }     /**     * Assign a number to a class field.     * (This is used to track definite assignment of some blank finals.)     */    public int declareFieldNumber(MemberDefinition field) {        return declare(null, new LocalMember(field));    }     /**     * Retrieve a number previously assigned by declareMember().     * Return -1 if there was no such assignment in this context.     */    public int getFieldNumber(MemberDefinition field) {        for (LocalMember f = locals ; f != null ; f = f.prev) {            if (f.getMember() == field) {                return f.number;            }        }        return -1;    }     /**     * Return the local field or member field corresponding to a number.     * Return null if there is no such field.     */    public MemberDefinition getElement(int number) {        for (LocalMember f = locals ; f != null ; f = f.prev) {            if (f.number == number) {                MemberDefinition field = f.getMember();                return (field != null) ? field : f;            }        }        return null;    }     /**     * Get a local class by name     */    public    LocalMember getLocalClass(Identifier name) {        for (LocalMember f = classes ; f != null ; f = f.prev) {            if (name.equals(f.getName())) {                return f;            }        }        return null;    }     private    MemberDefinition getClassCommon(Environment env, Identifier name,                                   boolean apparentOnly) throws ClassNotFound {        LocalMember lf = getLocalClass(name);        int ls = (lf == null) ? -2 : lf.scopeNumber;         // Also look for a class member in a shallower scope.        for (ClassDefinition c = field.getClassDefinition();             c != null;             c = c.getOuterClass()) {            // QUERY: We may need to get the inner class from a            // superclass of 'c'.  This call is prepared to            // resolve the superclass if necessary.  Can we arrange            // to assure that it is always previously resolved?            // This is one of a small number of problematic calls that            // requires 'getSuperClass' to resolve superclasses on demand.            // See 'ClassDefinition.getInnerClass(env, nm)'.            MemberDefinition f = c.getInnerClass(env, name);            if (f != null && getScopeNumber(c) > ls) {                if (apparentOnly && f.getClassDefinition() != c) {                    continue;                }                return f;            }        }         return lf;    }     /**     * Get either a local variable, or a field in a current class     */    public final    MemberDefinition getField(Environment env, Identifier name) throws AmbiguousMember, ClassNotFound {        return getFieldCommon(env, name, false);    }     /**     * Like getField, except that it skips over inherited fields.     * Used for error checking.     */    public final    MemberDefinition getApparentField(Environment env, Identifier name) throws AmbiguousMember, ClassNotFound {        return getFieldCommon(env, name, true);    }     /**     * Check if the given field is active in this context.     */    public boolean isInScope(LocalMember field) {        for (LocalMember f = locals ; f != null ; f = f.prev) {            if (field == f) {                return true;            }        }        return false;    }     /**     * Notice a reference (usually an uplevel one).     * Update the references list of every enclosing class     * which is enclosed by the scope of the target.     * Update decisions about which uplevels to make into fields.     * Return the uplevel reference descriptor, or null if it's local.     * <p>     * The target must be in scope in this context.     * So, call this method only from the check phase.     * (In other phases, the context may be less complete.)     * <p>     * This can and should be called both before and after classes are frozen.     * It should be a no-op, and will raise a compiler error if not.     */    public UplevelReference noteReference(Environment env, LocalMember target) {        int targetScopeNumber = !isInScope(target) ? -1 : target.scopeNumber;         // Walk outward visiting each scope.        // Note each distinct frame (i.e., enclosing method).        // For each frame in which the variable is uplevel,        // record the event in the references list of the enclosing class.        UplevelReference res = null;        int currentFrameNumber = -1;        for (Context refctx = this; refctx != null; refctx = refctx.prev) {            if (currentFrameNumber == refctx.frameNumber) {                continue;       // we're processing frames, not contexts            }            currentFrameNumber = refctx.frameNumber;            if (targetScopeNumber >= currentFrameNumber) {                break;          // the target is native to this frame            }             // process a frame which is using this variable as an uplevel            ClassDefinition refc = refctx.field.getClassDefinition();            UplevelReference r = refc.getReference(target);            r.noteReference(env, refctx);             // remember the reference pertaining to the innermost frame            if (res == null) {                res = r;            }        }        return res;    }     /**     * Implement a reference (usually an uplevel one).     * Call noteReference() first, to make sure the reference     * lists are up to date.     * <p>     * The resulting expression tree does not need checking;     * it can be code-generated right away.     * If the reference is not uplevel, the result is an IDENT or THIS.     */    public Expression makeReference(Environment env, LocalMember target) {        UplevelReference r = noteReference(env, target);         // Now create a referencing expression.        if (r != null) {            return r.makeLocalReference(env, this);        } else if (idThis.equals(target.getName())) {            return new ThisExpression(0, target);        } else {            return new IdentifierExpression(0, target);        }    }     /**     * Return a local expression which can serve as the base reference     * for the given field.  If the field is a constructor, return an     * expression for the implicit enclosing instance argument.     * <p>     * Return null if there is no need for such an argument,     * or if there was an error.     */    public Expression findOuterLink(Environment env, long where,                                    MemberDefinition f) {        // reqc is the base pointer type required to use f        ClassDefinition fc = f.getClassDefinition();        ClassDefinition reqc = f.isStatic() ? null                             : !f.isConstructor() ? fc                             : fc.isTopLevel() ? null                             : fc.getOuterClass();        if (reqc == null) {            return null;        }        return findOuterLink(env, where, reqc, f, false);    }     private static boolean match(Environment env,                                 ClassDefinition thisc, ClassDefinition reqc) {        try {            return thisc == reqc                || reqc.implementedBy(env, thisc.getClassDeclaration());        } catch (ClassNotFound ee) {            return false;        }    }     public Expression findOuterLink(Environment env, long where,                                    ClassDefinition reqc,                                    MemberDefinition f,                                    boolean needExactMatch) {        if (field.isStatic()) {            if (f == null) {                // say something like: undefined variable A.this                Identifier nm = reqc.getName().getFlatName().getName();                env.error(where, "undef.var", Identifier.lookup(nm,idThis));            } else if (f.isConstructor()) {                env.error(where, "no.outer.arg", reqc, f.getClassDeclaration());            } else if (f.isMethod()) {                env.error(where, "no.static.meth.access",                          f, f.getClassDeclaration());            } else {                env.error(where, "no.static.field.access", f.getName(),                          f.getClassDeclaration());            }            // This is an attempt at error recovery.            // Unfortunately, the constructor may throw            // a null pointer exception after failing to resolve            // 'idThis'.  Since an error message has already been            // issued previously, this exception is caught and            // silently ignored.  Ideally, we should avoid throwing            // the exception.            Expression e = new ThisExpression(where, this);            e.type = reqc.getType();            return e;        }         // use lp to scan for current instances (locals named "this")        LocalMember lp = locals;         // thise is a link expression being built up        Expression thise = null;         // root is the local variable (idThis) at the far left of thise        LocalMember root = null;         // thisc is the class of the link expression thise        ClassDefinition thisc = null;         // conCls is the class of the "this", in a constructor        ClassDefinition conCls = null;        if (field.isConstructor()) {            conCls = field.getClassDefinition();        }         if (!field.isMethod()) {            thisc = field.getClassDefinition();            thise = new ThisExpression(where, this);        }         while (true) {            if (thise == null) {                // start fresh from lp                while (lp != null && !idThis.equals(lp.getName())) {                    lp = lp.prev;                }                if (lp == null) {                    break;                }                thise = new ThisExpression(where, lp);                thisc = lp.getClassDefinition();                root = lp;                lp = lp.prev;            }             // Require exact class identity when called with            // 'needExactMatch' true.  This is done when checking            // the '<class>.this' syntax.  Fixes 4102393 and 4133457.            if (thisc == reqc ||                (!needExactMatch && match(env, thisc, reqc))) {                break;            }             // move out one step, if the current instance has an outer link             MemberDefinition outerMember = thisc.findOuterMember();            if (outerMember == null) {                thise = null;                continue;       // try to find more help in lp            }            ClassDefinition prevc = thisc;

Lines 1–500 of 861.