View Javadoc
1   /*
2    * Copyright (c) 1997, 2003, Oracle and/or its affiliates. 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.  Oracle designates this
8    * particular file as subject to the "Classpath" exception as provided
9    * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22   * or visit www.oracle.com if you need additional information or have any
23   * questions.
24   */
25  
26  package sun.tools.tree;
27  
28  import sun.tools.java.*;
29  import sun.tools.tree.*;
30  import 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  
41  public
42  class 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 }