IdentNode.java revision 1068:34ef988d5959
1/*
2 * Copyright (c) 2010, 2013, 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 jdk.nashorn.internal.ir;
27
28import static jdk.nashorn.internal.codegen.CompilerConstants.__DIR__;
29import static jdk.nashorn.internal.codegen.CompilerConstants.__FILE__;
30import static jdk.nashorn.internal.codegen.CompilerConstants.__LINE__;
31import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
32
33import java.util.function.Function;
34import jdk.nashorn.internal.codegen.types.Type;
35import jdk.nashorn.internal.ir.annotations.Immutable;
36import jdk.nashorn.internal.ir.visitor.NodeVisitor;
37import jdk.nashorn.internal.parser.Token;
38import jdk.nashorn.internal.parser.TokenType;
39
40/**
41 * IR representation for an identifier.
42 */
43@Immutable
44public final class IdentNode extends Expression implements PropertyKey, FunctionCall, Optimistic, JoinPredecessor {
45    private static final long serialVersionUID = 1L;
46
47    private static final int PROPERTY_NAME     = 1 << 0;
48    private static final int INITIALIZED_HERE  = 1 << 1;
49    private static final int FUNCTION          = 1 << 2;
50    private static final int FUTURESTRICT_NAME = 1 << 3;
51    private static final int IS_DECLARED_HERE  = 1 << 4;
52    private static final int IS_DEAD           = 1 << 5;
53
54    /** Identifier. */
55    private final String name;
56
57    /** Optimistic type */
58    private final Type type;
59
60    private final int flags;
61
62    private final int programPoint;
63
64    private final LocalVariableConversion conversion;
65
66    private Symbol symbol;
67
68
69    /**
70     * Constructor
71     *
72     * @param token   token
73     * @param finish  finish position
74     * @param name    name of identifier
75     */
76    public IdentNode(final long token, final int finish, final String name) {
77        super(token, finish);
78        this.name = name;
79        this.type = null;
80        this.flags = 0;
81        this.programPoint = INVALID_PROGRAM_POINT;
82        this.conversion = null;
83    }
84
85    private IdentNode(final IdentNode identNode, final String name, final Type type, final int flags, final int programPoint, final LocalVariableConversion conversion) {
86        super(identNode);
87        this.name = name;
88        this.type = type;
89        this.flags = flags;
90        this.programPoint = programPoint;
91        this.conversion = conversion;
92        this.symbol = identNode.symbol;
93    }
94
95    /**
96     * Copy constructor - create a new IdentNode for the same location
97     *
98     * @param identNode  identNode
99     */
100    public IdentNode(final IdentNode identNode) {
101        super(identNode);
102        this.name = identNode.getName();
103        this.type = identNode.type;
104        this.flags = identNode.flags;
105        this.conversion = identNode.conversion;
106        this.programPoint = INVALID_PROGRAM_POINT;
107        this.symbol = identNode.symbol;
108    }
109
110    /**
111     * Creates an identifier for the symbol. Normally used by code generator for creating temporary storage identifiers
112     * that must contain both a symbol and a type.
113     * @param symbol the symbol to create a temporary identifier for.
114     * @return a temporary identifier for the symbol.
115     */
116    public static IdentNode createInternalIdentifier(final Symbol symbol) {
117        return new IdentNode(Token.toDesc(TokenType.IDENT, 0, 0), 0, symbol.getName()).setSymbol(symbol);
118    }
119
120    @Override
121    public Type getType(final Function<Symbol, Type> localVariableTypes) {
122        if(type != null) {
123            return type;
124        } else if(symbol != null && symbol.isScope()) {
125            return Type.OBJECT;
126        }
127        final Type symbolType = localVariableTypes.apply(symbol);
128        return symbolType == null ? Type.UNDEFINED : symbolType;
129    }
130
131    /**
132     * Assist in IR navigation.
133     *
134     * @param visitor IR navigating visitor.
135     */
136    @Override
137    public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
138        if (visitor.enterIdentNode(this)) {
139            return visitor.leaveIdentNode(this);
140        }
141
142        return this;
143    }
144
145    @Override
146    public void toString(final StringBuilder sb, final boolean printType) {
147        if (printType) {
148            optimisticTypeToString(sb, symbol == null || !symbol.hasSlot());
149        }
150        sb.append(name);
151    }
152
153    /**
154     * Get the name of the identifier
155     * @return  IdentNode name
156     */
157    public String getName() {
158        return name;
159    }
160
161    @Override
162    public String getPropertyName() {
163        return getName();
164    }
165
166    @Override
167    public boolean isLocal() {
168        return !getSymbol().isScope();
169    }
170
171    /**
172     * Return the Symbol the compiler has assigned to this identifier. The symbol is a description of the storage
173     * location for the identifier.
174     *
175     * @return the symbol
176     */
177    public Symbol getSymbol() {
178        return symbol;
179    }
180
181    /**
182     * Assign a symbol to this identifier. See {@link IdentNode#getSymbol()} for explanation of what a symbol is.
183     *
184     * @param symbol the symbol
185     * @return new node
186     */
187    public IdentNode setSymbol(final Symbol symbol) {
188        if (this.symbol == symbol) {
189            return this;
190        }
191        final IdentNode newIdent = (IdentNode)clone();
192        newIdent.symbol = symbol;
193        return newIdent;
194    }
195
196    /**
197     * Check if this IdentNode is a property name
198     * @return true if this is a property name
199     */
200    public boolean isPropertyName() {
201        return (flags & PROPERTY_NAME) == PROPERTY_NAME;
202    }
203
204    /**
205     * Flag this IdentNode as a property name
206     * @return a node equivalent to this one except for the requested change.
207     */
208    public IdentNode setIsPropertyName() {
209        if (isPropertyName()) {
210            return this;
211        }
212        return new IdentNode(this, name, type, flags | PROPERTY_NAME, programPoint, conversion);
213    }
214
215    /**
216     * Check if this IdentNode is a future strict name
217     * @return true if this is a future strict name
218     */
219    public boolean isFutureStrictName() {
220        return (flags & FUTURESTRICT_NAME) == FUTURESTRICT_NAME;
221    }
222
223    /**
224     * Flag this IdentNode as a future strict name
225     * @return a node equivalent to this one except for the requested change.
226     */
227    public IdentNode setIsFutureStrictName() {
228        if (isFutureStrictName()) {
229            return this;
230        }
231        return new IdentNode(this, name, type, flags | FUTURESTRICT_NAME, programPoint, conversion);
232    }
233
234    /**
235     * Helper function for local def analysis.
236     * @return true if IdentNode is initialized on creation
237     */
238    public boolean isInitializedHere() {
239        return (flags & INITIALIZED_HERE) == INITIALIZED_HERE;
240    }
241
242    /**
243     * Flag IdentNode to be initialized on creation
244     * @return a node equivalent to this one except for the requested change.
245     */
246    public IdentNode setIsInitializedHere() {
247        if (isInitializedHere()) {
248            return this;
249        }
250        return new IdentNode(this, name, type, flags | INITIALIZED_HERE, programPoint, conversion);
251    }
252
253    /**
254     * Is this a LET or CONST identifier used before its declaration?
255     *
256     * @return true if identifier is dead
257     */
258    public boolean isDead() {
259        return (flags & IS_DEAD) != 0;
260    }
261
262    /**
263     * Flag this IdentNode as a LET or CONST identifier used before its declaration.
264     *
265     * @return a new IdentNode equivalent to this but marked as dead.
266     */
267    public IdentNode markDead() {
268        return new IdentNode(this, name, type, flags | IS_DEAD, programPoint, conversion);
269    }
270
271    /**
272     * Is this IdentNode declared here?
273     *
274     * @return true if identifier is declared here
275     */
276    public boolean isDeclaredHere() {
277        return (flags & IS_DECLARED_HERE) != 0;
278    }
279
280    /**
281     * Flag this IdentNode as being declared here.
282     *
283     * @return a new IdentNode equivalent to this but marked as declared here.
284     */
285    public IdentNode setIsDeclaredHere() {
286        if (isDeclaredHere()) {
287            return this;
288        }
289        return new IdentNode(this, name, type, flags | IS_DECLARED_HERE, programPoint, conversion);
290    }
291
292    /**
293     * Check if the name of this IdentNode is same as that of a compile-time property (currently __DIR__, __FILE__, and
294     * __LINE__).
295     *
296     * @return true if this IdentNode's name is same as that of a compile-time property
297     */
298    public boolean isCompileTimePropertyName() {
299        return name.equals(__DIR__.symbolName()) || name.equals(__FILE__.symbolName()) || name.equals(__LINE__.symbolName());
300    }
301
302    @Override
303    public boolean isFunction() {
304        return (flags & FUNCTION) == FUNCTION;
305    }
306
307    @Override
308    public IdentNode setType(final Type type) {
309        if (this.type == type) {
310            return this;
311        }
312        return new IdentNode(this, name, type, flags, programPoint, conversion);
313    }
314
315    /**
316     * Mark this node as being the callee operand of a {@link CallNode}.
317     * @return an ident node identical to this one in all aspects except with its function flag set.
318     */
319    public IdentNode setIsFunction() {
320        if (isFunction()) {
321            return this;
322        }
323        return new IdentNode(this, name, type, flags | FUNCTION, programPoint, conversion);
324    }
325
326    /**
327     * Mark this node as not being the callee operand of a {@link CallNode}.
328     * @return an ident node identical to this one in all aspects except with its function flag unset.
329     */
330    public IdentNode setIsNotFunction() {
331        if (! isFunction()) {
332            return this;
333        }
334        return new IdentNode(this, name, type, flags & ~FUNCTION, programPoint, conversion);
335    }
336
337    @Override
338    public int getProgramPoint() {
339        return programPoint;
340    }
341
342    @Override
343    public Optimistic setProgramPoint(final int programPoint) {
344        if (this.programPoint == programPoint) {
345            return this;
346        }
347        return new IdentNode(this, name, type, flags, programPoint, conversion);
348    }
349
350    @Override
351    public Type getMostOptimisticType() {
352        return Type.INT;
353    }
354
355    @Override
356    public Type getMostPessimisticType() {
357        return Type.OBJECT;
358    }
359
360    @Override
361    public boolean canBeOptimistic() {
362        return true;
363    }
364
365    @Override
366    public JoinPredecessor setLocalVariableConversion(final LexicalContext lc, final LocalVariableConversion conversion) {
367        if(this.conversion == conversion) {
368            return this;
369        }
370        return new IdentNode(this, name, type, flags, programPoint, conversion);
371    }
372
373    /**
374     * Is this an internal symbol, i.e. one that starts with ':'. Those can
375     * never be optimistic.
376     * @return true if internal symbol
377     */
378    public boolean isInternal() {
379        assert name != null;
380        return name.charAt(0) == ':';
381    }
382
383    @Override
384    public LocalVariableConversion getLocalVariableConversion() {
385        return conversion;
386    }
387}
388