blob: 3b60d2737ae5f1d59f9790d74dbaeeb2c1352483 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1997-2003 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package sun.tools.tree;
27
28import sun.tools.java.*;
29import sun.tools.tree.*;
30import sun.tools.asm.Assembler;
31
32/**
33 * A reference from one scope to another.
34 *
35 * WARNING: The contents of this source file are not part of any
36 * supported API. Code that depends on them does so at its own risk:
37 * they are subject to change or removal without notice.
38 *
39 */
40
41public
42class UplevelReference implements Constants {
43 /**
44 * The class in which the reference occurs.
45 */
46 ClassDefinition client;
47
48 /**
49 * The field being referenced.
50 * It is always a final argument or a final local variable.
51 * (An uplevel reference to a field of a class C is fetched
52 * through an implicit uplevel reference to C.this, which is
53 * an argument.)
54 */
55 LocalMember target;
56
57 /**
58 * The local variable which bears a copy of the target's value,
59 * for all methods of the client class.
60 * Its name is "this$C" for <code>this.C</code> or
61 * "val$x" for other target variables <code>x</code>.
62 * <p>
63 * This local variable is always a constructor argument,
64 * and is therefore usable only in the constructor and in initializers.
65 * All other methods use the local field.
66 * @see #localField
67 */
68 LocalMember localArgument;
69
70 /**
71 * A private synthetic field of the client class which
72 * bears a copy of the target's value.
73 * The compiler tries to avoid creating it if possible.
74 * The field has the same name and type as the localArgument.
75 * @see #localArgument
76 */
77 MemberDefinition localField;
78
79 /**
80 * The next item on the references list of the client.
81 */
82 UplevelReference next;
83
84 /**
85 * constructor
86 */
87 public UplevelReference(ClassDefinition client, LocalMember target) {
88 this.client = client;
89 this.target = target;
90
91 // Choose a name and build a variable declaration node.
92 Identifier valName;
93 if (target.getName().equals(idThis)) {
94 ClassDefinition tc = target.getClassDefinition();
95 // It should always be true that tc.enclosingClassOf(client).
96 // If it were false, the numbering scheme would fail
97 // to produce unique names, since we'd be trying
98 // to number classes which were not in the sequence
99 // of enclosing scopes. The next paragraph of this
100 // code robustly deals with that possibility, however,
101 // by detecting name collisions and perturbing the names.
102 int depth = 0;
103 for (ClassDefinition pd = tc; !pd.isTopLevel(); pd = pd.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();
146 r != null; r = r.next) {
147 if (r.target.getName().equals(valName)) {
148 failed = true;
149 }
150 }
151 if (!failed) {
152 break;
153 }
154 // try another name
155 valName = Identifier.lookup(base + "$" + (++tick));
156 }
157
158 // Build the constructor argument.
159 // Like "this", it wil be shared equally by all constructors of client.
160 localArgument = new LocalMember(target.getWhere(),
161 client,
162 M_FINAL | M_SYNTHETIC,
163 target.getType(),
164 valName);
165 }
166
167 /**
168 * Insert self into a list of references.
169 * Maintain "isEarlierThan" as an invariant of the list.
170 * This is important (a) to maximize stability of signatures,
171 * and (b) to allow uplevel "this" parameters to come at the
172 * front of every argument list they appear in.
173 */
174 public UplevelReference insertInto(UplevelReference references) {
175 if (references == null || isEarlierThan(references)) {
176 next = references;
177 return this;
178 } else {
179 UplevelReference prev = references;
180 while (!(prev.next == null || isEarlierThan(prev.next))) {
181 prev = prev.next;
182 }
183 next = prev.next;
184 prev.next = this;
185 return references;
186 }
187 }
188
189 /**
190 * Tells if self precedes the other in the canonical ordering.
191 */
192 public final boolean isEarlierThan(UplevelReference other) {
193 // Outer fields always come first.
194 if (isClientOuterField()) {
195 return true;
196 } else if (other.isClientOuterField()) {
197 return false;
198 }
199
200 // Now it doesn't matter what the order is; use string comparison.
201 LocalMember target2 = other.target;
202 Identifier name = target.getName();
203 Identifier name2 = target2.getName();
204 int cmp = name.toString().compareTo(name2.toString());
205 if (cmp != 0) {
206 return cmp < 0;
207 }
208 Identifier cname = target.getClassDefinition().getName();
209 Identifier cname2 = target2.getClassDefinition().getName();
210 int ccmp = cname.toString().compareTo(cname2.toString());
211 return ccmp < 0;
212 }
213
214 /**
215 * the target of this reference
216 */
217 public final LocalMember getTarget() {
218 return target;
219 }
220
221 /**
222 * the local argument for this reference
223 */
224 public final LocalMember getLocalArgument() {
225 return localArgument;
226 }
227
228 /**
229 * the field allocated in the client for this reference
230 */
231 public final MemberDefinition getLocalField() {
232 return localField;
233 }
234
235 /**
236 * Get the local field, creating one if necessary.
237 * The client class must not be frozen.
238 */
239 public final MemberDefinition getLocalField(Environment env) {
240 if (localField == null) {
241 makeLocalField(env);
242 }
243 return localField;
244 }
245
246 /**
247 * the client class
248 */
249 public final ClassDefinition getClient() {
250 return client;
251 }
252
253 /**
254 * the next reference in the client's list
255 */
256 public final UplevelReference getNext() {
257 return next;
258 }
259
260 /**
261 * Tell if this uplevel reference is the up-level "this" pointer
262 * of an inner class. Such references are treated differently
263 * than others, because they affect constructor calls across
264 * compilation units.
265 */
266 public boolean isClientOuterField() {
267 MemberDefinition outerf = client.findOuterMember();
268 return (outerf != null) && (localField == outerf);
269 }
270
271 /**
272 * Tell if my local argument is directly available in this context.
273 * If not, the uplevel reference will have to be via a class field.
274 * <p>
275 * This must be called in a context which is local
276 * to the client of the uplevel reference.
277 */
278 public boolean localArgumentAvailable(Environment env, Context ctx) {
279 MemberDefinition reff = ctx.field;
280 if (reff.getClassDefinition() != client) {
281 throw new CompilerError("localArgumentAvailable");
282 }
283 return ( reff.isConstructor()
284 || reff.isVariable()
285 || reff.isInitializer() );
286 }
287
288 /**
289 * Process an uplevel reference.
290 * The only decision to make at this point is whether
291 * to build a "localField" instance variable, which
292 * is done (lazily) when localArgumentAvailable() proves false.
293 */
294 public void noteReference(Environment env, Context ctx) {
295 if (localField == null && !localArgumentAvailable(env, ctx)) {
296 // We need an instance variable unless client is a constructor.
297 makeLocalField(env);
298 }
299 }
300
301 private void makeLocalField(Environment env) {
302 // Cannot alter decisions like this one at a late date.
303 client.referencesMustNotBeFrozen();
304 int mod = M_PRIVATE | M_FINAL | M_SYNTHETIC;
305 localField = env.makeMemberDefinition(env,
306 localArgument.getWhere(),
307 client, null,
308 mod,
309 localArgument.getType(),
310 localArgument.getName(),
311 null, null, null);
312 }
313
314 /**
315 * Assuming noteReference() is all taken care of,
316 * build an uplevel reference.
317 * <p>
318 * This must be called in a context which is local
319 * to the client of the uplevel reference.
320 */
321 public Expression makeLocalReference(Environment env, Context ctx) {
322 if (ctx.field.getClassDefinition() != client) {
323 throw new CompilerError("makeLocalReference");
324 }
325 if (localArgumentAvailable(env, ctx)) {
326 return new IdentifierExpression(0, localArgument);
327 } else {
328 return makeFieldReference(env, ctx);
329 }
330 }
331
332 /**
333 * As with makeLocalReference(), build a locally-usable reference.
334 * Ignore the availability of local arguments; always use a class field.
335 */
336 public Expression makeFieldReference(Environment env, Context ctx) {
337 Expression e = ctx.findOuterLink(env, 0, localField);
338 return new FieldExpression(0, e, localField);
339 }
340
341 /**
342 * During the inline phase, call this on a list of references
343 * for which the code phase will later emit arguments.
344 * It will make sure that any "double-uplevel" values
345 * needed by the callee are also present at the call site.
346 * <p>
347 * If any reference is a "ClientOuterField", it is skipped
348 * by this method (and by willCodeArguments). This is because
349 */
350 public void willCodeArguments(Environment env, Context ctx) {
351 if (!isClientOuterField()) {
352 ctx.noteReference(env, target);
353 }
354
355 if (next != null) {
356 next.willCodeArguments(env, ctx);
357 }
358 }
359
360 /**
361 * Code is being generated for a call to a constructor of
362 * the client class. Push an argument for the constructor.
363 */
364 public void codeArguments(Environment env, Context ctx, Assembler asm,
365 long where, MemberDefinition conField) {
366 if (!isClientOuterField()) {
367 Expression e = ctx.makeReference(env, target);
368 e.codeValue(env, ctx, asm);
369 }
370
371 if (next != null) {
372 next.codeArguments(env, ctx, asm, where, conField);
373 }
374 }
375
376 /**
377 * Code is being generated for a constructor of the client class.
378 * Emit code which initializes the instance.
379 */
380 public void codeInitialization(Environment env, Context ctx, Assembler asm,
381 long where, MemberDefinition conField) {
382 // If the reference is a clientOuterField, then the initialization
383 // code is generated in MethodExpression.makeVarInits().
384 // (Fix for bug 4075063.)
385 if (localField != null && !isClientOuterField()) {
386 Expression e = ctx.makeReference(env, target);
387 Expression f = makeFieldReference(env, ctx);
388 e = new AssignExpression(e.getWhere(), f, e);
389 e.type = localField.getType();
390 e.code(env, ctx, asm);
391 }
392
393 if (next != null) {
394 next.codeInitialization(env, ctx, asm, where, conField);
395 }
396 }
397
398 public String toString() {
399 return "[" + localArgument + " in " + client + "]";
400 }
401}