1 /*
2  * Copyright (c) 1994, 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.asm.Assembler;
30 import sun.tools.asm.LocalVariable;
31 import java.io.PrintStream;
32 import java.util.Hashtable;
33 
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 public
40 class IdentifierExpression extends Expression {
41     Identifier id;
42     MemberDefinition field;
43     Expression implementation;
44 
45     /**
46      * Constructor
47      */
IdentifierExpression(long where, Identifier id)48     public IdentifierExpression(long where, Identifier id) {
49         super(IDENT, where, Type.tError);
50         this.id = id;
51     }
IdentifierExpression(IdentifierToken id)52     public IdentifierExpression(IdentifierToken id) {
53         this(id.getWhere(), id.getName());
54     }
IdentifierExpression(long where, MemberDefinition field)55     public IdentifierExpression(long where, MemberDefinition field) {
56         super(IDENT, where, field.getType());
57         this.id = field.getName();
58         this.field = field;
59     }
60 
getImplementation()61     public Expression getImplementation() {
62         if (implementation != null)
63             return implementation;
64         return this;
65     }
66 
67     /**
68      * Check if the expression is equal to a value
69      */
equals(Identifier id)70     public boolean equals(Identifier id) {
71         return this.id.equals(id);
72     }
73 
74 
75     /**
76      * Assign a value to this identifier.  [It must already be "bound"]
77      */
assign(Environment env, Context ctx, Vset vset)78     private Vset assign(Environment env, Context ctx, Vset vset) {
79         if (field.isLocal()) {
80             LocalMember local = (LocalMember)field;
81             if (local.scopeNumber < ctx.frameNumber) {
82                 env.error(where, "assign.to.uplevel", id);
83             }
84             if (local.isFinal()) {
85                 // allow definite single assignment of blank finals
86                 if (!local.isBlankFinal()) {
87                     env.error(where, "assign.to.final", id);
88                 } else if (!vset.testVarUnassigned(local.number)) {
89                     env.error(where, "assign.to.blank.final", id);
90                 }
91             }
92             vset.addVar(local.number);
93             local.writecount++;
94         } else if (field.isFinal()) {
95             vset = FieldExpression.checkFinalAssign(env, ctx, vset,
96                                                     where, field);
97         }
98         return vset;
99     }
100 
101     /**
102      * Get the value of this identifier.  [ It must already be "bound"]
103      */
get(Environment env, Context ctx, Vset vset)104     private Vset get(Environment env, Context ctx, Vset vset) {
105         if (field.isLocal()) {
106             LocalMember local = (LocalMember)field;
107             if (local.scopeNumber < ctx.frameNumber && !local.isFinal()) {
108                 env.error(where, "invalid.uplevel", id);
109             }
110             if (!vset.testVar(local.number)) {
111                 env.error(where, "var.not.initialized", id);
112                 vset.addVar(local.number);
113             }
114             local.readcount++;
115         } else {
116             if (!field.isStatic()) {
117                 if (!vset.testVar(ctx.getThisNumber())) {
118                     env.error(where, "access.inst.before.super", id);
119                     implementation = null;
120                 }
121             }
122             if (field.isBlankFinal()) {
123                 int number = ctx.getFieldNumber(field);
124                 if (number >= 0 && !vset.testVar(number)) {
125                     env.error(where, "var.not.initialized", id);
126                 }
127             }
128         }
129         return vset;
130     }
131 
132     /**
133      * Bind to a field
134      */
bind(Environment env, Context ctx)135     boolean bind(Environment env, Context ctx) {
136         try {
137             field = ctx.getField(env, id);
138             if (field == null) {
139                 for (ClassDefinition cdef = ctx.field.getClassDefinition();
140                      cdef != null; cdef = cdef.getOuterClass()) {
141                     if (cdef.findAnyMethod(env, id) != null) {
142                         env.error(where, "invalid.var", id,
143                                   ctx.field.getClassDeclaration());
144                         return false;
145                     }
146                 }
147                 env.error(where, "undef.var", id);
148                 return false;
149             }
150 
151             type = field.getType();
152 
153             // Check access permission
154             if (!ctx.field.getClassDefinition().canAccess(env, field)) {
155                 env.error(where, "no.field.access",
156                           id, field.getClassDeclaration(),
157                           ctx.field.getClassDeclaration());
158                 return false;
159             }
160 
161             // Find out how to access this variable.
162             if (field.isLocal()) {
163                 LocalMember local = (LocalMember)field;
164                 if (local.scopeNumber < ctx.frameNumber) {
165                     // get a "val$x" copy via the current object
166                     implementation = ctx.makeReference(env, local);
167                 }
168             } else {
169                 MemberDefinition f = field;
170 
171                 if (f.reportDeprecated(env)) {
172                     env.error(where, "warn.field.is.deprecated",
173                               id, f.getClassDefinition());
174                 }
175 
176                 ClassDefinition fclass = f.getClassDefinition();
177                 if (fclass != ctx.field.getClassDefinition()) {
178                     // Maybe an inherited field hides an apparent variable.
179                     MemberDefinition f2 = ctx.getApparentField(env, id);
180                     if (f2 != null && f2 != f) {
181                         ClassDefinition c = ctx.findScope(env, fclass);
182                         if (c == null)  c = f.getClassDefinition();
183                         if (f2.isLocal()) {
184                             env.error(where, "inherited.hides.local",
185                                       id, c.getClassDeclaration());
186                         } else {
187                             env.error(where, "inherited.hides.field",
188                                       id, c.getClassDeclaration(),
189                                       f2.getClassDeclaration());
190                         }
191                     }
192                 }
193 
194                 // Rewrite as a FieldExpression.
195                 // Access methods for private fields, if needed, will be added
196                 // during subsequent processing of the FieldExpression.  See
197                 // method 'FieldExpression.checkCommon'. This division of labor
198                 // is somewhat awkward, as most further processing of a
199                 // FieldExpression during the checking phase is suppressed when
200                 // the referenced field is pre-set as it is here.
201 
202                 if (f.isStatic()) {
203                     Expression base = new TypeExpression(where,
204                                         f.getClassDeclaration().getType());
205                     implementation = new FieldExpression(where, null, f);
206                 } else {
207                     Expression base = ctx.findOuterLink(env, where, f);
208                     if (base != null) {
209                         implementation = new FieldExpression(where, base, f);
210                     }
211                 }
212             }
213 
214             // Check forward reference
215             if (!ctx.canReach(env, field)) {
216                 env.error(where, "forward.ref",
217                           id, field.getClassDeclaration());
218                 return false;
219             }
220             return true;
221         } catch (ClassNotFound e) {
222             env.error(where, "class.not.found", e.name, ctx.field);
223         } catch (AmbiguousMember e) {
224             env.error(where, "ambig.field", id,
225                       e.field1.getClassDeclaration(),
226                       e.field2.getClassDeclaration());
227         }
228         return false;
229     }
230 
231     /**
232      * Check expression
233      */
checkValue(Environment env, Context ctx, Vset vset, Hashtable<Object, Object> exp)234     public Vset checkValue(Environment env, Context ctx, Vset vset, Hashtable<Object, Object> exp) {
235         if (field != null) {
236             // An internally pre-set field, such as an argument copying
237             // an uplevel value.  Do not re-check it.
238             return vset;
239         }
240         if (bind(env, ctx)) {
241             vset = get(env, ctx, vset);
242             ctx.field.getClassDefinition().addDependency(field.getClassDeclaration());
243             if (implementation != null)
244                 vset = implementation.checkValue(env, ctx, vset, exp);
245         }
246         return vset;
247     }
248 
249     /**
250      * Check the expression if it appears on the LHS of an assignment
251      */
checkLHS(Environment env, Context ctx, Vset vset, Hashtable<Object, Object> exp)252     public Vset checkLHS(Environment env, Context ctx,
253                          Vset vset, Hashtable<Object, Object> exp) {
254         if (!bind(env, ctx))
255             return vset;
256         vset = assign(env, ctx, vset);
257         if (implementation != null)
258             vset = implementation.checkValue(env, ctx, vset, exp);
259         return vset;
260     }
261 
262     /**
263      * Check the expression if it appears on the LHS of an op= expression
264      */
checkAssignOp(Environment env, Context ctx, Vset vset, Hashtable<Object, Object> exp, Expression outside)265     public Vset checkAssignOp(Environment env, Context ctx,
266                               Vset vset, Hashtable<Object, Object> exp, Expression outside) {
267         if (!bind(env, ctx))
268             return vset;
269         vset = assign(env, ctx, get(env, ctx, vset));
270         if (implementation != null)
271             vset = implementation.checkValue(env, ctx, vset, exp);
272         return vset;
273     }
274 
275     /**
276      * Return an accessor if one is needed for assignments to this expression.
277      */
getAssigner(Environment env, Context ctx)278     public FieldUpdater getAssigner(Environment env, Context ctx) {
279         if (implementation != null)
280             return implementation.getAssigner(env, ctx);
281         return null;
282     }
283 
284     /**
285      * Return an updater if one is needed for assignments to this expression.
286      */
getUpdater(Environment env, Context ctx)287     public FieldUpdater getUpdater(Environment env, Context ctx) {
288         if (implementation != null)
289             return implementation.getUpdater(env, ctx);
290         return null;
291     }
292 
293     /**
294      * Check if the present name is part of a scoping prefix.
295      */
checkAmbigName(Environment env, Context ctx, Vset vset, Hashtable<Object, Object> exp, UnaryExpression loc)296     public Vset checkAmbigName(Environment env, Context ctx, Vset vset, Hashtable<Object, Object> exp,
297                                UnaryExpression loc) {
298         try {
299             if (ctx.getField(env, id) != null) {
300                 // if this is a local field, there's nothing more to do.
301                 return checkValue(env, ctx, vset, exp);
302             }
303         } catch (ClassNotFound ee) {
304         } catch (AmbiguousMember ee) {
305         }
306         // Can this be interpreted as a type?
307         ClassDefinition c = toResolvedType(env, ctx, true);
308         // Is it a real type??
309         if (c != null) {
310             loc.right = new TypeExpression(where, c.getType());
311             return vset;
312         }
313         // We hope it is a package prefix.  Let the caller decide.
314         type = Type.tPackage;
315         return vset;
316     }
317 
318     /**
319      * Convert an identifier to a known type, or null.
320      */
toResolvedType(Environment env, Context ctx, boolean pkgOK)321     private ClassDefinition toResolvedType(Environment env, Context ctx,
322                                            boolean pkgOK) {
323         Identifier rid = ctx.resolveName(env, id);
324         Type t = Type.tClass(rid);
325         if (pkgOK && !env.classExists(t)) {
326             return null;
327         }
328         if (env.resolve(where, ctx.field.getClassDefinition(), t)) {
329             try {
330                 ClassDefinition c = env.getClassDefinition(t);
331 
332                 // Maybe an inherited class hides an apparent class.
333                 if (c.isMember()) {
334                     ClassDefinition sc = ctx.findScope(env, c.getOuterClass());
335                     if (sc != c.getOuterClass()) {
336                         Identifier rid2 = ctx.getApparentClassName(env, id);
337                         if (!rid2.equals(idNull) && !rid2.equals(rid)) {
338                             env.error(where, "inherited.hides.type",
339                                       id, sc.getClassDeclaration());
340                         }
341                     }
342                 }
343 
344                 if (!c.getLocalName().equals(id.getFlatName().getName())) {
345                     env.error(where, "illegal.mangled.name", id, c);
346                 }
347 
348                 return c;
349             } catch (ClassNotFound ee) {
350             }
351         }
352         return null;
353     }
354 
355     /**
356      * Convert an identifier to a type.
357      * If one is not known, use the current package as a qualifier.
358      */
toType(Environment env, Context ctx)359     Type toType(Environment env, Context ctx) {
360         ClassDefinition c = toResolvedType(env, ctx, false);
361         if (c != null) {
362             return c.getType();
363         }
364         return Type.tError;
365     }
366 
367     /**
368      * Convert an expresion to a type in a context where a qualified
369      * type name is expected, e.g., in the prefix of a qualified type
370      * name. We do not necessarily know where the package prefix ends,
371      * so we operate similarly to 'checkAmbiguousName'.  This is the
372      * base case -- the first component of the qualified name.
373      */
374     /*-------------------------------------------------------*
375     Type toQualifiedType(Environment env, Context ctx) {
376         // We do not look for non-type fields.  Is this correct?
377         ClassDefinition c = toResolvedType(env, ctx, true);
378         // Is it a real type?
379         if (c != null) {
380             return c.getType();
381         }
382         // We hope it is a package prefix.  Let the caller decide.
383         return Type.tPackage;
384     }
385     *-------------------------------------------------------*/
386 
387     /**
388      * Check if constant:  Will it inline away?
389      */
isConstant()390     public boolean isConstant() {
391         if (implementation != null)
392             return implementation.isConstant();
393         if (field != null) {
394             return field.isConstant();
395         }
396         return false;
397     }
398 
399     /**
400      * Inline
401      */
inline(Environment env, Context ctx)402     public Expression inline(Environment env, Context ctx) {
403         return null;
404     }
inlineValue(Environment env, Context ctx)405     public Expression inlineValue(Environment env, Context ctx) {
406         if (implementation != null)
407             return implementation.inlineValue(env, ctx);
408         if (field == null) {
409             return this;
410         }
411         try {
412             if (field.isLocal()) {
413                 if (field.isInlineable(env, false)) {
414                     Expression e = (Expression)field.getValue(env);
415                     return (e == null) ? this : e.inlineValue(env, ctx);
416                 }
417                 return this;
418             }
419             return this;
420         } catch (ClassNotFound e) {
421             throw new CompilerError(e);
422         }
423     }
inlineLHS(Environment env, Context ctx)424     public Expression inlineLHS(Environment env, Context ctx) {
425         if (implementation != null)
426             return implementation.inlineLHS(env, ctx);
427         return this;
428     }
429 
copyInline(Context ctx)430     public Expression copyInline(Context ctx) {
431         if (implementation != null)
432             return implementation.copyInline(ctx);
433         IdentifierExpression e =
434             (IdentifierExpression)super.copyInline(ctx);
435         if (field != null && field.isLocal()) {
436             e.field = ((LocalMember)field).getCurrentInlineCopy(ctx);
437         }
438         return e;
439     }
440 
costInline(int thresh, Environment env, Context ctx)441     public int costInline(int thresh, Environment env, Context ctx) {
442         if (implementation != null)
443             return implementation.costInline(thresh, env, ctx);
444         return super.costInline(thresh, env, ctx);
445     }
446 
447     /**
448      * Code local vars (object fields have been inlined away)
449      */
codeLValue(Environment env, Context ctx, Assembler asm)450     int codeLValue(Environment env, Context ctx, Assembler asm) {
451         return 0;
452     }
codeLoad(Environment env, Context ctx, Assembler asm)453     void codeLoad(Environment env, Context ctx, Assembler asm) {
454         asm.add(where, opc_iload + type.getTypeCodeOffset(),
455                 ((LocalMember)field).number);
456     }
codeStore(Environment env, Context ctx, Assembler asm)457     void codeStore(Environment env, Context ctx, Assembler asm) {
458         LocalMember local = (LocalMember)field;
459         asm.add(where, opc_istore + type.getTypeCodeOffset(),
460                 new LocalVariable(local, local.number));
461     }
codeValue(Environment env, Context ctx, Assembler asm)462     public void codeValue(Environment env, Context ctx, Assembler asm) {
463         codeLValue(env, ctx, asm);
464         codeLoad(env, ctx, asm);
465     }
466 
467     /**
468      * Print
469      */
print(PrintStream out)470     public void print(PrintStream out) {
471         out.print(id + "#" + ((field != null) ? field.hashCode() : 0));
472         if (implementation != null) {
473             out.print("/IMPL=");
474             implementation.print(out);
475         }
476     }
477 }
478