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