001: /*
002: * Copyright 1997-2003 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package sun.tools.tree;
027:
028: import sun.tools.java.*;
029: import sun.tools.tree.*;
030: import sun.tools.asm.Assembler;
031:
032: /**
033: * A reference from one scope to another.
034: *
035: * WARNING: The contents of this source file are not part of any
036: * supported API. Code that depends on them does so at its own risk:
037: * they are subject to change or removal without notice.
038: *
039: */
040:
041: public class UplevelReference implements Constants {
042: /**
043: * The class in which the reference occurs.
044: */
045: ClassDefinition client;
046:
047: /**
048: * The field being referenced.
049: * It is always a final argument or a final local variable.
050: * (An uplevel reference to a field of a class C is fetched
051: * through an implicit uplevel reference to C.this, which is
052: * an argument.)
053: */
054: LocalMember target;
055:
056: /**
057: * The local variable which bears a copy of the target's value,
058: * for all methods of the client class.
059: * Its name is "this$C" for <code>this.C</code> or
060: * "val$x" for other target variables <code>x</code>.
061: * <p>
062: * This local variable is always a constructor argument,
063: * and is therefore usable only in the constructor and in initializers.
064: * All other methods use the local field.
065: * @see #localField
066: */
067: LocalMember localArgument;
068:
069: /**
070: * A private synthetic field of the client class which
071: * bears a copy of the target's value.
072: * The compiler tries to avoid creating it if possible.
073: * The field has the same name and type as the localArgument.
074: * @see #localArgument
075: */
076: MemberDefinition localField;
077:
078: /**
079: * The next item on the references list of the client.
080: */
081: UplevelReference next;
082:
083: /**
084: * constructor
085: */
086: public UplevelReference(ClassDefinition client, LocalMember target) {
087: this .client = client;
088: this .target = target;
089:
090: // Choose a name and build a variable declaration node.
091: Identifier valName;
092: if (target.getName().equals(idThis)) {
093: ClassDefinition tc = target.getClassDefinition();
094: // It should always be true that tc.enclosingClassOf(client).
095: // If it were false, the numbering scheme would fail
096: // to produce unique names, since we'd be trying
097: // to number classes which were not in the sequence
098: // of enclosing scopes. The next paragraph of this
099: // code robustly deals with that possibility, however,
100: // by detecting name collisions and perturbing the names.
101: int depth = 0;
102: for (ClassDefinition pd = tc; !pd.isTopLevel(); pd = pd
103: .getOuterClass()) {
104: // The inner classes specification states that the name of
105: // a private field containing a reference to the outermost
106: // enclosing instance is named "this$0". That outermost
107: // enclosing instance is always the innermost toplevel class.
108: depth += 1;
109: }
110: // In this example, T1,T2,T3 are all top-level (static),
111: // while I4,I5,I6,I7 are all inner. Each of the inner classes
112: // will have a single up-level "this$N" reference to the next
113: // class out. Only the outermost "this$0" will refer to a
114: // top-level class, T3.
115: //
116: // class T1 {
117: // static class T2 {
118: // static class T3 {
119: // class I4 {
120: // class I5 {
121: // class I6 {
122: // // at this point we have these fields in various places:
123: // // I4 this$0; I5 this$1; I6 this$2;
124: // }
125: // }
126: // class I7 {
127: // // I4 this$0; I7 this$1;
128: // }
129: // }
130: // }
131: // }
132: // }
133: valName = Identifier.lookup(prefixThis + depth);
134: } else {
135: valName = Identifier.lookup(prefixVal + target.getName());
136: }
137:
138: // Make reasonably certain that valName is unique to this client.
139: // (This check can be fooled by malicious naming of explicit
140: // constructor arguments, or of inherited fields.)
141: Identifier base = valName;
142: int tick = 0;
143: while (true) {
144: boolean failed = (client.getFirstMatch(valName) != null);
145: for (UplevelReference r = client.getReferences(); r != null; r = r.next) {
146: if (r.target.getName().equals(valName)) {
147: failed = true;
148: }
149: }
150: if (!failed) {
151: break;
152: }
153: // try another name
154: valName = Identifier.lookup(base + "$" + (++tick));
155: }
156:
157: // Build the constructor argument.
158: // Like "this", it wil be shared equally by all constructors of client.
159: localArgument = new LocalMember(target.getWhere(), client,
160: M_FINAL | M_SYNTHETIC, target.getType(), valName);
161: }
162:
163: /**
164: * Insert self into a list of references.
165: * Maintain "isEarlierThan" as an invariant of the list.
166: * This is important (a) to maximize stability of signatures,
167: * and (b) to allow uplevel "this" parameters to come at the
168: * front of every argument list they appear in.
169: */
170: public UplevelReference insertInto(UplevelReference references) {
171: if (references == null || isEarlierThan(references)) {
172: next = references;
173: return this ;
174: } else {
175: UplevelReference prev = references;
176: while (!(prev.next == null || isEarlierThan(prev.next))) {
177: prev = prev.next;
178: }
179: next = prev.next;
180: prev.next = this ;
181: return references;
182: }
183: }
184:
185: /**
186: * Tells if self precedes the other in the canonical ordering.
187: */
188: public final boolean isEarlierThan(UplevelReference other) {
189: // Outer fields always come first.
190: if (isClientOuterField()) {
191: return true;
192: } else if (other.isClientOuterField()) {
193: return false;
194: }
195:
196: // Now it doesn't matter what the order is; use string comparison.
197: LocalMember target2 = other.target;
198: Identifier name = target.getName();
199: Identifier name2 = target2.getName();
200: int cmp = name.toString().compareTo(name2.toString());
201: if (cmp != 0) {
202: return cmp < 0;
203: }
204: Identifier cname = target.getClassDefinition().getName();
205: Identifier cname2 = target2.getClassDefinition().getName();
206: int ccmp = cname.toString().compareTo(cname2.toString());
207: return ccmp < 0;
208: }
209:
210: /**
211: * the target of this reference
212: */
213: public final LocalMember getTarget() {
214: return target;
215: }
216:
217: /**
218: * the local argument for this reference
219: */
220: public final LocalMember getLocalArgument() {
221: return localArgument;
222: }
223:
224: /**
225: * the field allocated in the client for this reference
226: */
227: public final MemberDefinition getLocalField() {
228: return localField;
229: }
230:
231: /**
232: * Get the local field, creating one if necessary.
233: * The client class must not be frozen.
234: */
235: public final MemberDefinition getLocalField(Environment env) {
236: if (localField == null) {
237: makeLocalField(env);
238: }
239: return localField;
240: }
241:
242: /**
243: * the client class
244: */
245: public final ClassDefinition getClient() {
246: return client;
247: }
248:
249: /**
250: * the next reference in the client's list
251: */
252: public final UplevelReference getNext() {
253: return next;
254: }
255:
256: /**
257: * Tell if this uplevel reference is the up-level "this" pointer
258: * of an inner class. Such references are treated differently
259: * than others, because they affect constructor calls across
260: * compilation units.
261: */
262: public boolean isClientOuterField() {
263: MemberDefinition outerf = client.findOuterMember();
264: return (outerf != null) && (localField == outerf);
265: }
266:
267: /**
268: * Tell if my local argument is directly available in this context.
269: * If not, the uplevel reference will have to be via a class field.
270: * <p>
271: * This must be called in a context which is local
272: * to the client of the uplevel reference.
273: */
274: public boolean localArgumentAvailable(Environment env, Context ctx) {
275: MemberDefinition reff = ctx.field;
276: if (reff.getClassDefinition() != client) {
277: throw new CompilerError("localArgumentAvailable");
278: }
279: return (reff.isConstructor() || reff.isVariable() || reff
280: .isInitializer());
281: }
282:
283: /**
284: * Process an uplevel reference.
285: * The only decision to make at this point is whether
286: * to build a "localField" instance variable, which
287: * is done (lazily) when localArgumentAvailable() proves false.
288: */
289: public void noteReference(Environment env, Context ctx) {
290: if (localField == null && !localArgumentAvailable(env, ctx)) {
291: // We need an instance variable unless client is a constructor.
292: makeLocalField(env);
293: }
294: }
295:
296: private void makeLocalField(Environment env) {
297: // Cannot alter decisions like this one at a late date.
298: client.referencesMustNotBeFrozen();
299: int mod = M_PRIVATE | M_FINAL | M_SYNTHETIC;
300: localField = env.makeMemberDefinition(env, localArgument
301: .getWhere(), client, null, mod,
302: localArgument.getType(), localArgument.getName(), null,
303: null, null);
304: }
305:
306: /**
307: * Assuming noteReference() is all taken care of,
308: * build an uplevel reference.
309: * <p>
310: * This must be called in a context which is local
311: * to the client of the uplevel reference.
312: */
313: public Expression makeLocalReference(Environment env, Context ctx) {
314: if (ctx.field.getClassDefinition() != client) {
315: throw new CompilerError("makeLocalReference");
316: }
317: if (localArgumentAvailable(env, ctx)) {
318: return new IdentifierExpression(0, localArgument);
319: } else {
320: return makeFieldReference(env, ctx);
321: }
322: }
323:
324: /**
325: * As with makeLocalReference(), build a locally-usable reference.
326: * Ignore the availability of local arguments; always use a class field.
327: */
328: public Expression makeFieldReference(Environment env, Context ctx) {
329: Expression e = ctx.findOuterLink(env, 0, localField);
330: return new FieldExpression(0, e, localField);
331: }
332:
333: /**
334: * During the inline phase, call this on a list of references
335: * for which the code phase will later emit arguments.
336: * It will make sure that any "double-uplevel" values
337: * needed by the callee are also present at the call site.
338: * <p>
339: * If any reference is a "ClientOuterField", it is skipped
340: * by this method (and by willCodeArguments). This is because
341: */
342: public void willCodeArguments(Environment env, Context ctx) {
343: if (!isClientOuterField()) {
344: ctx.noteReference(env, target);
345: }
346:
347: if (next != null) {
348: next.willCodeArguments(env, ctx);
349: }
350: }
351:
352: /**
353: * Code is being generated for a call to a constructor of
354: * the client class. Push an argument for the constructor.
355: */
356: public void codeArguments(Environment env, Context ctx,
357: Assembler asm, long where, MemberDefinition conField) {
358: if (!isClientOuterField()) {
359: Expression e = ctx.makeReference(env, target);
360: e.codeValue(env, ctx, asm);
361: }
362:
363: if (next != null) {
364: next.codeArguments(env, ctx, asm, where, conField);
365: }
366: }
367:
368: /**
369: * Code is being generated for a constructor of the client class.
370: * Emit code which initializes the instance.
371: */
372: public void codeInitialization(Environment env, Context ctx,
373: Assembler asm, long where, MemberDefinition conField) {
374: // If the reference is a clientOuterField, then the initialization
375: // code is generated in MethodExpression.makeVarInits().
376: // (Fix for bug 4075063.)
377: if (localField != null && !isClientOuterField()) {
378: Expression e = ctx.makeReference(env, target);
379: Expression f = makeFieldReference(env, ctx);
380: e = new AssignExpression(e.getWhere(), f, e);
381: e.type = localField.getType();
382: e.code(env, ctx, asm);
383: }
384:
385: if (next != null) {
386: next.codeInitialization(env, ctx, asm, where, conField);
387: }
388: }
389:
390: public String toString() {
391: return "[" + localArgument + " in " + client + "]";
392: }
393: }
|