View Javadoc
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  
26  package jdk.nashorn.internal.ir;
27  
28  import java.io.PrintWriter;
29  import java.util.HashSet;
30  import java.util.Set;
31  import java.util.StringTokenizer;
32  import jdk.nashorn.internal.codegen.types.Range;
33  import jdk.nashorn.internal.codegen.types.Type;
34  import jdk.nashorn.internal.runtime.Context;
35  import jdk.nashorn.internal.runtime.Debug;
36  import jdk.nashorn.internal.runtime.options.Options;
37  
38  import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN;
39  
40  /**
41   * Maps a name to specific data.
42   */
43  
44  public final class Symbol implements Comparable<Symbol> {
45      /** Symbol kinds. Kind ordered by precedence. */
46      public static final int IS_TEMP     = 1;
47      /** Is this Global */
48      public static final int IS_GLOBAL   = 2;
49      /** Is this a variable */
50      public static final int IS_VAR      = 3;
51      /** Is this a parameter */
52      public static final int IS_PARAM    = 4;
53      /** Is this a constant */
54      public static final int IS_CONSTANT = 5;
55      /** Mask for kind flags */
56      public static final int KINDMASK = (1 << 3) - 1; // Kinds are represented by lower three bits
57  
58      /** Is this scope */
59      public static final int IS_SCOPE             = 1 <<  4;
60      /** Is this a this symbol */
61      public static final int IS_THIS              = 1 <<  5;
62      /** Can this symbol ever be undefined */
63      public static final int CAN_BE_UNDEFINED     = 1 <<  6;
64      /** Is this symbol always defined? */
65      public static final int IS_ALWAYS_DEFINED    = 1 <<  8;
66      /** Can this symbol ever have primitive types */
67      public static final int CAN_BE_PRIMITIVE     = 1 <<  9;
68      /** Is this a let */
69      public static final int IS_LET               = 1 << 10;
70      /** Is this an internal symbol, never represented explicitly in source code */
71      public static final int IS_INTERNAL          = 1 << 11;
72      /** Is this a function self-reference symbol */
73      public static final int IS_FUNCTION_SELF     = 1 << 12;
74      /** Is this a specialized param? */
75      public static final int IS_SPECIALIZED_PARAM = 1 << 13;
76      /** Is this symbol a shared temporary? */
77      public static final int IS_SHARED            = 1 << 14;
78      /** Is this a function declaration? */
79      public static final int IS_FUNCTION_DECLARATION = 1 << 15;
80  
81      /** Null or name identifying symbol. */
82      private final String name;
83  
84      /** Symbol flags. */
85      private int flags;
86  
87      /** Type of symbol. */
88      private Type type;
89  
90      /** Local variable slot. -1 indicates external property. */
91      private int slot;
92  
93      /** Field number in scope or property; array index in varargs when not using arguments object. */
94      private int fieldIndex;
95  
96      /** Number of times this symbol is used in code */
97      private int useCount;
98  
99      /** Range for symbol */
100     private Range range;
101 
102     /** Debugging option - dump info and stack trace when symbols with given names are manipulated */
103     private static final Set<String> TRACE_SYMBOLS;
104     private static final Set<String> TRACE_SYMBOLS_STACKTRACE;
105 
106     static {
107         final String stacktrace = Options.getStringProperty("nashorn.compiler.symbol.stacktrace", null);
108         final String trace;
109         if (stacktrace != null) {
110             trace = stacktrace; //stacktrace always implies trace as well
111             TRACE_SYMBOLS_STACKTRACE = new HashSet<>();
112             for (StringTokenizer st = new StringTokenizer(stacktrace, ","); st.hasMoreTokens(); ) {
113                 TRACE_SYMBOLS_STACKTRACE.add(st.nextToken());
114             }
115         } else {
116             trace = Options.getStringProperty("nashorn.compiler.symbol.trace", null);
117             TRACE_SYMBOLS_STACKTRACE = null;
118         }
119 
120         if (trace != null) {
121             TRACE_SYMBOLS = new HashSet<>();
122             for (StringTokenizer st = new StringTokenizer(trace, ","); st.hasMoreTokens(); ) {
123                 TRACE_SYMBOLS.add(st.nextToken());
124             }
125         } else {
126             TRACE_SYMBOLS = null;
127         }
128     }
129 
130     /**
131      * Constructor
132      *
133      * @param name  name of symbol
134      * @param flags symbol flags
135      * @param type  type of this symbol
136      * @param slot  bytecode slot for this symbol
137      */
138     protected Symbol(final String name, final int flags, final Type type, final int slot) {
139         this.name       = name;
140         this.flags      = flags;
141         this.type       = type;
142         this.slot       = slot;
143         this.fieldIndex = -1;
144         this.range      = Range.createUnknownRange();
145         trace("CREATE SYMBOL");
146     }
147 
148     /**
149      * Constructor
150      *
151      * @param name  name of symbol
152      * @param flags symbol flags
153      */
154     public Symbol(final String name, final int flags) {
155         this(name, flags, Type.UNKNOWN, -1);
156     }
157 
158     /**
159      * Constructor
160      *
161      * @param name  name of symbol
162      * @param flags symbol flags
163      * @param type  type of this symbol
164      */
165     public Symbol(final String name, final int flags, final Type type) {
166         this(name, flags, type, -1);
167     }
168 
169     private Symbol(final Symbol base, final String name, final int flags) {
170         this.flags = flags;
171         this.name  = name;
172 
173         this.fieldIndex = base.fieldIndex;
174         this.slot       = base.slot;
175         this.type       = base.type;
176         this.useCount   = base.useCount;
177         this.range      = base.range;
178     }
179 
180     private static String align(final String string, final int max) {
181         final StringBuilder sb = new StringBuilder();
182         sb.append(string.substring(0, Math.min(string.length(), max)));
183 
184         while (sb.length() < max) {
185             sb.append(' ');
186         }
187         return sb.toString();
188     }
189 
190     /**
191      * Return the type for this symbol. Normally, if there is no type override,
192      * this is where any type for any node is stored. If the node has a TypeOverride,
193      * it may override this, e.g. when asking for a scoped field as a double
194      *
195      * @return symbol type
196      */
197     public final Type getSymbolType() {
198         return type;
199     }
200 
201     /**
202      * Debugging .
203      *
204      * @param stream Stream to print to.
205      */
206 
207     void print(final PrintWriter stream) {
208         final String printName = align(name, 20);
209         final String printType = align(type.toString(), 10);
210         final String printSlot = align(slot == -1 ? "none" : "" + slot, 10);
211         String printFlags = "";
212 
213         switch (flags & KINDMASK) {
214         case IS_TEMP:
215             printFlags = "temp " + printFlags;
216             break;
217         case IS_GLOBAL:
218             printFlags = "global " + printFlags;
219             break;
220         case IS_VAR:
221             printFlags = "var " + printFlags;
222             break;
223         case IS_PARAM:
224             printFlags = "param " + printFlags;
225             break;
226         case IS_CONSTANT:
227             printFlags = "CONSTANT " + printFlags;
228             break;
229         default:
230             break;
231         }
232 
233         if (isScope()) {
234             printFlags += "scope ";
235         }
236 
237         if (isInternal()) {
238             printFlags += "internal ";
239         }
240 
241         if (isLet()) {
242             printFlags += "let ";
243         }
244 
245         if (isThis()) {
246             printFlags += "this ";
247         }
248 
249         if (!canBeUndefined()) {
250             printFlags += "always_def ";
251         }
252 
253         if (canBePrimitive()) {
254             printFlags += "can_be_prim ";
255         }
256 
257         stream.print(printName + ": " + printType + ", " + printSlot + ", " + printFlags);
258         stream.println();
259     }
260 
261     /**
262      * Compare the the symbol kind with another.
263      *
264      * @param other Other symbol's flags.
265      * @return True if symbol has less kind.
266      */
267     public boolean less(final int other) {
268         return (flags & KINDMASK) < (other & KINDMASK);
269     }
270 
271     /**
272      * Allocate a slot for this symbol.
273      *
274      * @param needsSlot True if symbol needs a slot.
275      */
276     public void setNeedsSlot(final boolean needsSlot) {
277         setSlot(needsSlot ? 0 : -1);
278     }
279 
280     /**
281      * Return the number of slots required for the symbol.
282      *
283      * @return Number of slots.
284      */
285     public int slotCount() {
286         return type.isCategory2() ? 2 : 1;
287     }
288 
289     @Override
290     public String toString() {
291         final StringBuilder sb = new StringBuilder();
292 
293         sb.append(name).
294             append(' ').
295             append('(').
296             append(getSymbolType().getTypeClass().getSimpleName()).
297             append(')');
298 
299         if (hasSlot()) {
300             sb.append(' ').
301                 append('(').
302                 append("slot=").
303                 append(slot).
304                 append(')');
305         }
306 
307         if (isScope()) {
308             if(isGlobal()) {
309                 sb.append(" G");
310             } else {
311                 sb.append(" S");
312             }
313         }
314 
315         if (canBePrimitive()) {
316             sb.append(" P?");
317         }
318 
319         return sb.toString();
320     }
321 
322     @Override
323     public int compareTo(final Symbol other) {
324         return name.compareTo(other.name);
325     }
326 
327     /**
328      * Does this symbol have an allocated bytecode slot. If not, it is scope
329      * and must be loaded from memory upon access
330      *
331      * @return true if this symbol has a local bytecode slot
332      */
333     public boolean hasSlot() {
334         return slot >= 0;
335     }
336 
337     /**
338      * Check if this is a temporary symbol
339      * @return true if temporary
340      */
341     public boolean isTemp() {
342         return (flags & KINDMASK) == IS_TEMP;
343     }
344 
345     /**
346      * Check if this is a symbol in scope. Scope symbols cannot, for obvious reasons
347      * be stored in byte code slots on the local frame
348      *
349      * @return true if this is scoped
350      */
351     public boolean isScope() {
352         assert ((flags & KINDMASK) != IS_GLOBAL) || ((flags & IS_SCOPE) == IS_SCOPE) : "global without scope flag";
353         return (flags & IS_SCOPE) == IS_SCOPE;
354     }
355 
356     /**
357      * Returns true if this symbol is a temporary that is being shared across expressions.
358      * @return true if this symbol is a temporary that is being shared across expressions.
359      */
360     public boolean isShared() {
361         return (flags & IS_SHARED) == IS_SHARED;
362     }
363 
364     /**
365      * Check if this symbol is a function declaration
366      * @return true if a function declaration
367      */
368     public boolean isFunctionDeclaration() {
369         return (flags & IS_FUNCTION_DECLARATION) == IS_FUNCTION_DECLARATION;
370     }
371 
372     /**
373      * Creates an unshared copy of a symbol. The symbol must be currently shared.
374      * @param newName the name for the new symbol.
375      * @return a new, unshared symbol.
376      */
377     public Symbol createUnshared(final String newName) {
378         assert isShared();
379         return new Symbol(this, newName, flags & ~IS_SHARED);
380     }
381 
382     /**
383      * Flag this symbol as scope as described in {@link Symbol#isScope()}
384      */
385     /**
386      * Flag this symbol as scope as described in {@link Symbol#isScope()}
387      */
388      public void setIsScope() {
389         if (!isScope()) {
390             trace("SET IS SCOPE");
391             assert !isShared();
392             flags |= IS_SCOPE;
393         }
394     }
395 
396      /**
397       * Mark this symbol as one being shared by multiple expressions. The symbol must be a temporary.
398       */
399      public void setIsShared() {
400          if (!isShared()) {
401              assert isTemp();
402              trace("SET IS SHARED");
403              flags |= IS_SHARED;
404          }
405      }
406 
407 
408     /**
409      * Mark this symbol as a function declaration.
410      */
411     public void setIsFunctionDeclaration() {
412         if (!isFunctionDeclaration()) {
413             trace("SET IS FUNCTION DECLARATION");
414             flags |= IS_FUNCTION_DECLARATION;
415         }
416     }
417 
418     /**
419      * Check if this symbol is a variable
420      * @return true if variable
421      */
422     public boolean isVar() {
423         return (flags & KINDMASK) == IS_VAR;
424     }
425 
426     /**
427      * Check if this symbol is a global (undeclared) variable
428      * @return true if global
429      */
430     public boolean isGlobal() {
431         return (flags & KINDMASK) == IS_GLOBAL;
432     }
433 
434     /**
435      * Check if this symbol is a function parameter
436      * @return true if parameter
437      */
438     public boolean isParam() {
439         return (flags & KINDMASK) == IS_PARAM;
440     }
441 
442     /**
443      * Check if this symbol is always defined, which overrides all canBeUndefined tags
444      * @return true if always defined
445      */
446     public boolean isAlwaysDefined() {
447         return isParam() || (flags & IS_ALWAYS_DEFINED) == IS_ALWAYS_DEFINED;
448     }
449 
450     /**
451      * Get the range for this symbol
452      * @return range for symbol
453      */
454     public Range getRange() {
455         return range;
456     }
457 
458     /**
459      * Set the range for this symbol
460      * @param range range
461      */
462     public void setRange(final Range range) {
463         this.range = range;
464     }
465 
466     /**
467      * Check if this symbol represents a return value with a known non-generic type.
468      * @return true if specialized return value
469      */
470     public boolean isNonGenericReturn() {
471         return getName().equals(RETURN.symbolName()) && type != Type.OBJECT;
472     }
473 
474     /**
475      * Check if this symbol is a function parameter of known
476      * narrowest type
477      * @return true if parameter
478      */
479     public boolean isSpecializedParam() {
480         return (flags & IS_SPECIALIZED_PARAM) == IS_SPECIALIZED_PARAM;
481     }
482 
483     /**
484      * Check whether this symbol ever has primitive assignments. Conservative
485      * @return true if primitive assignments exist
486      */
487     public boolean canBePrimitive() {
488         return (flags & CAN_BE_PRIMITIVE) == CAN_BE_PRIMITIVE;
489     }
490 
491     /**
492      * Check if this symbol can ever be undefined
493      * @return true if can be undefined
494      */
495     public boolean canBeUndefined() {
496         return (flags & CAN_BE_UNDEFINED) == CAN_BE_UNDEFINED;
497     }
498 
499     /**
500      * Flag this symbol as potentially undefined in parts of the program
501      */
502     public void setCanBeUndefined() {
503         assert type.isObject() : type;
504         if (isAlwaysDefined()) {
505             return;
506         } else if (!canBeUndefined()) {
507             assert !isShared();
508             flags |= CAN_BE_UNDEFINED;
509         }
510     }
511 
512     /**
513      * Flag this symbol as potentially primitive
514      * @param type the primitive type it occurs with, currently unused but can be used for width guesses
515      */
516     public void setCanBePrimitive(final Type type) {
517         if(!canBePrimitive()) {
518             assert !isShared();
519             flags |= CAN_BE_PRIMITIVE;
520         }
521     }
522 
523     /**
524      * Check if this symbol is a constant
525      * @return true if a constant
526      */
527     public boolean isConstant() {
528         return (flags & KINDMASK) == IS_CONSTANT;
529     }
530 
531     /**
532      * Check if this is an internal symbol, without an explicit JavaScript source
533      * code equivalent
534      * @return true if internal
535      */
536     public boolean isInternal() {
537         return (flags & IS_INTERNAL) != 0;
538     }
539 
540     /**
541      * Check if this symbol represents {@code this}
542      * @return true if this
543      */
544     public boolean isThis() {
545         return (flags & IS_THIS) != 0;
546     }
547 
548     /**
549      * Check if this symbol is a let
550      * @return true if let
551      */
552     public boolean isLet() {
553         return (flags & IS_LET) == IS_LET;
554     }
555 
556     /**
557      * Flag this symbol as a let
558      */
559     public void setIsLet() {
560         if(!isLet()) {
561             assert !isShared();
562             flags |= IS_LET;
563         }
564     }
565 
566     /**
567      * Flag this symbol as a function's self-referencing symbol.
568      * @return true if this symbol as a function's self-referencing symbol.
569      */
570     public boolean isFunctionSelf() {
571         return (flags & IS_FUNCTION_SELF) == IS_FUNCTION_SELF;
572     }
573 
574     /**
575      * Get the index of the field used to store this symbol, should it be an AccessorProperty
576      * and get allocated in a JO-prefixed ScriptObject subclass.
577      *
578      * @return field index
579      */
580     public int getFieldIndex() {
581         assert fieldIndex != -1 : "fieldIndex must be initialized " + fieldIndex;
582         return fieldIndex;
583     }
584 
585     /**
586      * Set the index of the field used to store this symbol, should it be an AccessorProperty
587      * and get allocated in a JO-prefixed ScriptObject subclass.
588      *
589      * @param fieldIndex field index - a positive integer
590      */
591     public void setFieldIndex(final int fieldIndex) {
592         if(this.fieldIndex != fieldIndex) {
593             assert !isShared();
594             this.fieldIndex = fieldIndex;
595         }
596     }
597 
598     /**
599      * Get the symbol flags
600      * @return flags
601      */
602     public int getFlags() {
603         return flags;
604     }
605 
606     /**
607      * Set the symbol flags
608      * @param flags flags
609      */
610     public void setFlags(final int flags) {
611         if(this.flags != flags) {
612             assert !isShared();
613             this.flags = flags;
614         }
615     }
616 
617     /**
618      * Get the name of this symbol
619      * @return symbol name
620      */
621     public String getName() {
622         return name;
623     }
624 
625     /**
626      * Get the byte code slot for this symbol
627      * @return byte code slot, or -1 if no slot allocated/possible
628      */
629     public int getSlot() {
630         return slot;
631     }
632 
633     /**
634      * Increase the symbol's use count by one.
635      */
636     public void increaseUseCount() {
637         useCount++;
638     }
639 
640     /**
641      * Get the symbol's use count
642      * @return the number of times the symbol is used in code.
643      */
644     public int getUseCount() {
645         return useCount;
646     }
647 
648     /**
649      * Set the bytecode slot for this symbol
650      * @param slot valid bytecode slot, or -1 if not available
651      */
652     public void setSlot(final int slot) {
653         if (slot != this.slot) {
654             assert !isShared();
655             trace("SET SLOT " + slot);
656             this.slot = slot;
657         }
658     }
659 
660     /**
661      * Assign a specific subclass of Object to the symbol
662      *
663      * @param type  the type
664      */
665     public void setType(final Class<?> type) {
666         assert !type.isPrimitive() && !Number.class.isAssignableFrom(type) : "Class<?> types can only be subclasses of object";
667         setType(Type.typeFor(type));
668     }
669 
670     /**
671      * Assign a type to the symbol
672      *
673      * @param type the type
674      */
675     public void setType(final Type type) {
676         setTypeOverride(Type.widest(this.type, type));
677     }
678 
679     /**
680      * Returns true if calling {@link #setType(Type)} on this symbol would effectively change its type.
681      * @param newType the new type to test for
682      * @return true if setting this symbols type to a new value would effectively change its type.
683      */
684     public boolean wouldChangeType(final Type newType) {
685         return Type.widest(this.type, newType) != this.type;
686     }
687 
688     /**
689      * Only use this if you know about an existing type
690      * constraint - otherwise a type can only be
691      * widened
692      *
693      * @param type  the type
694      */
695     public void setTypeOverride(final Type type) {
696         final Type old = this.type;
697         if (old != type) {
698             assert !isShared();
699             trace("TYPE CHANGE: " + old + "=>" + type + " == " + type);
700             this.type = type;
701         }
702     }
703 
704     /**
705      * Sets the type of the symbol to the specified type. If the type would be changed, but this symbol is a shared
706      * temporary, it will instead return a different temporary symbol of the requested type from the passed temporary
707      * symbols. That way, it never mutates the type of a shared temporary.
708      * @param type the new type for the symbol
709      * @param ts a holder of temporary symbols
710      * @return either this symbol, or a different symbol if this symbol is a shared temporary and it type would have to
711      * be changed.
712      */
713     public Symbol setTypeOverrideShared(final Type type, final TemporarySymbols ts) {
714         if(getSymbolType() != type) {
715             if(isShared()) {
716                 assert !hasSlot();
717                 return ts.getTypedTemporarySymbol(type);
718             }
719             setTypeOverride(type);
720         }
721         return this;
722     }
723 
724     /**
725      * From a lexical context, set this symbol as needing scope, which
726      * will set flags for the defining block that will be written when
727      * block is popped from the lexical context stack, used by codegen
728      * when flags need to be tagged, but block is in the
729      * middle of evaluation and cannot be modified.
730      *
731      * @param lc     lexical context
732      * @param symbol symbol
733      */
734     public static void setSymbolIsScope(final LexicalContext lc, final Symbol symbol) {
735         symbol.setIsScope();
736         if (!symbol.isGlobal()) {
737             lc.setBlockNeedsScope(lc.getDefiningBlock(symbol));
738         }
739     }
740 
741     private void trace(final String desc) {
742         if (TRACE_SYMBOLS != null && (TRACE_SYMBOLS.isEmpty() || TRACE_SYMBOLS.contains(name))) {
743             Context.err(Debug.id(this) + " SYMBOL: '" + name + "' " + desc);
744             if (TRACE_SYMBOLS_STACKTRACE != null && (TRACE_SYMBOLS_STACKTRACE.isEmpty() || TRACE_SYMBOLS_STACKTRACE.contains(name))) {
745                 new Throwable().printStackTrace(Context.getCurrentErr());
746             }
747         }
748     }
749 }