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