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
26package sun.tools.tree;
27
28import sun.tools.java.*;
29import sun.tools.asm.Assembler;
30import sun.tools.asm.LocalVariable;
31import java.io.PrintStream;
32import 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 */
39public
40class IdentifierExpression extends Expression {
41    Identifier id;
42    MemberDefinition field;
43    Expression implementation;
44
45    /**
46     * Constructor
47     */
48    public IdentifierExpression(long where, Identifier id) {
49        super(IDENT, where, Type.tError);
50        this.id = id;
51    }
52    public IdentifierExpression(IdentifierToken id) {
53        this(id.getWhere(), id.getName());
54    }
55    public IdentifierExpression(long where, MemberDefinition field) {
56        super(IDENT, where, field.getType());
57        this.id = field.getName();
58        this.field = field;
59    }
60
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
402    public Expression inline(Environment env, Context ctx) {
403        return null;
404    }
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    }
424    public Expression inlineLHS(Environment env, Context ctx) {
425        if (implementation != null)
426            return implementation.inlineLHS(env, ctx);
427        return this;
428    }
429
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
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     */
450    int codeLValue(Environment env, Context ctx, Assembler asm) {
451        return 0;
452    }
453    void codeLoad(Environment env, Context ctx, Assembler asm) {
454        asm.add(where, opc_iload + type.getTypeCodeOffset(),
455                ((LocalMember)field).number);
456    }
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    }
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     */
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