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