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