401 lines · 14.1 KB · Java
RawDownload
/* * Copyright (c) 1997, 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.tree.*;import sun.tools.asm.Assembler; /** * A reference from one scope to another. * * 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 UplevelReference implements Constants {    /**     * The class in which the reference occurs.     */    ClassDefinition client;     /**     * The field being referenced.     * It is always a final argument or a final local variable.     * (An uplevel reference to a field of a class C is fetched     * through an implicit uplevel reference to C.this, which is     * an argument.)     */    LocalMember target;     /**     * The local variable which bears a copy of the target's value,     * for all methods of the client class.     * Its name is "this$C" for <code>this.C</code> or     * "val$x" for other target variables <code>x</code>.     * <p>     * This local variable is always a constructor argument,     * and is therefore usable only in the constructor and in initializers.     * All other methods use the local field.     * @see #localField     */    LocalMember localArgument;     /**     * A private synthetic field of the client class which     * bears a copy of the target's value.     * The compiler tries to avoid creating it if possible.     * The field has the same name and type as the localArgument.     * @see #localArgument     */    MemberDefinition localField;     /**     * The next item on the references list of the client.     */    UplevelReference next;     /**     * constructor     */    public UplevelReference(ClassDefinition client, LocalMember target) {        this.client = client;        this.target = target;         // Choose a name and build a variable declaration node.        Identifier valName;        if (target.getName().equals(idThis)) {            ClassDefinition tc = target.getClassDefinition();            // It should always be true that tc.enclosingClassOf(client).            // If it were false, the numbering scheme would fail            // to produce unique names, since we'd be trying            // to number classes which were not in the sequence            // of enclosing scopes.  The next paragraph of this            // code robustly deals with that possibility, however,            // by detecting name collisions and perturbing the names.            int depth = 0;            for (ClassDefinition pd = tc; !pd.isTopLevel(); pd = pd.getOuterClass()) {                // The inner classes specification states that the name of                // a private field containing a reference to the outermost                // enclosing instance is named "this$0".  That outermost                // enclosing instance is always the innermost toplevel class.                depth += 1;            }            // In this example, T1,T2,T3 are all top-level (static),            // while I4,I5,I6,I7 are all inner.  Each of the inner classes            // will have a single up-level "this$N" reference to the next            // class out.  Only the outermost "this$0" will refer to a            // top-level class, T3.            //            // class T1 {            //  static class T2 {            //   static class T3 {            //    class I4 {            //     class I5 {            //      class I6 {            //       // at this point we have these fields in various places:            //       // I4 this$0; I5 this$1; I6 this$2;            //      }            //     }            //     class I7 {            //       // I4 this$0; I7 this$1;            //     }            //    }            //   }            //  }            // }            valName = Identifier.lookup(prefixThis + depth);        } else {            valName = Identifier.lookup(prefixVal + target.getName());        }         // Make reasonably certain that valName is unique to this client.        // (This check can be fooled by malicious naming of explicit        // constructor arguments, or of inherited fields.)        Identifier base = valName;        int tick = 0;        while (true) {            boolean failed = (client.getFirstMatch(valName) != null);            for (UplevelReference r = client.getReferences();                    r != null; r = r.next) {                if (r.target.getName().equals(valName)) {                    failed = true;                }            }            if (!failed) {                break;            }            // try another name            valName = Identifier.lookup(base + "$" + (++tick));        }         // Build the constructor argument.        // Like "this", it wil be shared equally by all constructors of client.        localArgument = new LocalMember(target.getWhere(),                                       client,                                       M_FINAL | M_SYNTHETIC,                                       target.getType(),                                       valName);    }     /**     * Insert self into a list of references.     * Maintain "isEarlierThan" as an invariant of the list.     * This is important (a) to maximize stability of signatures,     * and (b) to allow uplevel "this" parameters to come at the     * front of every argument list they appear in.     */    public UplevelReference insertInto(UplevelReference references) {        if (references == null || isEarlierThan(references)) {            next = references;            return this;        } else {            UplevelReference prev = references;            while (!(prev.next == null || isEarlierThan(prev.next))) {                prev = prev.next;            }            next = prev.next;            prev.next = this;            return references;        }    }     /**     * Tells if self precedes the other in the canonical ordering.     */    public final boolean isEarlierThan(UplevelReference other) {        // Outer fields always come first.        if (isClientOuterField()) {            return true;        } else if (other.isClientOuterField()) {            return false;        }         // Now it doesn't matter what the order is; use string comparison.        LocalMember target2 = other.target;        Identifier name = target.getName();        Identifier name2 = target2.getName();        int cmp = name.toString().compareTo(name2.toString());        if (cmp != 0) {            return cmp < 0;        }        Identifier cname = target.getClassDefinition().getName();        Identifier cname2 = target2.getClassDefinition().getName();        int ccmp = cname.toString().compareTo(cname2.toString());        return ccmp < 0;    }     /**     * the target of this reference     */    public final LocalMember getTarget() {        return target;    }     /**     * the local argument for this reference     */    public final LocalMember getLocalArgument() {        return localArgument;    }     /**     * the field allocated in the client for this reference     */    public final MemberDefinition getLocalField() {        return localField;    }     /**     * Get the local field, creating one if necessary.     * The client class must not be frozen.     */    public final MemberDefinition getLocalField(Environment env) {        if (localField == null) {            makeLocalField(env);        }        return localField;    }     /**     * the client class     */    public final ClassDefinition getClient() {        return client;    }     /**     * the next reference in the client's list     */    public final UplevelReference getNext() {        return next;    }     /**     * Tell if this uplevel reference is the up-level "this" pointer     * of an inner class.  Such references are treated differently     * than others, because they affect constructor calls across     * compilation units.     */    public boolean isClientOuterField() {        MemberDefinition outerf = client.findOuterMember();        return (outerf != null) && (localField == outerf);    }     /**     * Tell if my local argument is directly available in this context.     * If not, the uplevel reference will have to be via a class field.     * <p>     * This must be called in a context which is local     * to the client of the uplevel reference.     */    public boolean localArgumentAvailable(Environment env, Context ctx) {        MemberDefinition reff = ctx.field;        if (reff.getClassDefinition() != client) {            throw new CompilerError("localArgumentAvailable");        }        return (   reff.isConstructor()                || reff.isVariable()                || reff.isInitializer() );    }     /**     * Process an uplevel reference.     * The only decision to make at this point is whether     * to build a "localField" instance variable, which     * is done (lazily) when localArgumentAvailable() proves false.     */    public void noteReference(Environment env, Context ctx) {        if (localField == null && !localArgumentAvailable(env, ctx)) {            // We need an instance variable unless client is a constructor.            makeLocalField(env);        }    }     private void makeLocalField(Environment env) {        // Cannot alter decisions like this one at a late date.        client.referencesMustNotBeFrozen();        int mod = M_PRIVATE | M_FINAL | M_SYNTHETIC;        localField = env.makeMemberDefinition(env,                                             localArgument.getWhere(),                                             client, null,                                             mod,                                             localArgument.getType(),                                             localArgument.getName(),                                             null, null, null);    }     /**     * Assuming noteReference() is all taken care of,     * build an uplevel reference.     * <p>     * This must be called in a context which is local     * to the client of the uplevel reference.     */    public Expression makeLocalReference(Environment env, Context ctx) {        if (ctx.field.getClassDefinition() != client) {            throw new CompilerError("makeLocalReference");        }        if (localArgumentAvailable(env, ctx)) {            return new IdentifierExpression(0, localArgument);        } else {            return makeFieldReference(env, ctx);        }    }     /**     * As with makeLocalReference(), build a locally-usable reference.     * Ignore the availability of local arguments; always use a class field.     */    public Expression makeFieldReference(Environment env, Context ctx) {        Expression e = ctx.findOuterLink(env, 0, localField);        return new FieldExpression(0, e, localField);    }     /**     * During the inline phase, call this on a list of references     * for which the code phase will later emit arguments.     * It will make sure that any "double-uplevel" values     * needed by the callee are also present at the call site.     * <p>     * If any reference is a "ClientOuterField", it is skipped     * by this method (and by willCodeArguments).  This is because     */    public void willCodeArguments(Environment env, Context ctx) {        if (!isClientOuterField()) {            ctx.noteReference(env, target);        }         if (next != null) {            next.willCodeArguments(env, ctx);        }    }     /**     * Code is being generated for a call to a constructor of     * the client class.  Push an argument for the constructor.     */    public void codeArguments(Environment env, Context ctx, Assembler asm,                              long where, MemberDefinition conField) {        if (!isClientOuterField()) {            Expression e = ctx.makeReference(env, target);            e.codeValue(env, ctx, asm);        }         if (next != null) {            next.codeArguments(env, ctx, asm, where, conField);        }    }     /**     * Code is being generated for a constructor of the client class.     * Emit code which initializes the instance.     */    public void codeInitialization(Environment env, Context ctx, Assembler asm,                                   long where, MemberDefinition conField) {        // If the reference is a clientOuterField, then the initialization        // code is generated in MethodExpression.makeVarInits().        // (Fix for bug 4075063.)        if (localField != null && !isClientOuterField()) {            Expression e = ctx.makeReference(env, target);            Expression f = makeFieldReference(env, ctx);            e = new AssignExpression(e.getWhere(), f, e);            e.type = localField.getType();            e.code(env, ctx, asm);        }         if (next != null) {            next.codeInitialization(env, ctx, asm, where, conField);        }    }     public String toString() {        return "[" + localArgument + " in " + client + "]";    }}