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.objects; 27 28 import static jdk.nashorn.internal.lookup.Lookup.MH; 29 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; 30 import static jdk.nashorn.internal.runtime.JSType.isRepresentableAsInt; 31 import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; 32 33 import java.lang.invoke.MethodHandle; 34 import java.lang.invoke.MethodHandles; 35 import java.lang.invoke.MethodType; 36 import java.text.Collator; 37 import java.util.ArrayList; 38 import java.util.Arrays; 39 import java.util.LinkedList; 40 import java.util.List; 41 import java.util.Locale; 42 import jdk.internal.dynalink.CallSiteDescriptor; 43 import jdk.internal.dynalink.linker.GuardedInvocation; 44 import jdk.internal.dynalink.linker.LinkRequest; 45 import jdk.nashorn.internal.lookup.MethodHandleFactory.LookupException; 46 import jdk.nashorn.internal.objects.annotations.Attribute; 47 import jdk.nashorn.internal.objects.annotations.Constructor; 48 import jdk.nashorn.internal.objects.annotations.Function; 49 import jdk.nashorn.internal.objects.annotations.Getter; 50 import jdk.nashorn.internal.objects.annotations.ScriptClass; 51 import jdk.nashorn.internal.objects.annotations.SpecializedConstructor; 52 import jdk.nashorn.internal.objects.annotations.SpecializedFunction; 53 import jdk.nashorn.internal.objects.annotations.Where; 54 import jdk.nashorn.internal.runtime.ConsString; 55 import jdk.nashorn.internal.runtime.JSType; 56 import jdk.nashorn.internal.runtime.PropertyMap; 57 import jdk.nashorn.internal.runtime.ScriptFunction; 58 import jdk.nashorn.internal.runtime.ScriptObject; 59 import jdk.nashorn.internal.runtime.ScriptRuntime; 60 import jdk.nashorn.internal.runtime.arrays.ArrayIndex; 61 import jdk.nashorn.internal.runtime.linker.NashornGuards; 62 import jdk.nashorn.internal.runtime.linker.PrimitiveLookup; 63 64 65 /** 66 * ECMA 15.5 String Objects. 67 */ 68 @ScriptClass("String") 69 public final class NativeString extends ScriptObject { 70 71 private final CharSequence value; 72 73 // Method handle to create an object wrapper for a primitive string 74 private static final MethodHandle WRAPFILTER = findOwnMH("wrapFilter", MH.type(NativeString.class, Object.class)); 75 // Method handle to retrieve the String prototype object 76 private static final MethodHandle PROTOFILTER = findOwnMH("protoFilter", MH.type(Object.class, Object.class)); 77 78 // initialized by nasgen 79 private static PropertyMap $nasgenmap$; 80 81 static PropertyMap getInitialMap() { 82 return $nasgenmap$; 83 } 84 85 private NativeString(final CharSequence value) { 86 this(value, Global.instance()); 87 } 88 89 NativeString(final CharSequence value, final Global global) { 90 this(value, global.getStringPrototype(), getInitialMap()); 91 } 92 93 private NativeString(final CharSequence value, final ScriptObject proto, final PropertyMap map) { 94 super(proto, map); 95 assert value instanceof String || value instanceof ConsString; 96 this.value = value; 97 } 98 99 @Override 100 public String safeToString() { 101 return "[String " + toString() + "]"; 102 } 103 104 @Override 105 public String toString() { 106 return getStringValue(); 107 } 108 109 @Override 110 public boolean equals(final Object other) { 111 if (other instanceof NativeString) { 112 return getStringValue().equals(((NativeString) other).getStringValue()); 113 } 114 115 return false; 116 } 117 118 @Override 119 public int hashCode() { 120 return getStringValue().hashCode(); 121 } 122 123 private String getStringValue() { 124 return value instanceof String ? (String) value : value.toString(); 125 } 126 127 private CharSequence getValue() { 128 return value; 129 } 130 131 @Override 132 public String getClassName() { 133 return "String"; 134 } 135 136 @Override 137 public Object getLength() { 138 return value.length(); 139 } 140 141 // This is to support length as method call as well. 142 @Override 143 protected GuardedInvocation findGetMethod(final CallSiteDescriptor desc, final LinkRequest request, final String operator) { 144 final String name = desc.getNameToken(2); 145 146 // if str.length(), then let the bean linker handle it 147 if ("length".equals(name) && "getMethod".equals(operator)) { 148 return null; 149 } 150 151 return super.findGetMethod(desc, request, operator); 152 } 153 154 // This is to provide array-like access to string characters without creating a NativeString wrapper. 155 @Override 156 protected GuardedInvocation findGetIndexMethod(final CallSiteDescriptor desc, final LinkRequest request) { 157 final Object self = request.getReceiver(); 158 final Class<?> returnType = desc.getMethodType().returnType(); 159 160 if (returnType == Object.class && (self instanceof String || self instanceof ConsString)) { 161 try { 162 MethodHandle mh = MH.findStatic(MethodHandles.lookup(), NativeString.class, "get", desc.getMethodType()); 163 return new GuardedInvocation(mh, NashornGuards.getInstanceOf2Guard(String.class, ConsString.class)); 164 } catch (final LookupException e) { 165 // Shouldn't happen. Fall back to super 166 } 167 } 168 return super.findGetIndexMethod(desc, request); 169 } 170 171 @SuppressWarnings("unused") 172 private static Object get(final Object self, final Object key) { 173 final CharSequence cs = JSType.toCharSequence(self); 174 final Object primitiveKey = JSType.toPrimitive(key, String.class); 175 final int index = ArrayIndex.getArrayIndex(primitiveKey); 176 if (index >= 0 && index < cs.length()) { 177 return String.valueOf(cs.charAt(index)); 178 } 179 return ((ScriptObject) Global.toObject(self)).get(primitiveKey); 180 } 181 182 @SuppressWarnings("unused") 183 private static Object get(final Object self, final double key) { 184 if (isRepresentableAsInt(key)) { 185 return get(self, (int)key); 186 } 187 return ((ScriptObject) Global.toObject(self)).get(key); 188 } 189 190 @SuppressWarnings("unused") 191 private static Object get(final Object self, final long key) { 192 final CharSequence cs = JSType.toCharSequence(self); 193 if (key >= 0 && key < cs.length()) { 194 return String.valueOf(cs.charAt((int)key)); 195 } 196 return ((ScriptObject) Global.toObject(self)).get(key); 197 } 198 199 private static Object get(final Object self, final int key) { 200 final CharSequence cs = JSType.toCharSequence(self); 201 if (key >= 0 && key < cs.length()) { 202 return String.valueOf(cs.charAt(key)); 203 } 204 return ((ScriptObject) Global.toObject(self)).get(key); 205 } 206 207 // String characters can be accessed with array-like indexing.. 208 @Override 209 public Object get(final Object key) { 210 final Object primitiveKey = JSType.toPrimitive(key, String.class); 211 final int index = ArrayIndex.getArrayIndex(primitiveKey); 212 if (index >= 0 && index < value.length()) { 213 return String.valueOf(value.charAt(index)); 214 } 215 return super.get(primitiveKey); 216 } 217 218 @Override 219 public Object get(final double key) { 220 if (isRepresentableAsInt(key)) { 221 return get((int)key); 222 } 223 return super.get(key); 224 } 225 226 @Override 227 public Object get(final long key) { 228 if (key >= 0 && key < value.length()) { 229 return String.valueOf(value.charAt((int)key)); 230 } 231 return super.get(key); 232 } 233 234 @Override 235 public Object get(final int key) { 236 if (key >= 0 && key < value.length()) { 237 return String.valueOf(value.charAt(key)); 238 } 239 return super.get(key); 240 } 241 242 @Override 243 public int getInt(final Object key) { 244 return JSType.toInt32(get(key)); 245 } 246 247 @Override 248 public int getInt(final double key) { 249 return JSType.toInt32(get(key)); 250 } 251 252 @Override 253 public int getInt(final long key) { 254 return JSType.toInt32(get(key)); 255 } 256 257 @Override 258 public int getInt(final int key) { 259 return JSType.toInt32(get(key)); 260 } 261 262 @Override 263 public long getLong(final Object key) { 264 return JSType.toUint32(get(key)); 265 } 266 267 @Override 268 public long getLong(final double key) { 269 return JSType.toUint32(get(key)); 270 } 271 272 @Override 273 public long getLong(final long key) { 274 return JSType.toUint32(get(key)); 275 } 276 277 @Override 278 public long getLong(final int key) { 279 return JSType.toUint32(get(key)); 280 } 281 282 @Override 283 public double getDouble(final Object key) { 284 return JSType.toNumber(get(key)); 285 } 286 287 @Override 288 public double getDouble(final double key) { 289 return JSType.toNumber(get(key)); 290 } 291 292 @Override 293 public double getDouble(final long key) { 294 return JSType.toNumber(get(key)); 295 } 296 297 @Override 298 public double getDouble(final int key) { 299 return JSType.toNumber(get(key)); 300 } 301 302 @Override 303 public boolean has(final Object key) { 304 final Object primitiveKey = JSType.toPrimitive(key, String.class); 305 final int index = ArrayIndex.getArrayIndex(primitiveKey); 306 return isValid(index) || super.has(primitiveKey); 307 } 308 309 @Override 310 public boolean has(final int key) { 311 return isValid(key) || super.has(key); 312 } 313 314 @Override 315 public boolean has(final long key) { 316 final int index = ArrayIndex.getArrayIndex(key); 317 return isValid(index) || super.has(key); 318 } 319 320 @Override 321 public boolean has(final double key) { 322 final int index = ArrayIndex.getArrayIndex(key); 323 return isValid(index) || super.has(key); 324 } 325 326 @Override 327 public boolean hasOwnProperty(final Object key) { 328 final Object primitiveKey = JSType.toPrimitive(key, String.class); 329 final int index = ArrayIndex.getArrayIndex(primitiveKey); 330 return isValid(index) || super.hasOwnProperty(primitiveKey); 331 } 332 333 @Override 334 public boolean hasOwnProperty(final int key) { 335 return isValid(key) || super.hasOwnProperty(key); 336 } 337 338 @Override 339 public boolean hasOwnProperty(final long key) { 340 final int index = ArrayIndex.getArrayIndex(key); 341 return isValid(index) || super.hasOwnProperty(key); 342 } 343 344 @Override 345 public boolean hasOwnProperty(final double key) { 346 final int index = ArrayIndex.getArrayIndex(key); 347 return isValid(index) || super.hasOwnProperty(key); 348 } 349 350 @Override 351 public boolean delete(final int key, final boolean strict) { 352 return checkDeleteIndex(key, strict)? false : super.delete(key, strict); 353 } 354 355 @Override 356 public boolean delete(final long key, final boolean strict) { 357 final int index = ArrayIndex.getArrayIndex(key); 358 return checkDeleteIndex(index, strict)? false : super.delete(key, strict); 359 } 360 361 @Override 362 public boolean delete(final double key, final boolean strict) { 363 final int index = ArrayIndex.getArrayIndex(key); 364 return checkDeleteIndex(index, strict)? false : super.delete(key, strict); 365 } 366 367 @Override 368 public boolean delete(final Object key, final boolean strict) { 369 final Object primitiveKey = JSType.toPrimitive(key, String.class); 370 final int index = ArrayIndex.getArrayIndex(primitiveKey); 371 return checkDeleteIndex(index, strict)? false : super.delete(primitiveKey, strict); 372 } 373 374 private boolean checkDeleteIndex(final int index, final boolean strict) { 375 if (isValid(index)) { 376 if (strict) { 377 throw typeError("cant.delete.property", Integer.toString(index), ScriptRuntime.safeToString(this)); 378 } 379 return true; 380 } 381 382 return false; 383 } 384 385 @Override 386 public Object getOwnPropertyDescriptor(final String key) { 387 final int index = ArrayIndex.getArrayIndex(key); 388 if (index >= 0 && index < value.length()) { 389 final Global global = Global.instance(); 390 return global.newDataDescriptor(String.valueOf(value.charAt(index)), false, true, false); 391 } 392 393 return super.getOwnPropertyDescriptor(key); 394 } 395 396 /** 397 * return a List of own keys associated with the object. 398 * @param all True if to include non-enumerable keys. 399 * @return Array of keys. 400 */ 401 @Override 402 public String[] getOwnKeys(final boolean all) { 403 final List<Object> keys = new ArrayList<>(); 404 405 // add string index keys 406 for (int i = 0; i < value.length(); i++) { 407 keys.add(JSType.toString(i)); 408 } 409 410 // add super class properties 411 keys.addAll(Arrays.asList(super.getOwnKeys(all))); 412 return keys.toArray(new String[keys.size()]); 413 } 414 415 /** 416 * ECMA 15.5.3 String.length 417 * @param self self reference 418 * @return value of length property for string 419 */ 420 @Getter(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_WRITABLE | Attribute.NOT_CONFIGURABLE) 421 public static Object length(final Object self) { 422 return getCharSequence(self).length(); 423 } 424 425 /** 426 * ECMA 15.5.3.2 String.fromCharCode ( [ char0 [ , char1 [ , ... ] ] ] ) 427 * @param self self reference 428 * @param args array of arguments to be interpreted as char 429 * @return string with arguments translated to charcodes 430 */ 431 @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1, where = Where.CONSTRUCTOR) 432 public static Object fromCharCode(final Object self, final Object... args) { 433 final char[] buf = new char[args.length]; 434 int index = 0; 435 for (final Object arg : args) { 436 buf[index++] = (char)JSType.toUint16(arg); 437 } 438 return new String(buf); 439 } 440 441 /** 442 * ECMA 15.5.3.2 - specialization for one char 443 * @param self self reference 444 * @param value one argument to be interpreted as char 445 * @return string with one charcode 446 */ 447 @SpecializedFunction 448 public static Object fromCharCode(final Object self, final Object value) { 449 try { 450 return "" + (char)JSType.toUint16(((Number)value).doubleValue()); 451 } catch (final ClassCastException e) { 452 return fromCharCode(self, new Object[] { value }); 453 } 454 } 455 456 /** 457 * ECMA 15.5.3.2 - specialization for one char of int type 458 * @param self self reference 459 * @param value one argument to be interpreted as char 460 * @return string with one charcode 461 */ 462 @SpecializedFunction 463 public static Object fromCharCode(final Object self, final int value) { 464 return "" + (char)(value & 0xffff); 465 } 466 467 /** 468 * ECMA 15.5.3.2 - specialization for one char of long type 469 * @param self self reference 470 * @param value one argument to be interpreted as char 471 * @return string with one charcode 472 */ 473 @SpecializedFunction 474 public static Object fromCharCode(final Object self, final long value) { 475 return "" + (char)((int)value & 0xffff); 476 } 477 478 /** 479 * ECMA 15.5.3.2 - specialization for one char of double type 480 * @param self self reference 481 * @param value one argument to be interpreted as char 482 * @return string with one charcode 483 */ 484 @SpecializedFunction 485 public static Object fromCharCode(final Object self, final double value) { 486 return "" + (char)JSType.toUint16(value); 487 } 488 489 /** 490 * ECMA 15.5.4.2 String.prototype.toString ( ) 491 * @param self self reference 492 * @return self as string 493 */ 494 @Function(attributes = Attribute.NOT_ENUMERABLE) 495 public static Object toString(final Object self) { 496 return getString(self); 497 } 498 499 /** 500 * ECMA 15.5.4.3 String.prototype.valueOf ( ) 501 * @param self self reference 502 * @return self as string 503 */ 504 @Function(attributes = Attribute.NOT_ENUMERABLE) 505 public static Object valueOf(final Object self) { 506 return getString(self); 507 } 508 509 /** 510 * ECMA 15.5.4.4 String.prototype.charAt (pos) 511 * @param self self reference 512 * @param pos position in string 513 * @return string representing the char at the given position 514 */ 515 @Function(attributes = Attribute.NOT_ENUMERABLE) 516 public static Object charAt(final Object self, final Object pos) { 517 return charAtImpl(checkObjectToString(self), JSType.toInteger(pos)); 518 } 519 520 /** 521 * ECMA 15.5.4.4 String.prototype.charAt (pos) - specialized version for double position 522 * @param self self reference 523 * @param pos position in string 524 * @return string representing the char at the given position 525 */ 526 @SpecializedFunction 527 public static String charAt(final Object self, final double pos) { 528 return charAt(self, (int)pos); 529 } 530 531 /** 532 * ECMA 15.5.4.4 String.prototype.charAt (pos) - specialized version for int position 533 * @param self self reference 534 * @param pos position in string 535 * @return string representing the char at the given position 536 */ 537 @SpecializedFunction 538 public static String charAt(final Object self, final int pos) { 539 return charAtImpl(checkObjectToString(self), pos); 540 } 541 542 private static String charAtImpl(final String str, final int pos) { 543 return (pos < 0 || pos >= str.length()) ? "" : String.valueOf(str.charAt(pos)); 544 } 545 546 /** 547 * ECMA 15.5.4.5 String.prototype.charCodeAt (pos) 548 * @param self self reference 549 * @param pos position in string 550 * @return number representing charcode at position 551 */ 552 @Function(attributes = Attribute.NOT_ENUMERABLE) 553 public static Object charCodeAt(final Object self, final Object pos) { 554 return charCodeAtImpl(checkObjectToString(self), JSType.toInteger(pos)); 555 } 556 557 /** 558 * ECMA 15.5.4.5 String.prototype.charCodeAt (pos) - specialized version for double position 559 * @param self self reference 560 * @param pos position in string 561 * @return number representing charcode at position 562 */ 563 @SpecializedFunction 564 public static double charCodeAt(final Object self, final double pos) { 565 return charCodeAt(self, (int) pos); 566 } 567 568 /** 569 * ECMA 15.5.4.5 String.prototype.charCodeAt (pos) - specialized version for int position 570 * @param self self reference 571 * @param pos position in string 572 * @return number representing charcode at position 573 */ 574 @SpecializedFunction 575 public static double charCodeAt(final Object self, final int pos) { 576 return charCodeAtImpl(checkObjectToString(self), pos); 577 } 578 579 private static double charCodeAtImpl(final String str, final int pos) { 580 return (pos < 0 || pos >= str.length()) ? Double.NaN : str.charAt(pos); 581 } 582 583 /** 584 * ECMA 15.5.4.6 String.prototype.concat ( [ string1 [ , string2 [ , ... ] ] ] ) 585 * @param self self reference 586 * @param args list of string to concatenate 587 * @return concatenated string 588 */ 589 @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) 590 public static Object concat(final Object self, final Object... args) { 591 CharSequence cs = checkObjectToString(self); 592 if (args != null) { 593 for (final Object obj : args) { 594 cs = new ConsString(cs, JSType.toCharSequence(obj)); 595 } 596 } 597 return cs; 598 } 599 600 /** 601 * ECMA 15.5.4.7 String.prototype.indexOf (searchString, position) 602 * @param self self reference 603 * @param search string to search for 604 * @param pos position to start search 605 * @return position of first match or -1 606 */ 607 @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) 608 public static Object indexOf(final Object self, final Object search, final Object pos) { 609 final String str = checkObjectToString(self); 610 return str.indexOf(JSType.toString(search), JSType.toInteger(pos)); 611 } 612 613 /** 614 * ECMA 15.5.4.7 String.prototype.indexOf (searchString, position) specialized for no position parameter 615 * @param self self reference 616 * @param search string to search for 617 * @return position of first match or -1 618 */ 619 @SpecializedFunction 620 public static int indexOf(final Object self, final Object search) { 621 return indexOf(self, search, 0); 622 } 623 624 /** 625 * ECMA 15.5.4.7 String.prototype.indexOf (searchString, position) specialized for double position parameter 626 * @param self self reference 627 * @param search string to search for 628 * @param pos position to start search 629 * @return position of first match or -1 630 */ 631 @SpecializedFunction 632 public static int indexOf(final Object self, final Object search, final double pos) { 633 return indexOf(self, search, (int) pos); 634 } 635 636 /** 637 * ECMA 15.5.4.7 String.prototype.indexOf (searchString, position) specialized for int position parameter 638 * @param self self reference 639 * @param search string to search for 640 * @param pos position to start search 641 * @return position of first match or -1 642 */ 643 @SpecializedFunction 644 public static int indexOf(final Object self, final Object search, final int pos) { 645 return checkObjectToString(self).indexOf(JSType.toString(search), pos); 646 } 647 648 /** 649 * ECMA 15.5.4.8 String.prototype.lastIndexOf (searchString, position) 650 * @param self self reference 651 * @param search string to search for 652 * @param pos position to start search 653 * @return last position of match or -1 654 */ 655 @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) 656 public static Object lastIndexOf(final Object self, final Object search, final Object pos) { 657 658 final String str = checkObjectToString(self); 659 final String searchStr = JSType.toString(search); 660 final int length = str.length(); 661 662 int end; 663 664 if (pos == UNDEFINED) { 665 end = length; 666 } else { 667 final double numPos = JSType.toNumber(pos); 668 end = Double.isNaN(numPos) ? length : (int)numPos; 669 if (end < 0) { 670 end = 0; 671 } else if (end > length) { 672 end = length; 673 } 674 } 675 676 677 return str.lastIndexOf(searchStr, end); 678 } 679 680 /** 681 * ECMA 15.5.4.9 String.prototype.localeCompare (that) 682 * @param self self reference 683 * @param that comparison object 684 * @return result of locale sensitive comparison operation between {@code self} and {@code that} 685 */ 686 @Function(attributes = Attribute.NOT_ENUMERABLE) 687 public static Object localeCompare(final Object self, final Object that) { 688 689 final String str = checkObjectToString(self); 690 final Collator collator = Collator.getInstance(Global.getEnv()._locale); 691 692 collator.setStrength(Collator.IDENTICAL); 693 collator.setDecomposition(Collator.CANONICAL_DECOMPOSITION); 694 695 return (double)collator.compare(str, JSType.toString(that)); 696 } 697 698 /** 699 * ECMA 15.5.4.10 String.prototype.match (regexp) 700 * @param self self reference 701 * @param regexp regexp expression 702 * @return array of regexp matches 703 */ 704 @Function(attributes = Attribute.NOT_ENUMERABLE) 705 public static Object match(final Object self, final Object regexp) { 706 707 final String str = checkObjectToString(self); 708 709 NativeRegExp nativeRegExp; 710 if (regexp == UNDEFINED) { 711 nativeRegExp = new NativeRegExp(""); 712 } else { 713 nativeRegExp = Global.toRegExp(regexp); 714 } 715 716 if (!nativeRegExp.getGlobal()) { 717 return nativeRegExp.exec(str); 718 } 719 720 nativeRegExp.setLastIndex(0); 721 722 int previousLastIndex = 0; 723 final List<Object> matches = new ArrayList<>(); 724 725 Object result; 726 while ((result = nativeRegExp.exec(str)) != null) { 727 final int thisIndex = nativeRegExp.getLastIndex(); 728 if (thisIndex == previousLastIndex) { 729 nativeRegExp.setLastIndex(thisIndex + 1); 730 previousLastIndex = thisIndex + 1; 731 } else { 732 previousLastIndex = thisIndex; 733 } 734 matches.add(((ScriptObject)result).get(0)); 735 } 736 737 if (matches.isEmpty()) { 738 return null; 739 } 740 741 return new NativeArray(matches.toArray()); 742 } 743 744 /** 745 * ECMA 15.5.4.11 String.prototype.replace (searchValue, replaceValue) 746 * @param self self reference 747 * @param string item to replace 748 * @param replacement item to replace it with 749 * @return string after replacement 750 */ 751 @Function(attributes = Attribute.NOT_ENUMERABLE) 752 public static Object replace(final Object self, final Object string, final Object replacement) { 753 754 final String str = checkObjectToString(self); 755 756 final NativeRegExp nativeRegExp; 757 if (string instanceof NativeRegExp) { 758 nativeRegExp = (NativeRegExp) string; 759 } else { 760 nativeRegExp = NativeRegExp.flatRegExp(JSType.toString(string)); 761 } 762 763 if (replacement instanceof ScriptFunction) { 764 return nativeRegExp.replace(str, "", (ScriptFunction)replacement); 765 } 766 767 return nativeRegExp.replace(str, JSType.toString(replacement), null); 768 } 769 770 /** 771 * ECMA 15.5.4.12 String.prototype.search (regexp) 772 * 773 * @param self self reference 774 * @param string string to search for 775 * @return offset where match occurred 776 */ 777 @Function(attributes = Attribute.NOT_ENUMERABLE) 778 public static Object search(final Object self, final Object string) { 779 780 final String str = checkObjectToString(self); 781 final NativeRegExp nativeRegExp = Global.toRegExp(string == UNDEFINED ? "" : string); 782 783 return nativeRegExp.search(str); 784 } 785 786 /** 787 * ECMA 15.5.4.13 String.prototype.slice (start, end) 788 * 789 * @param self self reference 790 * @param start start position for slice 791 * @param end end position for slice 792 * @return sliced out substring 793 */ 794 @Function(attributes = Attribute.NOT_ENUMERABLE) 795 public static Object slice(final Object self, final Object start, final Object end) { 796 797 final String str = checkObjectToString(self); 798 if (end == UNDEFINED) { 799 return slice(str, JSType.toInteger(start)); 800 } 801 return slice(str, JSType.toInteger(start), JSType.toInteger(end)); 802 } 803 804 /** 805 * ECMA 15.5.4.13 String.prototype.slice (start, end) specialized for single int parameter 806 * 807 * @param self self reference 808 * @param start start position for slice 809 * @return sliced out substring 810 */ 811 @SpecializedFunction 812 public static Object slice(final Object self, final int start) { 813 final String str = checkObjectToString(self); 814 final int from = (start < 0) ? Math.max(str.length() + start, 0) : Math.min(start, str.length()); 815 816 return str.substring(from); 817 } 818 819 /** 820 * ECMA 15.5.4.13 String.prototype.slice (start, end) specialized for single double parameter 821 * 822 * @param self self reference 823 * @param start start position for slice 824 * @return sliced out substring 825 */ 826 @SpecializedFunction 827 public static Object slice(final Object self, final double start) { 828 return slice(self, (int)start); 829 } 830 831 /** 832 * ECMA 15.5.4.13 String.prototype.slice (start, end) specialized for two int parameters 833 * 834 * @param self self reference 835 * @param start start position for slice 836 * @param end end position for slice 837 * @return sliced out substring 838 */ 839 @SpecializedFunction 840 public static Object slice(final Object self, final int start, final int end) { 841 842 final String str = checkObjectToString(self); 843 final int len = str.length(); 844 845 final int from = (start < 0) ? Math.max(len + start, 0) : Math.min(start, len); 846 final int to = (end < 0) ? Math.max(len + end, 0) : Math.min(end, len); 847 848 return str.substring(Math.min(from, to), to); 849 } 850 851 /** 852 * ECMA 15.5.4.13 String.prototype.slice (start, end) specialized for two double parameters 853 * 854 * @param self self reference 855 * @param start start position for slice 856 * @param end end position for slice 857 * @return sliced out substring 858 */ 859 @SpecializedFunction 860 public static Object slice(final Object self, final double start, final double end) { 861 return slice(self, (int)start, (int)end); 862 } 863 864 /** 865 * ECMA 15.5.4.14 String.prototype.split (separator, limit) 866 * 867 * @param self self reference 868 * @param separator separator for split 869 * @param limit limit for splits 870 * @return array object in which splits have been placed 871 */ 872 @Function(attributes = Attribute.NOT_ENUMERABLE) 873 public static Object split(final Object self, final Object separator, final Object limit) { 874 final String str = checkObjectToString(self); 875 final long lim = (limit == UNDEFINED) ? JSType.MAX_UINT : JSType.toUint32(limit); 876 877 if (separator == UNDEFINED) { 878 return lim == 0 ? new NativeArray() : new NativeArray(new Object[]{str}); 879 } 880 881 if (separator instanceof NativeRegExp) { 882 return ((NativeRegExp) separator).split(str, lim); 883 } 884 885 // when separator is a string, it is treated as a literal search string to be used for splitting. 886 return splitString(str, JSType.toString(separator), lim); 887 } 888 889 private static Object splitString(String str, String separator, long limit) { 890 if (separator.isEmpty()) { 891 final int length = (int) Math.min(str.length(), limit); 892 final Object[] array = new Object[length]; 893 for (int i = 0; i < length; i++) { 894 array[i] = String.valueOf(str.charAt(i)); 895 } 896 return new NativeArray(array); 897 } 898 899 final List<String> elements = new LinkedList<>(); 900 final int strLength = str.length(); 901 final int sepLength = separator.length(); 902 int pos = 0; 903 int n = 0; 904 905 while (pos < strLength && n < limit) { 906 int found = str.indexOf(separator, pos); 907 if (found == -1) { 908 break; 909 } 910 elements.add(str.substring(pos, found)); 911 n++; 912 pos = found + sepLength; 913 } 914 if (pos <= strLength && n < limit) { 915 elements.add(str.substring(pos)); 916 } 917 918 return new NativeArray(elements.toArray()); 919 } 920 921 /** 922 * ECMA B.2.3 String.prototype.substr (start, length) 923 * 924 * @param self self reference 925 * @param start start position 926 * @param length length of section 927 * @return substring given start and length of section 928 */ 929 @Function(attributes = Attribute.NOT_ENUMERABLE) 930 public static Object substr(final Object self, final Object start, final Object length) { 931 final String str = JSType.toString(self); 932 final int strLength = str.length(); 933 934 int intStart = JSType.toInteger(start); 935 if (intStart < 0) { 936 intStart = Math.max(intStart + strLength, 0); 937 } 938 939 final int intLen = Math.min(Math.max((length == UNDEFINED) ? Integer.MAX_VALUE : JSType.toInteger(length), 0), strLength - intStart); 940 941 return intLen <= 0 ? "" : str.substring(intStart, intStart + intLen); 942 } 943 944 /** 945 * ECMA 15.5.4.15 String.prototype.substring (start, end) 946 * 947 * @param self self reference 948 * @param start start position of substring 949 * @param end end position of substring 950 * @return substring given start and end indexes 951 */ 952 @Function(attributes = Attribute.NOT_ENUMERABLE) 953 public static Object substring(final Object self, final Object start, final Object end) { 954 955 final String str = checkObjectToString(self); 956 if (end == UNDEFINED) { 957 return substring(str, JSType.toInteger(start)); 958 } 959 return substring(str, JSType.toInteger(start), JSType.toInteger(end)); 960 } 961 962 /** 963 * ECMA 15.5.4.15 String.prototype.substring (start, end) specialized for int start parameter 964 * 965 * @param self self reference 966 * @param start start position of substring 967 * @return substring given start and end indexes 968 */ 969 @SpecializedFunction 970 public static String substring(final Object self, final int start) { 971 final String str = checkObjectToString(self); 972 if (start < 0) { 973 return str; 974 } else if (start >= str.length()) { 975 return ""; 976 } else { 977 return str.substring(start); 978 } 979 } 980 981 /** 982 * ECMA 15.5.4.15 String.prototype.substring (start, end) specialized for double start parameter 983 * 984 * @param self self reference 985 * @param start start position of substring 986 * @return substring given start and end indexes 987 */ 988 @SpecializedFunction 989 public static String substring(final Object self, final double start) { 990 return substring(self, (int)start); 991 } 992 993 /** 994 * ECMA 15.5.4.15 String.prototype.substring (start, end) specialized for int start and end parameters 995 * 996 * @param self self reference 997 * @param start start position of substring 998 * @param end end position of substring 999 * @return substring given start and end indexes 1000 */ 1001 @SpecializedFunction 1002 public static String substring(final Object self, final int start, final int end) { 1003 final String str = checkObjectToString(self); 1004 final int len = str.length(); 1005 final int validStart = start < 0 ? 0 : (start > len ? len : start); 1006 final int validEnd = end < 0 ? 0 : (end > len ? len : end); 1007 1008 if (validStart < validEnd) { 1009 return str.substring(validStart, validEnd); 1010 } 1011 return str.substring(validEnd, validStart); 1012 } 1013 1014 /** 1015 * ECMA 15.5.4.15 String.prototype.substring (start, end) specialized for double start and end parameters 1016 * 1017 * @param self self reference 1018 * @param start start position of substring 1019 * @param end end position of substring 1020 * @return substring given start and end indexes 1021 */ 1022 @SpecializedFunction 1023 public static String substring(final Object self, final double start, final double end) { 1024 return substring(self, (int)start, (int)end); 1025 } 1026 1027 /** 1028 * ECMA 15.5.4.16 String.prototype.toLowerCase ( ) 1029 * @param self self reference 1030 * @return string to lower case 1031 */ 1032 @Function(attributes = Attribute.NOT_ENUMERABLE) 1033 public static Object toLowerCase(final Object self) { 1034 return checkObjectToString(self).toLowerCase(Locale.ROOT); 1035 } 1036 1037 /** 1038 * ECMA 15.5.4.17 String.prototype.toLocaleLowerCase ( ) 1039 * @param self self reference 1040 * @return string to locale sensitive lower case 1041 */ 1042 @Function(attributes = Attribute.NOT_ENUMERABLE) 1043 public static Object toLocaleLowerCase(final Object self) { 1044 return checkObjectToString(self).toLowerCase(Global.getEnv()._locale); 1045 } 1046 1047 /** 1048 * ECMA 15.5.4.18 String.prototype.toUpperCase ( ) 1049 * @param self self reference 1050 * @return string to upper case 1051 */ 1052 @Function(attributes = Attribute.NOT_ENUMERABLE) 1053 public static Object toUpperCase(final Object self) { 1054 return checkObjectToString(self).toUpperCase(Locale.ROOT); 1055 } 1056 1057 /** 1058 * ECMA 15.5.4.19 String.prototype.toLocaleUpperCase ( ) 1059 * @param self self reference 1060 * @return string to locale sensitive upper case 1061 */ 1062 @Function(attributes = Attribute.NOT_ENUMERABLE) 1063 public static Object toLocaleUpperCase(final Object self) { 1064 return checkObjectToString(self).toUpperCase(Global.getEnv()._locale); 1065 } 1066 1067 /** 1068 * ECMA 15.5.4.20 String.prototype.trim ( ) 1069 * @param self self reference 1070 * @return string trimmed from whitespace 1071 */ 1072 @Function(attributes = Attribute.NOT_ENUMERABLE) 1073 public static Object trim(final Object self) { 1074 1075 final String str = checkObjectToString(self); 1076 int start = 0; 1077 int end = str.length() - 1; 1078 1079 while (start <= end && ScriptRuntime.isJSWhitespace(str.charAt(start))) { 1080 start++; 1081 } 1082 while (end > start && ScriptRuntime.isJSWhitespace(str.charAt(end))) { 1083 end--; 1084 } 1085 1086 return str.substring(start, end + 1); 1087 } 1088 1089 /** 1090 * Nashorn extension: String.prototype.trimLeft ( ) 1091 * @param self self reference 1092 * @return string trimmed left from whitespace 1093 */ 1094 @Function(attributes = Attribute.NOT_ENUMERABLE) 1095 public static Object trimLeft(final Object self) { 1096 1097 final String str = checkObjectToString(self); 1098 int start = 0; 1099 int end = str.length() - 1; 1100 1101 while (start <= end && ScriptRuntime.isJSWhitespace(str.charAt(start))) { 1102 start++; 1103 } 1104 1105 return str.substring(start, end + 1); 1106 } 1107 1108 /** 1109 * Nashorn extension: String.prototype.trimRight ( ) 1110 * @param self self reference 1111 * @return string trimmed right from whitespace 1112 */ 1113 @Function(attributes = Attribute.NOT_ENUMERABLE) 1114 public static Object trimRight(final Object self) { 1115 1116 final String str = checkObjectToString(self); 1117 int start = 0; 1118 int end = str.length() - 1; 1119 1120 while (end >= start && ScriptRuntime.isJSWhitespace(str.charAt(end))) { 1121 end--; 1122 } 1123 1124 return str.substring(start, end + 1); 1125 } 1126 1127 private static Object newObj(final Object self, final CharSequence str) { 1128 return new NativeString(str); 1129 } 1130 1131 /** 1132 * ECMA 15.5.2.1 new String ( [ value ] ) 1133 * 1134 * Constructor 1135 * 1136 * @param newObj is this constructor invoked with the new operator 1137 * @param self self reference 1138 * @param args arguments (a value) 1139 * 1140 * @return new NativeString, empty string if no args, extraneous args ignored 1141 */ 1142 @Constructor(arity = 1) 1143 public static Object constructor(final boolean newObj, final Object self, final Object... args) { 1144 final CharSequence str = (args.length > 0) ? JSType.toCharSequence(args[0]) : ""; 1145 return newObj ? newObj(self, str) : str.toString(); 1146 } 1147 1148 /** 1149 * ECMA 15.5.2.1 new String ( [ value ] ) - special version with no args 1150 * 1151 * Constructor 1152 * 1153 * @param newObj is this constructor invoked with the new operator 1154 * @param self self reference 1155 * 1156 * @return new NativeString ("") 1157 */ 1158 @SpecializedConstructor 1159 public static Object constructor(final boolean newObj, final Object self) { 1160 return newObj ? newObj(self, "") : ""; 1161 } 1162 1163 /** 1164 * ECMA 15.5.2.1 new String ( [ value ] ) - special version with one arg 1165 * 1166 * Constructor 1167 * 1168 * @param newObj is this constructor invoked with the new operator 1169 * @param self self reference 1170 * @param arg argument 1171 * 1172 * @return new NativeString (arg) 1173 */ 1174 @SpecializedConstructor 1175 public static Object constructor(final boolean newObj, final Object self, final Object arg) { 1176 final CharSequence str = JSType.toCharSequence(arg); 1177 return newObj ? newObj(self, str) : str.toString(); 1178 } 1179 1180 /** 1181 * ECMA 15.5.2.1 new String ( [ value ] ) - special version with exactly one {@code int} arg 1182 * 1183 * Constructor 1184 * 1185 * @param newObj is this constructor invoked with the new operator 1186 * @param self self reference 1187 * @param arg the arg 1188 * 1189 * @return new NativeString containing the string representation of the arg 1190 */ 1191 @SpecializedConstructor 1192 public static Object constructor(final boolean newObj, final Object self, final int arg) { 1193 final String str = JSType.toString(arg); 1194 return newObj ? newObj(self, str) : str; 1195 } 1196 1197 /** 1198 * Lookup the appropriate method for an invoke dynamic call. 1199 * 1200 * @param request the link request 1201 * @param receiver receiver of call 1202 * @return Link to be invoked at call site. 1203 */ 1204 public static GuardedInvocation lookupPrimitive(final LinkRequest request, final Object receiver) { 1205 final MethodHandle guard = NashornGuards.getInstanceOf2Guard(String.class, ConsString.class); 1206 return PrimitiveLookup.lookupPrimitive(request, guard, new NativeString((CharSequence)receiver), WRAPFILTER, PROTOFILTER); 1207 } 1208 1209 @SuppressWarnings("unused") 1210 private static NativeString wrapFilter(final Object receiver) { 1211 return new NativeString((CharSequence)receiver); 1212 } 1213 1214 @SuppressWarnings("unused") 1215 private static Object protoFilter(final Object object) { 1216 return Global.instance().getStringPrototype(); 1217 } 1218 1219 private static CharSequence getCharSequence(final Object self) { 1220 if (self instanceof String || self instanceof ConsString) { 1221 return (CharSequence)self; 1222 } else if (self instanceof NativeString) { 1223 return ((NativeString)self).getValue(); 1224 } else if (self != null && self == Global.instance().getStringPrototype()) { 1225 return ""; 1226 } else { 1227 throw typeError("not.a.string", ScriptRuntime.safeToString(self)); 1228 } 1229 } 1230 1231 private static String getString(final Object self) { 1232 if (self instanceof String) { 1233 return (String)self; 1234 } else if (self instanceof ConsString) { 1235 return self.toString(); 1236 } else if (self instanceof NativeString) { 1237 return ((NativeString)self).getStringValue(); 1238 } else if (self != null && self == Global.instance().getStringPrototype()) { 1239 return ""; 1240 } else { 1241 throw typeError( "not.a.string", ScriptRuntime.safeToString(self)); 1242 } 1243 } 1244 1245 /** 1246 * Combines ECMA 9.10 CheckObjectCoercible and ECMA 9.8 ToString with a shortcut for strings. 1247 * 1248 * @param self the object 1249 * @return the object as string 1250 */ 1251 private static String checkObjectToString(final Object self) { 1252 if (self instanceof String) { 1253 return (String)self; 1254 } else if (self instanceof ConsString) { 1255 return self.toString(); 1256 } else { 1257 Global.checkObjectCoercible(self); 1258 return JSType.toString(self); 1259 } 1260 } 1261 1262 private boolean isValid(final int key) { 1263 return key >= 0 && key < value.length(); 1264 } 1265 1266 private static MethodHandle findOwnMH(final String name, final MethodType type) { 1267 return MH.findStatic(MethodHandles.lookup(), NativeString.class, name, type); 1268 } 1269 }