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.runtime; 27 28 import static jdk.nashorn.internal.codegen.CompilerConstants.staticCall; 29 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; 30 31 import java.lang.invoke.MethodHandle; 32 import java.lang.invoke.MethodHandles; 33 import java.lang.reflect.Array; 34 import java.util.Deque; 35 import java.util.List; 36 import jdk.internal.dynalink.beans.StaticClass; 37 import jdk.nashorn.api.scripting.JSObject; 38 import jdk.nashorn.internal.codegen.CompilerConstants.Call; 39 import jdk.nashorn.internal.objects.Global; 40 import jdk.nashorn.internal.parser.Lexer; 41 import jdk.nashorn.internal.runtime.arrays.ArrayLikeIterator; 42 import jdk.nashorn.internal.runtime.linker.Bootstrap; 43 44 /** 45 * Representation for ECMAScript types - this maps directly to the ECMA script standard 46 */ 47 public enum JSType { 48 /** The undefined type */ 49 UNDEFINED("undefined"), 50 51 /** The null type */ 52 NULL("object"), 53 54 /** The boolean type */ 55 BOOLEAN("boolean"), 56 57 /** The number type */ 58 NUMBER("number"), 59 60 /** The string type */ 61 STRING("string"), 62 63 /** The object type */ 64 OBJECT("object"), 65 66 /** The function type */ 67 FUNCTION("function"); 68 69 /** The type name as returned by ECMAScript "typeof" operator*/ 70 private final String typeName; 71 72 /** Max value for an uint32 in JavaScript */ 73 public static final long MAX_UINT = 0xFFFF_FFFFL; 74 75 private static final MethodHandles.Lookup myLookup = MethodHandles.lookup(); 76 77 /** JavaScript compliant conversion function from Object to boolean */ 78 public static final Call TO_BOOLEAN = staticCall(myLookup, JSType.class, "toBoolean", boolean.class, Object.class); 79 80 /** JavaScript compliant conversion function from number to boolean */ 81 public static final Call TO_BOOLEAN_D = staticCall(myLookup, JSType.class, "toBoolean", boolean.class, double.class); 82 83 /** JavaScript compliant conversion function from Object to integer */ 84 public static final Call TO_INTEGER = staticCall(myLookup, JSType.class, "toInteger", int.class, Object.class); 85 86 /** JavaScript compliant conversion function from Object to long */ 87 public static final Call TO_LONG = staticCall(myLookup, JSType.class, "toLong", long.class, Object.class); 88 89 /** JavaScript compliant conversion function from Object to number */ 90 public static final Call TO_NUMBER = staticCall(myLookup, JSType.class, "toNumber", double.class, Object.class); 91 92 /** JavaScript compliant conversion function from Object to String */ 93 public static final Call TO_STRING = staticCall(myLookup, JSType.class, "toString", String.class, Object.class); 94 95 /** JavaScript compliant conversion function from Object to int32 */ 96 public static final Call TO_INT32 = staticCall(myLookup, JSType.class, "toInt32", int.class, Object.class); 97 98 /** JavaScript compliant conversion function from double to int32 */ 99 public static final Call TO_INT32_D = staticCall(myLookup, JSType.class, "toInt32", int.class, double.class); 100 101 /** JavaScript compliant conversion function from Object to uint32 */ 102 public static final Call TO_UINT32 = staticCall(myLookup, JSType.class, "toUint32", long.class, Object.class); 103 104 /** JavaScript compliant conversion function from number to uint32 */ 105 public static final Call TO_UINT32_D = staticCall(myLookup, JSType.class, "toUint32", long.class, double.class); 106 107 /** JavaScript compliant conversion function from Object to int64 */ 108 public static final Call TO_INT64 = staticCall(myLookup, JSType.class, "toInt64", long.class, Object.class); 109 110 /** JavaScript compliant conversion function from number to int64 */ 111 public static final Call TO_INT64_D = staticCall(myLookup, JSType.class, "toInt64", long.class, double.class); 112 113 /** JavaScript compliant conversion function from number to String */ 114 public static final Call TO_STRING_D = staticCall(myLookup, JSType.class, "toString", String.class, double.class); 115 116 /** Combined call to toPrimitive followed by toString. */ 117 public static final Call TO_PRIMITIVE_TO_STRING = staticCall(myLookup, JSType.class, "toPrimitiveToString", String.class, Object.class); 118 119 /** Method handle to convert a JS Object to a Java array. */ 120 public static final Call TO_JAVA_ARRAY = staticCall(myLookup, JSType.class, "toJavaArray", Object.class, Object.class, Class.class); 121 122 /** Method handle to convert a JS Object to a Java List. */ 123 public static final Call TO_JAVA_LIST = staticCall(myLookup, JSType.class, "toJavaList", List.class, Object.class); 124 125 /** Method handle to convert a JS Object to a Java deque. */ 126 public static final Call TO_JAVA_DEQUE = staticCall(myLookup, JSType.class, "toJavaDeque", Deque.class, Object.class); 127 128 private static final double INT32_LIMIT = 4294967296.0; 129 130 /** 131 * Constructor 132 * 133 * @param typeName the type name 134 */ 135 private JSType(final String typeName) { 136 this.typeName = typeName; 137 } 138 139 /** 140 * The external type name as returned by ECMAScript "typeof" operator 141 * 142 * @return type name for this type 143 */ 144 public final String typeName() { 145 return this.typeName; 146 } 147 148 /** 149 * Return the JSType for a given object 150 * 151 * @param obj an object 152 * 153 * @return the JSType for the object 154 */ 155 public static JSType of(final Object obj) { 156 // Order of these statements is tuned for performance (see JDK-8024476) 157 if (obj == null) { 158 return JSType.NULL; 159 } 160 161 if (obj instanceof ScriptObject) { 162 return (obj instanceof ScriptFunction) ? JSType.FUNCTION : JSType.OBJECT; 163 } 164 165 if (obj instanceof Boolean) { 166 return JSType.BOOLEAN; 167 } 168 169 if (obj instanceof String || obj instanceof ConsString) { 170 return JSType.STRING; 171 } 172 173 if (obj instanceof Number) { 174 return JSType.NUMBER; 175 } 176 177 if (obj == ScriptRuntime.UNDEFINED) { 178 return JSType.UNDEFINED; 179 } 180 181 return Bootstrap.isCallable(obj) ? JSType.FUNCTION : JSType.OBJECT; 182 } 183 184 /** 185 * Returns true if double number can be represented as an int 186 * 187 * @param number a long to inspect 188 * 189 * @return true for int representable longs 190 */ 191 public static boolean isRepresentableAsInt(final long number) { 192 return (int)number == number; 193 } 194 195 /** 196 * Returns true if double number can be represented as an int 197 * 198 * @param number a double to inspect 199 * 200 * @return true for int representable doubles 201 */ 202 public static boolean isRepresentableAsInt(final double number) { 203 return (int)number == number; 204 } 205 206 /** 207 * Returns true if double number can be represented as a long 208 * 209 * @param number a double to inspect 210 * @return true for long representable doubles 211 */ 212 public static boolean isRepresentableAsLong(final double number) { 213 return (long)number == number; 214 } 215 216 /** 217 * Check whether an object is primitive 218 * 219 * @param obj an object 220 * 221 * @return true if object is primitive (includes null and undefined) 222 */ 223 public static boolean isPrimitive(final Object obj) { 224 return obj == null || 225 obj == ScriptRuntime.UNDEFINED || 226 obj instanceof Boolean || 227 obj instanceof Number || 228 obj instanceof String || 229 obj instanceof ConsString; 230 } 231 232 /** 233 * Primitive converter for an object 234 * 235 * @param obj an object 236 * 237 * @return primitive form of the object 238 */ 239 public static Object toPrimitive(final Object obj) { 240 return toPrimitive(obj, null); 241 } 242 243 /** 244 * Primitive converter for an object including type hint 245 * See ECMA 9.1 ToPrimitive 246 * 247 * @param obj an object 248 * @param hint a type hint 249 * 250 * @return the primitive form of the object 251 */ 252 public static Object toPrimitive(final Object obj, final Class<?> hint) { 253 return obj instanceof ScriptObject ? toPrimitive((ScriptObject)obj, hint) : obj; 254 } 255 256 private static Object toPrimitive(final ScriptObject sobj, final Class<?> hint) { 257 final Object result = sobj.getDefaultValue(hint); 258 259 if (!isPrimitive(result)) { 260 throw typeError("bad.default.value", result.toString()); 261 } 262 263 return result; 264 } 265 266 /** 267 * Combines a hintless toPrimitive and a toString call. 268 * 269 * @param obj an object 270 * 271 * @return the string form of the primitive form of the object 272 */ 273 public static String toPrimitiveToString(Object obj) { 274 return toString(toPrimitive(obj)); 275 } 276 277 /** 278 * JavaScript compliant conversion of number to boolean 279 * 280 * @param num a number 281 * 282 * @return a boolean 283 */ 284 public static boolean toBoolean(final double num) { 285 return num != 0 && !Double.isNaN(num); 286 } 287 288 /** 289 * JavaScript compliant conversion of Object to boolean 290 * See ECMA 9.2 ToBoolean 291 * 292 * @param obj an object 293 * 294 * @return a boolean 295 */ 296 public static boolean toBoolean(final Object obj) { 297 if (obj instanceof Boolean) { 298 return (Boolean)obj; 299 } 300 301 if (nullOrUndefined(obj)) { 302 return false; 303 } 304 305 if (obj instanceof Number) { 306 final double num = ((Number)obj).doubleValue(); 307 return num != 0 && !Double.isNaN(num); 308 } 309 310 if (obj instanceof String || obj instanceof ConsString) { 311 return ((CharSequence)obj).length() > 0; 312 } 313 314 return true; 315 } 316 317 318 /** 319 * JavaScript compliant converter of Object to String 320 * See ECMA 9.8 ToString 321 * 322 * @param obj an object 323 * 324 * @return a string 325 */ 326 public static String toString(final Object obj) { 327 return toStringImpl(obj, false); 328 } 329 330 /** 331 * If obj is an instance of {@link ConsString} cast to CharSequence, else return 332 * result of {@link #toString(Object)}. 333 * 334 * @param obj an object 335 * @return an instance of String or ConsString 336 */ 337 public static CharSequence toCharSequence(final Object obj) { 338 if (obj instanceof ConsString) { 339 return (CharSequence) obj; 340 } 341 return toString(obj); 342 } 343 344 /** 345 * Check whether a string is representable as a JavaScript number 346 * 347 * @param str a string 348 * 349 * @return true if string can be represented as a number 350 */ 351 public static boolean isNumber(final String str) { 352 try { 353 Double.parseDouble(str); 354 return true; 355 } catch (final NumberFormatException e) { 356 return false; 357 } 358 } 359 360 /** 361 * JavaScript compliant conversion of integer to String 362 * 363 * @param num an integer 364 * 365 * @return a string 366 */ 367 public static String toString(final int num) { 368 return Integer.toString(num); 369 } 370 371 /** 372 * JavaScript compliant conversion of number to String 373 * See ECMA 9.8.1 374 * 375 * @param num a number 376 * 377 * @return a string 378 */ 379 public static String toString(final double num) { 380 if (isRepresentableAsInt(num)) { 381 return Integer.toString((int)num); 382 } 383 384 if (num == Double.POSITIVE_INFINITY) { 385 return "Infinity"; 386 } 387 388 if (num == Double.NEGATIVE_INFINITY) { 389 return "-Infinity"; 390 } 391 392 if (Double.isNaN(num)) { 393 return "NaN"; 394 } 395 396 return NumberToString.stringFor(num); 397 } 398 399 /** 400 * JavaScript compliant conversion of number to String 401 * 402 * @param num a number 403 * @param radix a radix for the conversion 404 * 405 * @return a string 406 */ 407 public static String toString(final double num, final int radix) { 408 assert radix >= 2 && radix <= 36 : "invalid radix"; 409 410 if (isRepresentableAsInt(num)) { 411 return Integer.toString((int)num, radix); 412 } 413 414 if (num == Double.POSITIVE_INFINITY) { 415 return "Infinity"; 416 } 417 418 if (num == Double.NEGATIVE_INFINITY) { 419 return "-Infinity"; 420 } 421 422 if (Double.isNaN(num)) { 423 return "NaN"; 424 } 425 426 if (num == 0.0) { 427 return "0"; 428 } 429 430 final String chars = "0123456789abcdefghijklmnopqrstuvwxyz"; 431 final StringBuilder sb = new StringBuilder(); 432 433 final boolean negative = num < 0.0; 434 final double signedNum = negative ? -num : num; 435 436 double intPart = Math.floor(signedNum); 437 double decPart = signedNum - intPart; 438 439 // encode integer part from least significant digit, then reverse 440 do { 441 sb.append(chars.charAt((int) (intPart % radix))); 442 intPart /= radix; 443 } while (intPart >= 1.0); 444 445 if (negative) { 446 sb.append('-'); 447 } 448 sb.reverse(); 449 450 // encode decimal part 451 if (decPart > 0.0) { 452 final int dot = sb.length(); 453 sb.append('.'); 454 do { 455 decPart *= radix; 456 final double d = Math.floor(decPart); 457 sb.append(chars.charAt((int)d)); 458 decPart -= d; 459 } while (decPart > 0.0 && sb.length() - dot < 1100); 460 // somewhat arbitrarily use same limit as V8 461 } 462 463 return sb.toString(); 464 } 465 466 /** 467 * JavaScript compliant conversion of Object to number 468 * See ECMA 9.3 ToNumber 469 * 470 * @param obj an object 471 * 472 * @return a number 473 */ 474 public static double toNumber(final Object obj) { 475 if (obj instanceof Number) { 476 return ((Number)obj).doubleValue(); 477 } 478 return toNumberGeneric(obj); 479 } 480 481 482 /** 483 * JavaScript compliant conversion of Object to number 484 * See ECMA 9.3 ToNumber 485 * 486 * @param obj an object 487 * 488 * @return a number 489 */ 490 public static double toNumber(final ScriptObject obj) { 491 return toNumber(toPrimitive(obj, Number.class)); 492 } 493 494 /** 495 * Digit representation for a character 496 * 497 * @param ch a character 498 * @param radix radix 499 * 500 * @return the digit for this character 501 */ 502 public static int digit(final char ch, final int radix) { 503 return digit(ch, radix, false); 504 } 505 506 /** 507 * Digit representation for a character 508 * 509 * @param ch a character 510 * @param radix radix 511 * @param onlyIsoLatin1 iso latin conversion only 512 * 513 * @return the digit for this character 514 */ 515 public static int digit(final char ch, final int radix, final boolean onlyIsoLatin1) { 516 final char maxInRadix = (char)('a' + (radix - 1) - 10); 517 final char c = Character.toLowerCase(ch); 518 519 if (c >= 'a' && c <= maxInRadix) { 520 return Character.digit(ch, radix); 521 } 522 523 if (Character.isDigit(ch)) { 524 if (!onlyIsoLatin1 || ch >= '0' && ch <= '9') { 525 return Character.digit(ch, radix); 526 } 527 } 528 529 return -1; 530 } 531 532 /** 533 * JavaScript compliant String to number conversion 534 * 535 * @param str a string 536 * 537 * @return a number 538 */ 539 public static double toNumber(final String str) { 540 int end = str.length(); 541 if (end == 0) { 542 return 0.0; // Empty string 543 } 544 545 int start = 0; 546 char f = str.charAt(0); 547 548 while (Lexer.isJSWhitespace(f)) { 549 if (++start == end) { 550 return 0.0d; // All whitespace string 551 } 552 f = str.charAt(start); 553 } 554 555 // Guaranteed to terminate even without start >= end check, as the previous loop found at least one 556 // non-whitespace character. 557 while (Lexer.isJSWhitespace(str.charAt(end - 1))) { 558 end--; 559 } 560 561 final boolean negative; 562 if (f == '-') { 563 if(++start == end) { 564 return Double.NaN; // Single-char "-" string 565 } 566 f = str.charAt(start); 567 negative = true; 568 } else { 569 if (f == '+') { 570 if (++start == end) { 571 return Double.NaN; // Single-char "+" string 572 } 573 f = str.charAt(start); 574 } 575 negative = false; 576 } 577 578 final double value; 579 if (start + 1 < end && f == '0' && Character.toLowerCase(str.charAt(start + 1)) == 'x') { 580 //decode hex string 581 value = parseRadix(str.toCharArray(), start + 2, end, 16); 582 } else { 583 // Fast (no NumberFormatException) path to NaN for non-numeric strings. We allow those starting with "I" or 584 // "N" to allow for parsing "NaN" and "Infinity" correctly. 585 if ((f < '0' || f > '9') && f != '.' && f != 'I' && f != 'N') { 586 return Double.NaN; 587 } 588 try { 589 value = Double.parseDouble(str.substring(start, end)); 590 } catch (final NumberFormatException e) { 591 return Double.NaN; 592 } 593 } 594 595 return negative ? -value : value; 596 } 597 598 /** 599 * JavaScript compliant Object to integer conversion. See ECMA 9.4 ToInteger 600 * 601 * <p>Note that this returns {@link java.lang.Integer#MAX_VALUE} or {@link java.lang.Integer#MIN_VALUE} 602 * for double values that exceed the int range, including positive and negative Infinity. It is the 603 * caller's responsibility to handle such values correctly.</p> 604 * 605 * @param obj an object 606 * @return an integer 607 */ 608 public static int toInteger(final Object obj) { 609 return (int)toNumber(obj); 610 } 611 612 /** 613 * JavaScript compliant Object to long conversion. See ECMA 9.4 ToInteger 614 * 615 * <p>Note that this returns {@link java.lang.Long#MAX_VALUE} or {@link java.lang.Long#MIN_VALUE} 616 * for double values that exceed the long range, including positive and negative Infinity. It is the 617 * caller's responsibility to handle such values correctly.</p> 618 * 619 * @param obj an object 620 * @return a long 621 */ 622 public static long toLong(final Object obj) { 623 return (long)toNumber(obj); 624 } 625 626 /** 627 * JavaScript compliant Object to int32 conversion 628 * See ECMA 9.5 ToInt32 629 * 630 * @param obj an object 631 * @return an int32 632 */ 633 public static int toInt32(final Object obj) { 634 return toInt32(toNumber(obj)); 635 } 636 637 /** 638 * JavaScript compliant long to int32 conversion 639 * 640 * @param num a long 641 * @return an int32 642 */ 643 public static int toInt32(final long num) { 644 return (int)num; 645 } 646 647 /** 648 * JavaScript compliant number to int32 conversion 649 * 650 * @param num a number 651 * @return an int32 652 */ 653 public static int toInt32(final double num) { 654 return (int)doubleToInt32(num); 655 } 656 657 /** 658 * JavaScript compliant Object to int64 conversion 659 * 660 * @param obj an object 661 * @return an int64 662 */ 663 public static long toInt64(final Object obj) { 664 return toInt64(toNumber(obj)); 665 } 666 667 /** 668 * JavaScript compliant number to int64 conversion 669 * 670 * @param num a number 671 * @return an int64 672 */ 673 public static long toInt64(final double num) { 674 if (Double.isInfinite(num)) { 675 return 0L; 676 } 677 return (long)num; 678 } 679 680 /** 681 * JavaScript compliant Object to uint32 conversion 682 * 683 * @param obj an object 684 * @return a uint32 685 */ 686 public static long toUint32(final Object obj) { 687 return toUint32(toNumber(obj)); 688 } 689 690 /** 691 * JavaScript compliant number to uint32 conversion 692 * 693 * @param num a number 694 * @return a uint32 695 */ 696 public static long toUint32(final double num) { 697 return doubleToInt32(num) & MAX_UINT; 698 } 699 700 /** 701 * JavaScript compliant Object to uint16 conversion 702 * ECMA 9.7 ToUint16: (Unsigned 16 Bit Integer) 703 * 704 * @param obj an object 705 * @return a uint16 706 */ 707 public static int toUint16(final Object obj) { 708 return toUint16(toNumber(obj)); 709 } 710 711 /** 712 * JavaScript compliant number to uint16 conversion 713 * 714 * @param num a number 715 * @return a uint16 716 */ 717 public static int toUint16(final int num) { 718 return num & 0xffff; 719 } 720 721 /** 722 * JavaScript compliant number to uint16 conversion 723 * 724 * @param num a number 725 * @return a uint16 726 */ 727 public static int toUint16(final long num) { 728 return ((int)num) & 0xffff; 729 } 730 731 /** 732 * JavaScript compliant number to uint16 conversion 733 * 734 * @param num a number 735 * @return a uint16 736 */ 737 public static int toUint16(final double num) { 738 return ((int)doubleToInt32(num)) & 0xffff; 739 } 740 741 private static long doubleToInt32(final double num) { 742 final int exponent = Math.getExponent(num); 743 if (exponent < 31) { 744 return (long) num; // Fits into 32 bits 745 } 746 if (exponent >= 84) { 747 // Either infinite or NaN or so large that shift / modulo will produce 0 748 // (52 bit mantissa + 32 bit target width). 749 return 0; 750 } 751 // This is rather slow and could probably be sped up using bit-fiddling. 752 final double d = (num >= 0) ? Math.floor(num) : Math.ceil(num); 753 return (long)(d % INT32_LIMIT); 754 } 755 756 /** 757 * Check whether a number is finite 758 * 759 * @param num a number 760 * @return true if finite 761 */ 762 public static boolean isFinite(final double num) { 763 return !Double.isInfinite(num) && !Double.isNaN(num); 764 } 765 766 /** 767 * Convert a primitive to a double 768 * 769 * @param num a double 770 * @return a boxed double 771 */ 772 public static Double toDouble(final double num) { 773 return num; 774 } 775 776 /** 777 * Convert a primitive to a double 778 * 779 * @param num a long 780 * @return a boxed double 781 */ 782 public static Double toDouble(final long num) { 783 return (double)num; 784 } 785 786 /** 787 * Convert a primitive to a double 788 * 789 * @param num an int 790 * @return a boxed double 791 */ 792 public static Double toDouble(final int num) { 793 return (double)num; 794 } 795 796 /** 797 * Convert a boolean to an Object 798 * 799 * @param bool a boolean 800 * @return a boxed boolean, its Object representation 801 */ 802 public static Object toObject(final boolean bool) { 803 return bool; 804 } 805 806 /** 807 * Convert a number to an Object 808 * 809 * @param num an integer 810 * @return the boxed number 811 */ 812 public static Object toObject(final int num) { 813 return num; 814 } 815 816 /** 817 * Convert a number to an Object 818 * 819 * @param num a long 820 * @return the boxed number 821 */ 822 public static Object toObject(final long num) { 823 return num; 824 } 825 826 /** 827 * Convert a number to an Object 828 * 829 * @param num a double 830 * @return the boxed number 831 */ 832 public static Object toObject(final double num) { 833 return num; 834 } 835 836 /** 837 * Identity converter for objects. 838 * 839 * @param obj an object 840 * @return the boxed number 841 */ 842 public static Object toObject(final Object obj) { 843 return obj; 844 } 845 846 /** 847 * Object conversion. This is used to convert objects and numbers to their corresponding 848 * NativeObject type 849 * See ECMA 9.9 ToObject 850 * 851 * @param obj the object to convert 852 * 853 * @return the wrapped object 854 */ 855 public static Object toScriptObject(final Object obj) { 856 return toScriptObject(Context.getGlobal(), obj); 857 } 858 859 /** 860 * Object conversion. This is used to convert objects and numbers to their corresponding 861 * NativeObject type 862 * See ECMA 9.9 ToObject 863 * 864 * @param global the global object 865 * @param obj the object to convert 866 * 867 * @return the wrapped object 868 */ 869 public static Object toScriptObject(final Global global, final Object obj) { 870 if (nullOrUndefined(obj)) { 871 throw typeError(global, "not.an.object", ScriptRuntime.safeToString(obj)); 872 } 873 874 if (obj instanceof ScriptObject) { 875 return obj; 876 } 877 878 return global.wrapAsObject(obj); 879 } 880 881 /** 882 * Script object to Java array conversion. 883 * 884 * @param obj script object to be converted to Java array 885 * @param componentType component type of the destination array required 886 * @return converted Java array 887 */ 888 public static Object toJavaArray(final Object obj, final Class<?> componentType) { 889 if (obj instanceof ScriptObject) { 890 return ((ScriptObject)obj).getArray().asArrayOfType(componentType); 891 } else if (obj instanceof JSObject) { 892 final ArrayLikeIterator<?> itr = ArrayLikeIterator.arrayLikeIterator(obj); 893 final int len = (int) itr.getLength(); 894 final Object[] res = new Object[len]; 895 int idx = 0; 896 while (itr.hasNext()) { 897 res[idx++] = itr.next(); 898 } 899 return convertArray(res, componentType); 900 } else if(obj == null) { 901 return null; 902 } else { 903 throw new IllegalArgumentException("not a script object"); 904 } 905 } 906 907 /** 908 * Java array to java array conversion - but using type conversions implemented by linker. 909 * 910 * @param src source array 911 * @param componentType component type of the destination array required 912 * @return converted Java array 913 */ 914 public static Object convertArray(final Object[] src, final Class<?> componentType) { 915 if(componentType == Object.class) { 916 for(int i = 0; i < src.length; ++i) { 917 final Object e = src[i]; 918 if(e instanceof ConsString) { 919 src[i] = e.toString(); 920 } 921 } 922 } 923 924 final int l = src.length; 925 final Object dst = Array.newInstance(componentType, l); 926 final MethodHandle converter = Bootstrap.getLinkerServices().getTypeConverter(Object.class, componentType); 927 try { 928 for (int i = 0; i < src.length; i++) { 929 Array.set(dst, i, invoke(converter, src[i])); 930 } 931 } catch (final RuntimeException | Error e) { 932 throw e; 933 } catch (final Throwable t) { 934 throw new RuntimeException(t); 935 } 936 return dst; 937 } 938 939 /** 940 * Converts a JavaScript object to a Java List. See {@link ListAdapter} for details. 941 * @param obj the object to convert. Can be any array-like object. 942 * @return a List that is live-backed by the JavaScript object. 943 */ 944 public static List<?> toJavaList(final Object obj) { 945 return ListAdapter.create(obj); 946 } 947 948 /** 949 * Converts a JavaScript object to a Java Deque. See {@link ListAdapter} for details. 950 * @param obj the object to convert. Can be any array-like object. 951 * @return a Deque that is live-backed by the JavaScript object. 952 */ 953 public static Deque<?> toJavaDeque(final Object obj) { 954 return ListAdapter.create(obj); 955 } 956 957 /** 958 * Check if an object is null or undefined 959 * 960 * @param obj object to check 961 * 962 * @return true if null or undefined 963 */ 964 public static boolean nullOrUndefined(final Object obj) { 965 return obj == null || obj == ScriptRuntime.UNDEFINED; 966 } 967 968 static String toStringImpl(final Object obj, final boolean safe) { 969 if (obj instanceof String) { 970 return (String)obj; 971 } 972 973 if (obj instanceof Number) { 974 return toString(((Number)obj).doubleValue()); 975 } 976 977 if (obj == ScriptRuntime.UNDEFINED) { 978 return "undefined"; 979 } 980 981 if (obj == null) { 982 return "null"; 983 } 984 985 if (obj instanceof ScriptObject) { 986 if (safe) { 987 final ScriptObject sobj = (ScriptObject)obj; 988 final Global gobj = Context.getGlobal(); 989 return gobj.isError(sobj) ? 990 ECMAException.safeToString(sobj) : 991 sobj.safeToString(); 992 } 993 994 return toString(toPrimitive(obj, String.class)); 995 } 996 997 if (obj instanceof StaticClass) { 998 return "[JavaClass " + ((StaticClass)obj).getRepresentedClass().getName() + "]"; 999 } 1000 1001 return obj.toString(); 1002 } 1003 1004 // trim from left for JS whitespaces. 1005 static String trimLeft(final String str) { 1006 int start = 0; 1007 1008 while (start < str.length() && Lexer.isJSWhitespace(str.charAt(start))) { 1009 start++; 1010 } 1011 1012 return str.substring(start); 1013 } 1014 1015 private static double parseRadix(final char chars[], final int start, final int length, final int radix) { 1016 int pos = 0; 1017 1018 for (int i = start; i < length ; i++) { 1019 if (digit(chars[i], radix) == -1) { 1020 return Double.NaN; 1021 } 1022 pos++; 1023 } 1024 1025 if (pos == 0) { 1026 return Double.NaN; 1027 } 1028 1029 double value = 0.0; 1030 for (int i = start; i < start + pos; i++) { 1031 value *= radix; 1032 value += digit(chars[i], radix); 1033 } 1034 1035 return value; 1036 } 1037 1038 private static double toNumberGeneric(final Object obj) { 1039 if (obj == null) { 1040 return +0.0; 1041 } 1042 1043 if (obj instanceof String) { 1044 return toNumber((String)obj); 1045 } 1046 1047 if (obj instanceof ConsString) { 1048 return toNumber(obj.toString()); 1049 } 1050 1051 if (obj instanceof Boolean) { 1052 return (Boolean)obj ? 1 : +0.0; 1053 } 1054 1055 if (obj instanceof ScriptObject) { 1056 return toNumber((ScriptObject)obj); 1057 } 1058 1059 if (obj instanceof JSObject) { 1060 return ((JSObject)obj).toNumber(); 1061 } 1062 1063 return Double.NaN; 1064 } 1065 1066 private static Object invoke(final MethodHandle mh, final Object arg) { 1067 try { 1068 return mh.invoke(arg); 1069 } catch (final RuntimeException | Error e) { 1070 throw e; 1071 } catch (final Throwable t) { 1072 throw new RuntimeException(t); 1073 } 1074 } 1075 }