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