/* * 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. * */ public class 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 + "]"; } }