LiteralNode.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.Serializable; 29import java.util.Arrays; 30import java.util.Collections; 31import java.util.List; 32import java.util.function.Function; 33import jdk.nashorn.internal.codegen.CompileUnit; 34import jdk.nashorn.internal.codegen.types.ArrayType; 35import jdk.nashorn.internal.codegen.types.Type; 36import jdk.nashorn.internal.ir.annotations.Immutable; 37import jdk.nashorn.internal.ir.visitor.NodeVisitor; 38import jdk.nashorn.internal.objects.NativeArray; 39import jdk.nashorn.internal.parser.Lexer.LexerToken; 40import jdk.nashorn.internal.parser.Token; 41import jdk.nashorn.internal.parser.TokenType; 42import jdk.nashorn.internal.runtime.JSType; 43import jdk.nashorn.internal.runtime.ScriptRuntime; 44import jdk.nashorn.internal.runtime.Undefined; 45 46/** 47 * Literal nodes represent JavaScript values. 48 * 49 * @param <T> the literal type 50 */ 51@Immutable 52public abstract class LiteralNode<T> extends Expression implements PropertyKey { 53 private static final long serialVersionUID = 1L; 54 55 /** Literal value */ 56 protected final T value; 57 58 /** Marker for values that must be computed at runtime */ 59 public static final Object POSTSET_MARKER = new Object(); 60 61 /** 62 * Constructor 63 * 64 * @param token token 65 * @param finish finish 66 * @param value the value of the literal 67 */ 68 protected LiteralNode(final long token, final int finish, final T value) { 69 super(token, finish); 70 this.value = value; 71 } 72 73 /** 74 * Copy constructor 75 * 76 * @param literalNode source node 77 */ 78 protected LiteralNode(final LiteralNode<T> literalNode) { 79 this(literalNode, literalNode.value); 80 } 81 82 /** 83 * A copy constructor with value change. 84 * @param literalNode the original literal node 85 * @param newValue new value for this node 86 */ 87 protected LiteralNode(final LiteralNode<T> literalNode, final T newValue) { 88 super(literalNode); 89 this.value = newValue; 90 } 91 92 /** 93 * Initialization setter, if required for immutable state. This is used for 94 * things like ArrayLiteralNodes that need to carry state for the splitter. 95 * Default implementation is just a nop. 96 * @param lc lexical context 97 * @return new literal node with initialized state, or same if nothing changed 98 */ 99 public LiteralNode<?> initialize(final LexicalContext lc) { 100 return this; 101 } 102 103 /** 104 * Check if the literal value is null 105 * @return true if literal value is null 106 */ 107 public boolean isNull() { 108 return value == null; 109 } 110 111 @Override 112 public Type getType(final Function<Symbol, Type> localVariableTypes) { 113 return Type.typeFor(value.getClass()); 114 } 115 116 @Override 117 public String getPropertyName() { 118 return JSType.toString(getObject()); 119 } 120 121 /** 122 * Fetch boolean value of node. 123 * 124 * @return boolean value of node. 125 */ 126 public boolean getBoolean() { 127 return JSType.toBoolean(value); 128 } 129 130 /** 131 * Fetch int32 value of node. 132 * 133 * @return Int32 value of node. 134 */ 135 public int getInt32() { 136 return JSType.toInt32(value); 137 } 138 139 /** 140 * Fetch uint32 value of node. 141 * 142 * @return uint32 value of node. 143 */ 144 public long getUint32() { 145 return JSType.toUint32(value); 146 } 147 148 /** 149 * Fetch long value of node 150 * 151 * @return long value of node 152 */ 153 public long getLong() { 154 return JSType.toLong(value); 155 } 156 157 /** 158 * Fetch double value of node. 159 * 160 * @return double value of node. 161 */ 162 public double getNumber() { 163 return JSType.toNumber(value); 164 } 165 166 /** 167 * Get the array value of the node 168 * 169 * @return the array value 170 */ 171 public Node[] getArray() { 172 assert false : "not an array node"; 173 return null; 174 } 175 176 /** 177 * Fetch String value of node. 178 * 179 * @return String value of node. 180 */ 181 public String getString() { 182 return JSType.toString(value); 183 } 184 185 /** 186 * Fetch Object value of node. 187 * 188 * @return Object value of node. 189 */ 190 public Object getObject() { 191 return value; 192 } 193 194 /** 195 * Test if the value is a string. 196 * 197 * @return True if value is a string. 198 */ 199 public boolean isString() { 200 return value instanceof String; 201 } 202 203 /** 204 * Test if tha value is a number 205 * 206 * @return True if value is a number 207 */ 208 public boolean isNumeric() { 209 return value instanceof Number; 210 } 211 212 /** 213 * Assist in IR navigation. 214 * 215 * @param visitor IR navigating visitor. 216 */ 217 @Override 218 public Node accept(final NodeVisitor<? extends LexicalContext> visitor) { 219 if (visitor.enterLiteralNode(this)) { 220 return visitor.leaveLiteralNode(this); 221 } 222 223 return this; 224 } 225 226 @Override 227 public void toString(final StringBuilder sb, final boolean printType) { 228 if (value == null) { 229 sb.append("null"); 230 } else { 231 sb.append(value.toString()); 232 } 233 } 234 235 /** 236 * Get the literal node value 237 * @return the value 238 */ 239 public final T getValue() { 240 return value; 241 } 242 243 private static Expression[] valueToArray(final List<Expression> value) { 244 return value.toArray(new Expression[value.size()]); 245 } 246 247 /** 248 * Create a new null literal 249 * 250 * @param token token 251 * @param finish finish 252 * 253 * @return the new literal node 254 */ 255 public static LiteralNode<Object> newInstance(final long token, final int finish) { 256 return new NullLiteralNode(token, finish); 257 } 258 259 /** 260 * Create a new null literal based on a parent node (source, token, finish) 261 * 262 * @param parent parent node 263 * 264 * @return the new literal node 265 */ 266 public static LiteralNode<Object> newInstance(final Node parent) { 267 return new NullLiteralNode(parent.getToken(), parent.getFinish()); 268 } 269 270 /** 271 * Super class for primitive (side-effect free) literals. 272 * 273 * @param <T> the literal type 274 */ 275 public static class PrimitiveLiteralNode<T> extends LiteralNode<T> { 276 private static final long serialVersionUID = 1L; 277 278 private PrimitiveLiteralNode(final long token, final int finish, final T value) { 279 super(token, finish, value); 280 } 281 282 private PrimitiveLiteralNode(final PrimitiveLiteralNode<T> literalNode) { 283 super(literalNode); 284 } 285 286 /** 287 * Check if the literal value is boolean true 288 * @return true if literal value is boolean true 289 */ 290 public boolean isTrue() { 291 return JSType.toBoolean(value); 292 } 293 294 @Override 295 public boolean isLocal() { 296 return true; 297 } 298 299 @Override 300 public boolean isAlwaysFalse() { 301 return !isTrue(); 302 } 303 304 @Override 305 public boolean isAlwaysTrue() { 306 return isTrue(); 307 } 308 } 309 310 @Immutable 311 private static final class BooleanLiteralNode extends PrimitiveLiteralNode<Boolean> { 312 private static final long serialVersionUID = 1L; 313 314 private BooleanLiteralNode(final long token, final int finish, final boolean value) { 315 super(Token.recast(token, value ? TokenType.TRUE : TokenType.FALSE), finish, value); 316 } 317 318 private BooleanLiteralNode(final BooleanLiteralNode literalNode) { 319 super(literalNode); 320 } 321 322 @Override 323 public boolean isTrue() { 324 return value; 325 } 326 327 @Override 328 public Type getType(final Function<Symbol, Type> localVariableTypes) { 329 return Type.BOOLEAN; 330 } 331 332 @Override 333 public Type getWidestOperationType() { 334 return Type.BOOLEAN; 335 } 336 } 337 338 /** 339 * Create a new boolean literal 340 * 341 * @param token token 342 * @param finish finish 343 * @param value true or false 344 * 345 * @return the new literal node 346 */ 347 public static LiteralNode<Boolean> newInstance(final long token, final int finish, final boolean value) { 348 return new BooleanLiteralNode(token, finish, value); 349 } 350 351 /** 352 * Create a new boolean literal based on a parent node (source, token, finish) 353 * 354 * @param parent parent node 355 * @param value true or false 356 * 357 * @return the new literal node 358 */ 359 public static LiteralNode<?> newInstance(final Node parent, final boolean value) { 360 return new BooleanLiteralNode(parent.getToken(), parent.getFinish(), value); 361 } 362 363 @Immutable 364 private static final class NumberLiteralNode extends PrimitiveLiteralNode<Number> { 365 private static final long serialVersionUID = 1L; 366 367 private final Type type = numberGetType(value); 368 369 private NumberLiteralNode(final long token, final int finish, final Number value) { 370 super(Token.recast(token, TokenType.DECIMAL), finish, value); 371 } 372 373 private NumberLiteralNode(final NumberLiteralNode literalNode) { 374 super(literalNode); 375 } 376 377 private static Type numberGetType(final Number number) { 378 if (number instanceof Integer) { 379 return Type.INT; 380 } else if (number instanceof Long) { 381 return Type.LONG; 382 } else if (number instanceof Double) { 383 return Type.NUMBER; 384 } else { 385 assert false; 386 } 387 388 return null; 389 } 390 391 @Override 392 public Type getType(final Function<Symbol, Type> localVariableTypes) { 393 return type; 394 } 395 396 @Override 397 public Type getWidestOperationType() { 398 return getType(); 399 } 400 401 } 402 /** 403 * Create a new number literal 404 * 405 * @param token token 406 * @param finish finish 407 * @param value literal value 408 * 409 * @return the new literal node 410 */ 411 public static LiteralNode<Number> newInstance(final long token, final int finish, final Number value) { 412 return new NumberLiteralNode(token, finish, value); 413 } 414 415 /** 416 * Create a new number literal based on a parent node (source, token, finish) 417 * 418 * @param parent parent node 419 * @param value literal value 420 * 421 * @return the new literal node 422 */ 423 public static LiteralNode<?> newInstance(final Node parent, final Number value) { 424 return new NumberLiteralNode(parent.getToken(), parent.getFinish(), value); 425 } 426 427 private static class UndefinedLiteralNode extends PrimitiveLiteralNode<Undefined> { 428 private static final long serialVersionUID = 1L; 429 430 private UndefinedLiteralNode(final long token, final int finish) { 431 super(Token.recast(token, TokenType.OBJECT), finish, ScriptRuntime.UNDEFINED); 432 } 433 434 private UndefinedLiteralNode(final UndefinedLiteralNode literalNode) { 435 super(literalNode); 436 } 437 } 438 439 /** 440 * Create a new undefined literal 441 * 442 * @param token token 443 * @param finish finish 444 * @param value undefined value, passed only for polymorphisism discrimination 445 * 446 * @return the new literal node 447 */ 448 public static LiteralNode<Undefined> newInstance(final long token, final int finish, final Undefined value) { 449 return new UndefinedLiteralNode(token, finish); 450 } 451 452 /** 453 * Create a new null literal based on a parent node (source, token, finish) 454 * 455 * @param parent parent node 456 * @param value undefined value 457 * 458 * @return the new literal node 459 */ 460 public static LiteralNode<?> newInstance(final Node parent, final Undefined value) { 461 return new UndefinedLiteralNode(parent.getToken(), parent.getFinish()); 462 } 463 464 @Immutable 465 private static class StringLiteralNode extends PrimitiveLiteralNode<String> { 466 private static final long serialVersionUID = 1L; 467 468 private StringLiteralNode(final long token, final int finish, final String value) { 469 super(Token.recast(token, TokenType.STRING), finish, value); 470 } 471 472 private StringLiteralNode(final StringLiteralNode literalNode) { 473 super(literalNode); 474 } 475 476 @Override 477 public void toString(final StringBuilder sb, final boolean printType) { 478 sb.append('\"'); 479 sb.append(value); 480 sb.append('\"'); 481 } 482 } 483 484 /** 485 * Create a new string literal 486 * 487 * @param token token 488 * @param finish finish 489 * @param value string value 490 * 491 * @return the new literal node 492 */ 493 public static LiteralNode<String> newInstance(final long token, final int finish, final String value) { 494 return new StringLiteralNode(token, finish, value); 495 } 496 497 /** 498 * Create a new String literal based on a parent node (source, token, finish) 499 * 500 * @param parent parent node 501 * @param value string value 502 * 503 * @return the new literal node 504 */ 505 public static LiteralNode<?> newInstance(final Node parent, final String value) { 506 return new StringLiteralNode(parent.getToken(), parent.getFinish(), value); 507 } 508 509 @Immutable 510 private static class LexerTokenLiteralNode extends LiteralNode<LexerToken> { 511 private static final long serialVersionUID = 1L; 512 513 private LexerTokenLiteralNode(final long token, final int finish, final LexerToken value) { 514 super(Token.recast(token, TokenType.STRING), finish, value); //TODO is string the correct token type here? 515 } 516 517 private LexerTokenLiteralNode(final LexerTokenLiteralNode literalNode) { 518 super(literalNode); 519 } 520 521 @Override 522 public Type getType(final Function<Symbol, Type> localVariableTypes) { 523 return Type.OBJECT; 524 } 525 526 @Override 527 public void toString(final StringBuilder sb, final boolean printType) { 528 sb.append(value.toString()); 529 } 530 } 531 532 /** 533 * Create a new literal node for a lexer token 534 * 535 * @param token token 536 * @param finish finish 537 * @param value lexer token value 538 * 539 * @return the new literal node 540 */ 541 public static LiteralNode<LexerToken> newInstance(final long token, final int finish, final LexerToken value) { 542 return new LexerTokenLiteralNode(token, finish, value); 543 } 544 545 /** 546 * Create a new lexer token literal based on a parent node (source, token, finish) 547 * 548 * @param parent parent node 549 * @param value lexer token 550 * 551 * @return the new literal node 552 */ 553 public static LiteralNode<?> newInstance(final Node parent, final LexerToken value) { 554 return new LexerTokenLiteralNode(parent.getToken(), parent.getFinish(), value); 555 } 556 557 /** 558 * Get the constant value for an object, or {@link #POSTSET_MARKER} if the value can't be statically computed. 559 * 560 * @param object a node or value object 561 * @return the constant value or {@code POSTSET_MARKER} 562 */ 563 public static Object objectAsConstant(final Object object) { 564 if (object == null) { 565 return null; 566 } else if (object instanceof Number || object instanceof String || object instanceof Boolean) { 567 return object; 568 } else if (object instanceof LiteralNode) { 569 return objectAsConstant(((LiteralNode<?>)object).getValue()); 570 } 571 572 return POSTSET_MARKER; 573 } 574 575 private static final class NullLiteralNode extends PrimitiveLiteralNode<Object> { 576 private static final long serialVersionUID = 1L; 577 578 private NullLiteralNode(final long token, final int finish) { 579 super(Token.recast(token, TokenType.OBJECT), finish, null); 580 } 581 582 @Override 583 public Node accept(final NodeVisitor<? extends LexicalContext> visitor) { 584 if (visitor.enterLiteralNode(this)) { 585 return visitor.leaveLiteralNode(this); 586 } 587 588 return this; 589 } 590 591 @Override 592 public Type getType(final Function<Symbol, Type> localVariableTypes) { 593 return Type.OBJECT; 594 } 595 596 @Override 597 public Type getWidestOperationType() { 598 return Type.OBJECT; 599 } 600 } 601 602 /** 603 * Array literal node class. 604 */ 605 @Immutable 606 public static final class ArrayLiteralNode extends LiteralNode<Expression[]> implements LexicalContextNode { 607 private static final long serialVersionUID = 1L; 608 609 /** Array element type. */ 610 private final Type elementType; 611 612 /** Preset constant array. */ 613 private final Object presets; 614 615 /** Indices of array elements requiring computed post sets. */ 616 private final int[] postsets; 617 618 /** Sub units with indexes ranges, in which to split up code generation, for large literals */ 619 private final List<ArrayUnit> units; 620 621 /** 622 * An ArrayUnit is a range in an ArrayLiteral. ArrayLiterals can 623 * be split if they are too large, for bytecode generation reasons 624 */ 625 public static final class ArrayUnit implements CompileUnitHolder, Serializable { 626 private static final long serialVersionUID = 1L; 627 628 /** Compile unit associated with the postsets range. */ 629 private final CompileUnit compileUnit; 630 631 /** postsets range associated with the unit (hi not inclusive). */ 632 private final int lo, hi; 633 634 /** 635 * Constructor 636 * @param compileUnit compile unit 637 * @param lo lowest array index in unit 638 * @param hi highest array index in unit + 1 639 */ 640 public ArrayUnit(final CompileUnit compileUnit, final int lo, final int hi) { 641 this.compileUnit = compileUnit; 642 this.lo = lo; 643 this.hi = hi; 644 } 645 646 /** 647 * Get the high index position of the ArrayUnit (non inclusive) 648 * @return high index position 649 */ 650 public int getHi() { 651 return hi; 652 } 653 654 /** 655 * Get the low index position of the ArrayUnit (inclusive) 656 * @return low index position 657 */ 658 public int getLo() { 659 return lo; 660 } 661 662 /** 663 * The array compile unit 664 * @return array compile unit 665 */ 666 @Override 667 public CompileUnit getCompileUnit() { 668 return compileUnit; 669 } 670 } 671 672 private static final class ArrayLiteralInitializer { 673 674 static ArrayLiteralNode initialize(final ArrayLiteralNode node) { 675 final Type elementType = computeElementType(node.value); 676 final int[] postsets = computePostsets(node.value); 677 final Object presets = computePresets(node.value, elementType, postsets); 678 return new ArrayLiteralNode(node, node.value, elementType, postsets, presets, node.units); 679 } 680 681 private static Type computeElementType(final Expression[] value) { 682 Type widestElementType = Type.INT; 683 684 for (final Expression elem : value) { 685 if (elem == null) { 686 widestElementType = widestElementType.widest(Type.OBJECT); //no way to represent undefined as number 687 break; 688 } 689 690 final Type type = elem.getType().isUnknown() ? Type.OBJECT : elem.getType(); 691 if (type.isBoolean()) { 692 //TODO fix this with explicit boolean types 693 widestElementType = widestElementType.widest(Type.OBJECT); 694 break; 695 } 696 697 widestElementType = widestElementType.widest(type); 698 if (widestElementType.isObject()) { 699 break; 700 } 701 } 702 return widestElementType; 703 } 704 705 private static int[] computePostsets(final Expression[] value) { 706 final int[] computed = new int[value.length]; 707 int nComputed = 0; 708 709 for (int i = 0; i < value.length; i++) { 710 final Expression element = value[i]; 711 if (element == null || objectAsConstant(element) == POSTSET_MARKER) { 712 computed[nComputed++] = i; 713 } 714 } 715 return Arrays.copyOf(computed, nComputed); 716 } 717 718 private static boolean setArrayElement(final int[] array, final int i, final Object n) { 719 if (n instanceof Number) { 720 array[i] = ((Number)n).intValue(); 721 return true; 722 } 723 return false; 724 } 725 726 private static boolean setArrayElement(final long[] array, final int i, final Object n) { 727 if (n instanceof Number) { 728 array[i] = ((Number)n).longValue(); 729 return true; 730 } 731 return false; 732 } 733 734 private static boolean setArrayElement(final double[] array, final int i, final Object n) { 735 if (n instanceof Number) { 736 array[i] = ((Number)n).doubleValue(); 737 return true; 738 } 739 return false; 740 } 741 742 private static int[] presetIntArray(final Expression[] value, final int[] postsets) { 743 final int[] array = new int[value.length]; 744 int nComputed = 0; 745 for (int i = 0; i < value.length; i++) { 746 if (!setArrayElement(array, i, objectAsConstant(value[i]))) { 747 assert postsets[nComputed++] == i; 748 } 749 } 750 assert postsets.length == nComputed; 751 return array; 752 } 753 754 private static long[] presetLongArray(final Expression[] value, final int[] postsets) { 755 final long[] array = new long[value.length]; 756 int nComputed = 0; 757 for (int i = 0; i < value.length; i++) { 758 if (!setArrayElement(array, i, objectAsConstant(value[i]))) { 759 assert postsets[nComputed++] == i; 760 } 761 } 762 assert postsets.length == nComputed; 763 return array; 764 } 765 766 private static double[] presetDoubleArray(final Expression[] value, final int[] postsets) { 767 final double[] array = new double[value.length]; 768 int nComputed = 0; 769 for (int i = 0; i < value.length; i++) { 770 if (!setArrayElement(array, i, objectAsConstant(value[i]))) { 771 assert postsets[nComputed++] == i; 772 } 773 } 774 assert postsets.length == nComputed; 775 return array; 776 } 777 778 private static Object[] presetObjectArray(final Expression[] value, final int[] postsets) { 779 final Object[] array = new Object[value.length]; 780 int nComputed = 0; 781 782 for (int i = 0; i < value.length; i++) { 783 final Node node = value[i]; 784 785 if (node == null) { 786 assert postsets[nComputed++] == i; 787 continue; 788 } 789 final Object element = objectAsConstant(node); 790 791 if (element != POSTSET_MARKER) { 792 array[i] = element; 793 } else { 794 assert postsets[nComputed++] == i; 795 } 796 } 797 798 assert postsets.length == nComputed; 799 return array; 800 } 801 802 static Object computePresets(final Expression[] value, final Type elementType, final int[] postsets) { 803 assert !elementType.isUnknown(); 804 if (elementType.isInteger()) { 805 return presetIntArray(value, postsets); 806 } else if (elementType.isLong()) { 807 return presetLongArray(value, postsets); 808 } else if (elementType.isNumeric()) { 809 return presetDoubleArray(value, postsets); 810 } else { 811 return presetObjectArray(value, postsets); 812 } 813 } 814 } 815 816 /** 817 * Constructor 818 * 819 * @param token token 820 * @param finish finish 821 * @param value array literal value, a Node array 822 */ 823 protected ArrayLiteralNode(final long token, final int finish, final Expression[] value) { 824 super(Token.recast(token, TokenType.ARRAY), finish, value); 825 this.elementType = Type.UNKNOWN; 826 this.presets = null; 827 this.postsets = null; 828 this.units = null; 829 } 830 831 /** 832 * Copy constructor 833 * @param node source array literal node 834 */ 835 private ArrayLiteralNode(final ArrayLiteralNode node, final Expression[] value, final Type elementType, final int[] postsets, final Object presets, final List<ArrayUnit> units) { 836 super(node, value); 837 this.elementType = elementType; 838 this.postsets = postsets; 839 this.presets = presets; 840 this.units = units; 841 } 842 843 @Override 844 public Node[] getArray() { 845 return value; 846 } 847 848 /** 849 * Setter that initializes all code generation meta data for an 850 * ArrayLiteralNode. This acts a setter, so the return value may 851 * return a new node and must be handled 852 * 853 * @param lc lexical context 854 * @return new array literal node with postsets, presets and element types initialized 855 */ 856 @Override 857 public ArrayLiteralNode initialize(final LexicalContext lc) { 858 return Node.replaceInLexicalContext(lc, this, ArrayLiteralInitializer.initialize(this)); 859 } 860 861 /** 862 * Get the array element type as Java format, e.g. [I 863 * @return array element type 864 */ 865 public ArrayType getArrayType() { 866 return getArrayType(getElementType()); 867 } 868 869 private static ArrayType getArrayType(final Type elementType) { 870 if (elementType.isInteger()) { 871 return Type.INT_ARRAY; 872 } else if (elementType.isLong()) { 873 return Type.LONG_ARRAY; 874 } else if (elementType.isNumeric()) { 875 return Type.NUMBER_ARRAY; 876 } else { 877 return Type.OBJECT_ARRAY; 878 } 879 } 880 881 @Override 882 public Type getType(final Function<Symbol, Type> localVariableTypes) { 883 return Type.typeFor(NativeArray.class); 884 } 885 886 /** 887 * Get the element type of this array literal 888 * @return element type 889 */ 890 public Type getElementType() { 891 assert !elementType.isUnknown() : this + " has elementType=unknown"; 892 return elementType; 893 } 894 895 /** 896 * Get indices of arrays containing computed post sets. post sets 897 * are things like non literals e.g. "x+y" instead of i or 17 898 * @return post set indices 899 */ 900 public int[] getPostsets() { 901 assert postsets != null : this + " elementType=" + elementType + " has no postsets"; 902 return postsets; 903 } 904 905 private boolean presetsMatchElementType() { 906 if (elementType == Type.INT) { 907 return presets instanceof int[]; 908 } else if (elementType == Type.LONG) { 909 return presets instanceof long[]; 910 } else if (elementType == Type.NUMBER) { 911 return presets instanceof double[]; 912 } else { 913 return presets instanceof Object[]; 914 } 915 } 916 917 /** 918 * Get presets constant array 919 * @return presets array, always returns an array type 920 */ 921 public Object getPresets() { 922 assert presets != null && presetsMatchElementType() : this + " doesn't have presets, or invalid preset type: " + presets; 923 return presets; 924 } 925 926 /** 927 * Get the array units that make up this ArrayLiteral 928 * @see ArrayUnit 929 * @return list of array units 930 */ 931 public List<ArrayUnit> getUnits() { 932 return units == null ? null : Collections.unmodifiableList(units); 933 } 934 935 /** 936 * Set the ArrayUnits that make up this ArrayLiteral 937 * @param lc lexical context 938 * @see ArrayUnit 939 * @param units list of array units 940 * @return new or changed arrayliteralnode 941 */ 942 public ArrayLiteralNode setUnits(final LexicalContext lc, final List<ArrayUnit> units) { 943 if (this.units == units) { 944 return this; 945 } 946 return Node.replaceInLexicalContext(lc, this, new ArrayLiteralNode(this, value, elementType, postsets, presets, units)); 947 } 948 949 @Override 950 public Node accept(final NodeVisitor<? extends LexicalContext> visitor) { 951 return Acceptor.accept(this, visitor); 952 } 953 954 @Override 955 public Node accept(final LexicalContext lc, final NodeVisitor<? extends LexicalContext> visitor) { 956 if (visitor.enterLiteralNode(this)) { 957 final List<Expression> oldValue = Arrays.asList(value); 958 final List<Expression> newValue = Node.accept(visitor, oldValue); 959 return visitor.leaveLiteralNode(oldValue != newValue ? setValue(lc, newValue) : this); 960 } 961 return this; 962 } 963 964 private ArrayLiteralNode setValue(final LexicalContext lc, final Expression[] value) { 965 if (this.value == value) { 966 return this; 967 } 968 return Node.replaceInLexicalContext(lc, this, new ArrayLiteralNode(this, value, elementType, postsets, presets, units)); 969 } 970 971 private ArrayLiteralNode setValue(final LexicalContext lc, final List<Expression> value) { 972 return setValue(lc, value.toArray(new Expression[value.size()])); 973 } 974 975 @Override 976 public void toString(final StringBuilder sb, final boolean printType) { 977 sb.append('['); 978 boolean first = true; 979 for (final Node node : value) { 980 if (!first) { 981 sb.append(','); 982 sb.append(' '); 983 } 984 if (node == null) { 985 sb.append("undefined"); 986 } else { 987 node.toString(sb, printType); 988 } 989 first = false; 990 } 991 sb.append(']'); 992 } 993 } 994 995 /** 996 * Create a new array literal of Nodes from a list of Node values 997 * 998 * @param token token 999 * @param finish finish 1000 * @param value literal value list 1001 * 1002 * @return the new literal node 1003 */ 1004 public static LiteralNode<Expression[]> newInstance(final long token, final int finish, final List<Expression> value) { 1005 return new ArrayLiteralNode(token, finish, valueToArray(value)); 1006 } 1007 1008 /** 1009 * Create a new array literal based on a parent node (source, token, finish) 1010 * 1011 * @param parent parent node 1012 * @param value literal value list 1013 * 1014 * @return the new literal node 1015 */ 1016 public static LiteralNode<?> newInstance(final Node parent, final List<Expression> value) { 1017 return new ArrayLiteralNode(parent.getToken(), parent.getFinish(), valueToArray(value)); 1018 } 1019 1020 /** 1021 * Create a new array literal of Nodes 1022 * 1023 * @param token token 1024 * @param finish finish 1025 * @param value literal value array 1026 * 1027 * @return the new literal node 1028 */ 1029 public static LiteralNode<Expression[]> newInstance(final long token, final int finish, final Expression[] value) { 1030 return new ArrayLiteralNode(token, finish, value); 1031 } 1032} 1033