blob: dd4d7d0bcad2bb1e215e10008c11a5f077133263 [file] [log] [blame]
/*
* 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 + "]";
}
}