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.IOException;
  29 import java.io.ObjectInputStream;
  30 import java.io.PrintWriter;
  31 import java.io.Serializable;
  32 import java.util.HashSet;
  33 import java.util.Set;
  34 import java.util.StringTokenizer;
  35 import jdk.nashorn.internal.codegen.types.Type;
  36 import jdk.nashorn.internal.runtime.Context;
  37 import jdk.nashorn.internal.runtime.Debug;
  38 import jdk.nashorn.internal.runtime.options.Options;
  39 
  40 /**
  41  * Symbol is a symbolic address for a value ("variable" if you wish). Identifiers in JavaScript source, as well as
  42  * certain synthetic variables created by the compiler are represented by Symbol objects. Symbols can address either
  43  * local variable slots in bytecode ("slotted symbol"), or properties in scope objects ("scoped symbol"). A symbol can
  44  * also end up being defined but then not used during symbol assignment calculations; such symbol will be neither
  45  * scoped, nor slotted; it represents a dead variable (it might be written to, but is never read). Finally, a symbol can
  46  * be both slotted and in scope. This special case can only occur with bytecode method parameters. They all come in as
  47  * slotted, but if they are used by a nested function (or eval) then they will be copied into the scope object, and used
  48  * from there onwards. Two further special cases are parameters stored in {@code NativeArguments} objects and parameters
  49  * stored in {@code Object[]} parameter to variable-arity functions. Those use the {@code #getFieldIndex()} property to
  50  * refer to their location.
  51  */
  52 
  53 public final class Symbol implements Comparable<Symbol>, Cloneable, Serializable {
  54     private static final long serialVersionUID = 1L;
  55 
  56     /** Is this Global */
  57     public static final int IS_GLOBAL   = 1;
  58     /** Is this a variable */
  59     public static final int IS_VAR      = 2;
  60     /** Is this a parameter */
  61     public static final int IS_PARAM    = 3;
  62     /** Mask for kind flags */
  63     public static final int KINDMASK = (1 << 2) - 1; // Kinds are represented by lower two bits
  64 
  65     /** Is this symbol in scope */
  66     public static final int IS_SCOPE                = 1 <<  2;
  67     /** Is this a this symbol */
  68     public static final int IS_THIS                 = 1 <<  3;
  69     /** Is this a let */
  70     public static final int IS_LET                  = 1 <<  4;
  71     /** Is this a const */
  72     public static final int IS_CONST                = 1 <<  5;
  73     /** Is this an internal symbol, never represented explicitly in source code */
  74     public static final int IS_INTERNAL             = 1 <<  6;
  75     /** Is this a function self-reference symbol */
  76     public static final int IS_FUNCTION_SELF        = 1 <<  7;
  77     /** Is this a function declaration? */
  78     public static final int IS_FUNCTION_DECLARATION = 1 <<  8;
  79     /** Is this a program level symbol? */
  80     public static final int IS_PROGRAM_LEVEL        = 1 <<  9;
  81     /** Are this symbols' values stored in local variable slots? */
  82     public static final int HAS_SLOT                = 1 << 10;
  83     /** Is this symbol known to store an int value ? */
  84     public static final int HAS_INT_VALUE           = 1 << 11;
  85     /** Is this symbol known to store a long value ? */
  86     public static final int HAS_LONG_VALUE          = 1 << 12;
  87     /** Is this symbol known to store a double value ? */
  88     public static final int HAS_DOUBLE_VALUE        = 1 << 13;
  89     /** Is this symbol known to store an object value ? */
  90     public static final int HAS_OBJECT_VALUE        = 1 << 14;
  91     /** Is this symbol seen a declaration? Used for block scoped LET and CONST symbols only. */
  92     public static final int HAS_BEEN_DECLARED       = 1 << 15;
  93 
  94     /** Null or name identifying symbol. */
  95     private final String name;
  96 
  97     /** Symbol flags. */
  98     private int flags;
  99 
 100     /** First bytecode method local variable slot for storing the value(s) of this variable. -1 indicates the variable
 101      * is not stored in local variable slots or it is not yet known. */
 102     private transient int firstSlot = -1;
 103 
 104     /** Field number in scope or property; array index in varargs when not using arguments object. */
 105     private transient int fieldIndex = -1;
 106 
 107     /** Number of times this symbol is used in code */
 108     private int useCount;
 109 
 110     /** Debugging option - dump info and stack trace when symbols with given names are manipulated */
 111     private static final Set<String> TRACE_SYMBOLS;
 112     private static final Set<String> TRACE_SYMBOLS_STACKTRACE;
 113 
 114     static {
 115         final String stacktrace = Options.getStringProperty("nashorn.compiler.symbol.stacktrace", null);
 116         final String trace;
 117         if (stacktrace != null) {
 118             trace = stacktrace; //stacktrace always implies trace as well
 119             TRACE_SYMBOLS_STACKTRACE = new HashSet<>();
 120             for (final StringTokenizer st = new StringTokenizer(stacktrace, ","); st.hasMoreTokens(); ) {
 121                 TRACE_SYMBOLS_STACKTRACE.add(st.nextToken());
 122             }
 123         } else {
 124             trace = Options.getStringProperty("nashorn.compiler.symbol.trace", null);
 125             TRACE_SYMBOLS_STACKTRACE = null;
 126         }
 127 
 128         if (trace != null) {
 129             TRACE_SYMBOLS = new HashSet<>();
 130             for (final StringTokenizer st = new StringTokenizer(trace, ","); st.hasMoreTokens(); ) {
 131                 TRACE_SYMBOLS.add(st.nextToken());
 132             }
 133         } else {
 134             TRACE_SYMBOLS = null;
 135         }
 136     }
 137 
 138     /**
 139      * Constructor
 140      *
 141      * @param name  name of symbol
 142      * @param flags symbol flags
 143      */
 144     public Symbol(final String name, final int flags) {
 145         this.name       = name;
 146         this.flags      = flags;
 147         if(shouldTrace()) {
 148             trace("CREATE SYMBOL " + name);
 149         }
 150     }
 151 
 152     @Override
 153     public Symbol clone() {
 154         try {
 155             return (Symbol)super.clone();
 156         } catch (final CloneNotSupportedException e) {
 157             throw new AssertionError(e);
 158         }
 159     }
 160 
 161     private static String align(final String string, final int max) {
 162         final StringBuilder sb = new StringBuilder();
 163         sb.append(string.substring(0, Math.min(string.length(), max)));
 164 
 165         while (sb.length() < max) {
 166             sb.append(' ');
 167         }
 168         return sb.toString();
 169     }
 170 
 171     /**
 172      * Debugging .
 173      *
 174      * @param stream Stream to print to.
 175      */
 176 
 177     void print(final PrintWriter stream) {
 178         final StringBuilder sb = new StringBuilder();
 179 
 180         sb.append(align(name, 20)).
 181             append(": ").
 182             append(", ").
 183             append(align(firstSlot == -1 ? "none" : "" + firstSlot, 10));
 184 
 185         switch (flags & KINDMASK) {
 186         case IS_GLOBAL:
 187             sb.append(" global");
 188             break;
 189         case IS_VAR:
 190             if (isConst()) {
 191                 sb.append(" const");
 192             } else if (isLet()) {
 193                 sb.append(" let");
 194             } else {
 195                 sb.append(" var");
 196             }
 197             break;
 198         case IS_PARAM:
 199             sb.append(" param");
 200             break;
 201         default:
 202             break;
 203         }
 204 
 205         if (isScope()) {
 206             sb.append(" scope");
 207         }
 208 
 209         if (isInternal()) {
 210             sb.append(" internal");
 211         }
 212 
 213         if (isThis()) {
 214             sb.append(" this");
 215         }
 216 
 217         if (isProgramLevel()) {
 218             sb.append(" program");
 219         }
 220 
 221         sb.append('\n');
 222 
 223         stream.print(sb.toString());
 224     }
 225 
 226     /**
 227      * Compare the the symbol kind with another.
 228      *
 229      * @param other Other symbol's flags.
 230      * @return True if symbol has less kind.
 231      */
 232     public boolean less(final int other) {
 233         return (flags & KINDMASK) < (other & KINDMASK);
 234     }
 235 
 236     /**
 237      * Allocate a slot for this symbol.
 238      *
 239      * @param needsSlot True if symbol needs a slot.
 240      * @return the symbol
 241      */
 242     public Symbol setNeedsSlot(final boolean needsSlot) {
 243         if(needsSlot) {
 244             assert !isScope();
 245             flags |= HAS_SLOT;
 246         } else {
 247             flags &= ~HAS_SLOT;
 248         }
 249         return this;
 250     }
 251 
 252     /**
 253      * Return the number of slots required for the symbol.
 254      *
 255      * @return Number of slots.
 256      */
 257     public int slotCount() {
 258         return ((flags & HAS_INT_VALUE)    == 0 ? 0 : 1) +
 259                ((flags & HAS_LONG_VALUE)   == 0 ? 0 : 2) +
 260                ((flags & HAS_DOUBLE_VALUE) == 0 ? 0 : 2) +
 261                ((flags & HAS_OBJECT_VALUE) == 0 ? 0 : 1);
 262     }
 263 
 264     private boolean isSlotted() {
 265         return firstSlot != -1 && ((flags & HAS_SLOT) != 0);
 266     }
 267 
 268     @Override
 269     public String toString() {
 270         final StringBuilder sb = new StringBuilder();
 271 
 272         sb.append(name).
 273             append(' ');
 274 
 275         if (hasSlot()) {
 276             sb.append(' ').
 277                 append('(').
 278                 append("slot=").
 279                 append(firstSlot).append(' ');
 280             if((flags & HAS_INT_VALUE) != 0) { sb.append('I'); }
 281             if((flags & HAS_LONG_VALUE) != 0) { sb.append('J'); }
 282             if((flags & HAS_DOUBLE_VALUE) != 0) { sb.append('D'); }
 283             if((flags & HAS_OBJECT_VALUE) != 0) { sb.append('O'); }
 284             sb.append(')');
 285         }
 286 
 287         if (isScope()) {
 288             if(isGlobal()) {
 289                 sb.append(" G");
 290             } else {
 291                 sb.append(" S");
 292             }
 293         }
 294 
 295         return sb.toString();
 296     }
 297 
 298     @Override
 299     public int compareTo(final Symbol other) {
 300         return name.compareTo(other.name);
 301     }
 302 
 303     /**
 304      * Does this symbol have an allocated bytecode slot? Note that having an allocated bytecode slot doesn't necessarily
 305      * mean the symbol's value will be stored in it. Namely, a function parameter can have a bytecode slot, but if it is
 306      * in scope, then the bytecode slot will not be used. See {@link #isBytecodeLocal()}.
 307      *
 308      * @return true if this symbol has a local bytecode slot
 309      */
 310     public boolean hasSlot() {
 311         return (flags & HAS_SLOT) != 0;
 312     }
 313 
 314     /**
 315      * Is this symbol a local variable stored in bytecode local variable slots? This is true for a slotted variable that
 316      * is not in scope. (E.g. a parameter that is in scope is slotted, but it will not be a local variable).
 317      * @return true if this symbol is using bytecode local slots for its storage.
 318      */
 319     public boolean isBytecodeLocal() {
 320         return hasSlot() && !isScope();
 321     }
 322 
 323     /**
 324      * Returns true if this symbol is dead (it is a local variable that is statically proven to never be read in any type).
 325      * @return true if this symbol is dead
 326      */
 327     public boolean isDead() {
 328         return (flags & (HAS_SLOT | IS_SCOPE)) == 0;
 329     }
 330 
 331     /**
 332      * Check if this is a symbol in scope. Scope symbols cannot, for obvious reasons
 333      * be stored in byte code slots on the local frame
 334      *
 335      * @return true if this is scoped
 336      */
 337     public boolean isScope() {
 338         assert (flags & KINDMASK) != IS_GLOBAL || (flags & IS_SCOPE) == IS_SCOPE : "global without scope flag";
 339         return (flags & IS_SCOPE) != 0;
 340     }
 341 
 342     /**
 343      * Check if this symbol is a function declaration
 344      * @return true if a function declaration
 345      */
 346     public boolean isFunctionDeclaration() {
 347         return (flags & IS_FUNCTION_DECLARATION) != 0;
 348     }
 349 
 350     /**
 351      * Flag this symbol as scope as described in {@link Symbol#isScope()}
 352      * @return the symbol
 353      */
 354     public Symbol setIsScope() {
 355         if (!isScope()) {
 356             if(shouldTrace()) {
 357                 trace("SET IS SCOPE");
 358             }
 359             flags |= IS_SCOPE;
 360             if(!isParam()) {
 361                 flags &= ~HAS_SLOT;
 362             }
 363         }
 364         return this;
 365     }
 366 
 367     /**
 368      * Mark this symbol as a function declaration.
 369      */
 370     public void setIsFunctionDeclaration() {
 371         if (!isFunctionDeclaration()) {
 372             if(shouldTrace()) {
 373                 trace("SET IS FUNCTION DECLARATION");
 374             }
 375             flags |= IS_FUNCTION_DECLARATION;
 376         }
 377     }
 378 
 379     /**
 380      * Check if this symbol is a variable
 381      * @return true if variable
 382      */
 383     public boolean isVar() {
 384         return (flags & KINDMASK) == IS_VAR;
 385     }
 386 
 387     /**
 388      * Check if this symbol is a global (undeclared) variable
 389      * @return true if global
 390      */
 391     public boolean isGlobal() {
 392         return (flags & KINDMASK) == IS_GLOBAL;
 393     }
 394 
 395     /**
 396      * Check if this symbol is a function parameter
 397      * @return true if parameter
 398      */
 399     public boolean isParam() {
 400         return (flags & KINDMASK) == IS_PARAM;
 401     }
 402 
 403     /**
 404      * Check if this is a program (script) level definition
 405      * @return true if program level
 406      */
 407     public boolean isProgramLevel() {
 408         return (flags & IS_PROGRAM_LEVEL) != 0;
 409     }
 410 
 411     /**
 412      * Check if this symbol is a constant
 413      * @return true if a constant
 414      */
 415     public boolean isConst() {
 416         return (flags & IS_CONST) != 0;
 417     }
 418 
 419     /**
 420      * Check if this is an internal symbol, without an explicit JavaScript source
 421      * code equivalent
 422      * @return true if internal
 423      */
 424     public boolean isInternal() {
 425         return (flags & IS_INTERNAL) != 0;
 426     }
 427 
 428     /**
 429      * Check if this symbol represents {@code this}
 430      * @return true if this
 431      */
 432     public boolean isThis() {
 433         return (flags & IS_THIS) != 0;
 434     }
 435 
 436     /**
 437      * Check if this symbol is a let
 438      * @return true if let
 439      */
 440     public boolean isLet() {
 441         return (flags & IS_LET) != 0;
 442     }
 443 
 444     /**
 445      * Flag this symbol as a function's self-referencing symbol.
 446      * @return true if this symbol as a function's self-referencing symbol.
 447      */
 448     public boolean isFunctionSelf() {
 449         return (flags & IS_FUNCTION_SELF) != 0;
 450     }
 451 
 452     /**
 453      * Is this a block scoped symbol
 454      * @return true if block scoped
 455      */
 456     public boolean isBlockScoped() {
 457         return isLet() || isConst();
 458     }
 459 
 460     /**
 461      * Has this symbol been declared
 462      * @return true if declared
 463      */
 464     public boolean hasBeenDeclared() {
 465         return (flags & HAS_BEEN_DECLARED) != 0;
 466     }
 467 
 468     /**
 469      * Mark this symbol as declared
 470      */
 471     public void setHasBeenDeclared() {
 472         if (!hasBeenDeclared()) {
 473             flags |= HAS_BEEN_DECLARED;
 474         }
 475     }
 476 
 477     /**
 478      * Get the index of the field used to store this symbol, should it be an AccessorProperty
 479      * and get allocated in a JO-prefixed ScriptObject subclass.
 480      *
 481      * @return field index
 482      */
 483     public int getFieldIndex() {
 484         assert fieldIndex != -1 : "fieldIndex must be initialized " + fieldIndex;
 485         return fieldIndex;
 486     }
 487 
 488     /**
 489      * Set the index of the field used to store this symbol, should it be an AccessorProperty
 490      * and get allocated in a JO-prefixed ScriptObject subclass.
 491      *
 492      * @param fieldIndex field index - a positive integer
 493      * @return the symbol
 494      */
 495     public Symbol setFieldIndex(final int fieldIndex) {
 496         if (this.fieldIndex != fieldIndex) {
 497             this.fieldIndex = fieldIndex;
 498         }
 499         return this;
 500     }
 501 
 502     /**
 503      * Get the symbol flags
 504      * @return flags
 505      */
 506     public int getFlags() {
 507         return flags;
 508     }
 509 
 510     /**
 511      * Set the symbol flags
 512      * @param flags flags
 513      * @return the symbol
 514      */
 515     public Symbol setFlags(final int flags) {
 516         if (this.flags != flags) {
 517             this.flags = flags;
 518         }
 519         return this;
 520     }
 521 
 522     /**
 523      * Set a single symbol flag
 524      * @param flag flag to set
 525      * @return the symbol
 526      */
 527     public Symbol setFlag(final int flag) {
 528         if ((this.flags & flag) == 0) {
 529             this.flags |= flag;
 530         }
 531         return this;
 532     }
 533 
 534     /**
 535      * Clears a single symbol flag
 536      * @param flag flag to set
 537      * @return the symbol
 538      */
 539     public Symbol clearFlag(final int flag) {
 540         if ((this.flags & flag) != 0) {
 541             this.flags &= ~flag;
 542         }
 543         return this;
 544     }
 545 
 546     /**
 547      * Get the name of this symbol
 548      * @return symbol name
 549      */
 550     public String getName() {
 551         return name;
 552     }
 553 
 554     /**
 555      * Get the index of the first bytecode slot for this symbol
 556      * @return byte code slot
 557      */
 558     public int getFirstSlot() {
 559         assert isSlotted();
 560         return firstSlot;
 561     }
 562 
 563     /**
 564      * Get the index of the bytecode slot for this symbol for storing a value of the specified type.
 565      * @param type the requested type
 566      * @return byte code slot
 567      */
 568     public int getSlot(final Type type) {
 569         assert isSlotted();
 570         int typeSlot = firstSlot;
 571         if(type.isBoolean() || type.isInteger()) {
 572             assert (flags & HAS_INT_VALUE) != 0;
 573             return typeSlot;
 574         }
 575         typeSlot += ((flags & HAS_INT_VALUE) == 0 ? 0 : 1);
 576         if(type.isLong()) {
 577             assert (flags & HAS_LONG_VALUE) != 0;
 578             return typeSlot;
 579         }
 580         typeSlot += ((flags & HAS_LONG_VALUE) == 0 ? 0 : 2);
 581         if(type.isNumber()) {
 582             assert (flags & HAS_DOUBLE_VALUE) != 0;
 583             return typeSlot;
 584         }
 585         assert type.isObject();
 586         assert (flags & HAS_OBJECT_VALUE) != 0 : name;
 587         return typeSlot + ((flags & HAS_DOUBLE_VALUE) == 0 ? 0 : 2);
 588     }
 589 
 590     /**
 591      * Returns true if this symbol has a local variable slot for storing a value of specific type.
 592      * @param type the type
 593      * @return true if this symbol has a local variable slot for storing a value of specific type.
 594      */
 595     public boolean hasSlotFor(final Type type) {
 596         if(type.isBoolean() || type.isInteger()) {
 597             return (flags & HAS_INT_VALUE) != 0;
 598         } else if(type.isLong()) {
 599             return (flags & HAS_LONG_VALUE) != 0;
 600         } else if(type.isNumber()) {
 601             return (flags & HAS_DOUBLE_VALUE) != 0;
 602         }
 603         assert type.isObject();
 604         return (flags & HAS_OBJECT_VALUE) != 0;
 605     }
 606 
 607     /**
 608      * Marks this symbol as having a local variable slot for storing a value of specific type.
 609      * @param type the type
 610      */
 611     public void setHasSlotFor(final Type type) {
 612         if(type.isBoolean() || type.isInteger()) {
 613             setFlag(HAS_INT_VALUE);
 614         } else if(type.isLong()) {
 615             setFlag(HAS_LONG_VALUE);
 616         } else if(type.isNumber()) {
 617             setFlag(HAS_DOUBLE_VALUE);
 618         } else {
 619             assert type.isObject();
 620             setFlag(HAS_OBJECT_VALUE);
 621         }
 622     }
 623 
 624     /**
 625      * Increase the symbol's use count by one.
 626      */
 627     public void increaseUseCount() {
 628         if (isScope()) { // Avoid dirtying a cache line; we only need the use count for scoped symbols
 629             useCount++;
 630         }
 631     }
 632 
 633     /**
 634      * Get the symbol's use count
 635      * @return the number of times the symbol is used in code.
 636      */
 637     public int getUseCount() {
 638         return useCount;
 639     }
 640 
 641     /**
 642      * Set the bytecode slot for this symbol
 643      * @param  firstSlot valid bytecode slot
 644      * @return the symbol
 645      */
 646     public Symbol setFirstSlot(final int firstSlot) {
 647         assert firstSlot >= 0 && firstSlot <= 65535;
 648         if (firstSlot != this.firstSlot) {
 649             if(shouldTrace()) {
 650                 trace("SET SLOT " + firstSlot);
 651             }
 652             this.firstSlot = firstSlot;
 653         }
 654         return this;
 655     }
 656 
 657     /**
 658      * From a lexical context, set this symbol as needing scope, which
 659      * will set flags for the defining block that will be written when
 660      * block is popped from the lexical context stack, used by codegen
 661      * when flags need to be tagged, but block is in the
 662      * middle of evaluation and cannot be modified.
 663      *
 664      * @param  lc     lexical context
 665      * @param  symbol symbol
 666      * @return the symbol
 667      */
 668     public static Symbol setSymbolIsScope(final LexicalContext lc, final Symbol symbol) {
 669         symbol.setIsScope();
 670         if (!symbol.isGlobal()) {
 671             lc.setBlockNeedsScope(lc.getDefiningBlock(symbol));
 672         }
 673         return symbol;
 674     }
 675 
 676     private boolean shouldTrace() {
 677         return TRACE_SYMBOLS != null && (TRACE_SYMBOLS.isEmpty() || TRACE_SYMBOLS.contains(name));
 678     }
 679 
 680     private void trace(final String desc) {
 681         Context.err(Debug.id(this) + " SYMBOL: '" + name + "' " + desc);
 682         if (TRACE_SYMBOLS_STACKTRACE != null && (TRACE_SYMBOLS_STACKTRACE.isEmpty() || TRACE_SYMBOLS_STACKTRACE.contains(name))) {
 683             new Throwable().printStackTrace(Context.getCurrentErr());
 684         }
 685     }
 686 
 687     private void readObject(final ObjectInputStream in) throws ClassNotFoundException, IOException {
 688         in.defaultReadObject();
 689         firstSlot = -1;
 690         fieldIndex = -1;
 691     }
 692 }
--- EOF ---