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