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 long position 543 * @param self self reference 544 * @param pos position in string 545 * @return number representing charcode at position 546 */ 547 @SpecializedFunction(linkLogic=CharCodeAtLinkLogic.class) 548 public static int charCodeAt(final Object self, final long pos) { 549 return charCodeAt(self, (int)pos); 550 } 551 552 /** 553 * ECMA 15.5.4.5 String.prototype.charCodeAt (pos) - specialized version for int position 554 * @param self self reference 555 * @param pos position in string 556 * @return number representing charcode at position 557 */ 558 559 @SpecializedFunction(linkLogic=CharCodeAtLinkLogic.class) 560 public static int charCodeAt(final Object self, final int pos) { 561 return getValidChar(self, pos); 562 } 563 564 /** 565 * ECMA 15.5.4.6 String.prototype.concat ( [ string1 [ , string2 [ , ... ] ] ] ) 566 * @param self self reference 567 * @param args list of string to concatenate 568 * @return concatenated string 569 */ 570 @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) 571 public static Object concat(final Object self, final Object... args) { 572 CharSequence cs = checkObjectToString(self); 573 if (args != null) { 574 for (final Object obj : args) { 575 cs = new ConsString(cs, JSType.toCharSequence(obj)); 576 } 577 } 578 return cs; 579 } 580 581 /** 582 * ECMA 15.5.4.7 String.prototype.indexOf (searchString, position) 583 * @param self self reference 584 * @param search string to search for 585 * @param pos position to start search 586 * @return position of first match or -1 587 */ 588 @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) 589 public static int indexOf(final Object self, final Object search, final Object pos) { 590 final String str = checkObjectToString(self); 591 return str.indexOf(JSType.toString(search), JSType.toInteger(pos)); 592 } 593 594 /** 595 * ECMA 15.5.4.7 String.prototype.indexOf (searchString, position) specialized for no position parameter 596 * @param self self reference 597 * @param search string to search for 598 * @return position of first match or -1 599 */ 600 @SpecializedFunction 601 public static int indexOf(final Object self, final Object search) { 602 return indexOf(self, search, 0); 603 } 604 605 /** 606 * ECMA 15.5.4.7 String.prototype.indexOf (searchString, position) specialized for double position parameter 607 * @param self self reference 608 * @param search string to search for 609 * @param pos position to start search 610 * @return position of first match or -1 611 */ 612 @SpecializedFunction 613 public static int indexOf(final Object self, final Object search, final double pos) { 614 return indexOf(self, search, (int) pos); 615 } 616 617 /** 618 * ECMA 15.5.4.7 String.prototype.indexOf (searchString, position) specialized for int position parameter 619 * @param self self reference 620 * @param search string to search for 621 * @param pos position to start search 622 * @return position of first match or -1 623 */ 624 @SpecializedFunction 625 public static int indexOf(final Object self, final Object search, final int pos) { 626 return checkObjectToString(self).indexOf(JSType.toString(search), pos); 627 } 628 629 /** 630 * ECMA 15.5.4.8 String.prototype.lastIndexOf (searchString, position) 631 * @param self self reference 632 * @param search string to search for 633 * @param pos position to start search 634 * @return last position of match or -1 635 */ 636 @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) 637 public static int lastIndexOf(final Object self, final Object search, final Object pos) { 638 639 final String str = checkObjectToString(self); 640 final String searchStr = JSType.toString(search); 641 final int length = str.length(); 642 643 int end; 644 645 if (pos == UNDEFINED) { 646 end = length; 647 } else { 648 final double numPos = JSType.toNumber(pos); 649 end = Double.isNaN(numPos) ? length : (int)numPos; 650 if (end < 0) { 651 end = 0; 652 } else if (end > length) { 653 end = length; 654 } 655 } 656 657 658 return str.lastIndexOf(searchStr, end); 659 } 660 661 /** 662 * ECMA 15.5.4.9 String.prototype.localeCompare (that) 663 * @param self self reference 664 * @param that comparison object 665 * @return result of locale sensitive comparison operation between {@code self} and {@code that} 666 */ 667 @Function(attributes = Attribute.NOT_ENUMERABLE) 668 public static double localeCompare(final Object self, final Object that) { 669 670 final String str = checkObjectToString(self); 671 final Collator collator = Collator.getInstance(Global.getEnv()._locale); 672 673 collator.setStrength(Collator.IDENTICAL); 674 collator.setDecomposition(Collator.CANONICAL_DECOMPOSITION); 675 676 return collator.compare(str, JSType.toString(that)); 677 } 678 679 /** 680 * ECMA 15.5.4.10 String.prototype.match (regexp) 681 * @param self self reference 682 * @param regexp regexp expression 683 * @return array of regexp matches 684 */ 685 @Function(attributes = Attribute.NOT_ENUMERABLE) 686 public static ScriptObject match(final Object self, final Object regexp) { 687 688 final String str = checkObjectToString(self); 689 690 NativeRegExp nativeRegExp; 691 if (regexp == UNDEFINED) { 692 nativeRegExp = new NativeRegExp(""); 693 } else { 694 nativeRegExp = Global.toRegExp(regexp); 695 } 696 697 if (!nativeRegExp.getGlobal()) { 698 return nativeRegExp.exec(str); 699 } 700 701 nativeRegExp.setLastIndex(0); 702 703 int previousLastIndex = 0; 704 final List<Object> matches = new ArrayList<>(); 705 706 Object result; 707 while ((result = nativeRegExp.exec(str)) != null) { 708 final int thisIndex = nativeRegExp.getLastIndex(); 709 if (thisIndex == previousLastIndex) { 710 nativeRegExp.setLastIndex(thisIndex + 1); 711 previousLastIndex = thisIndex + 1; 712 } else { 713 previousLastIndex = thisIndex; 714 } 715 matches.add(((ScriptObject)result).get(0)); 716 } 717 718 if (matches.isEmpty()) { 719 return null; 720 } 721 722 return new NativeArray(matches.toArray()); 723 } 724 725 /** 726 * ECMA 15.5.4.11 String.prototype.replace (searchValue, replaceValue) 727 * @param self self reference 728 * @param string item to replace 729 * @param replacement item to replace it with 730 * @return string after replacement 731 * @throws Throwable if replacement fails 732 */ 733 @Function(attributes = Attribute.NOT_ENUMERABLE) 734 public static String replace(final Object self, final Object string, final Object replacement) throws Throwable { 735 736 final String str = checkObjectToString(self); 737 738 final NativeRegExp nativeRegExp; 739 if (string instanceof NativeRegExp) { 740 nativeRegExp = (NativeRegExp) string; 741 } else { 742 nativeRegExp = NativeRegExp.flatRegExp(JSType.toString(string)); 743 } 744 745 if (Bootstrap.isCallable(replacement)) { 746 return nativeRegExp.replace(str, "", replacement); 747 } 748 749 return nativeRegExp.replace(str, JSType.toString(replacement), null); 750 } 751 752 /** 753 * ECMA 15.5.4.12 String.prototype.search (regexp) 754 * 755 * @param self self reference 756 * @param string string to search for 757 * @return offset where match occurred 758 */ 759 @Function(attributes = Attribute.NOT_ENUMERABLE) 760 public static int search(final Object self, final Object string) { 761 762 final String str = checkObjectToString(self); 763 final NativeRegExp nativeRegExp = Global.toRegExp(string == UNDEFINED ? "" : string); 764 765 return nativeRegExp.search(str); 766 } 767 768 /** 769 * ECMA 15.5.4.13 String.prototype.slice (start, end) 770 * 771 * @param self self reference 772 * @param start start position for slice 773 * @param end end position for slice 774 * @return sliced out substring 775 */ 776 @Function(attributes = Attribute.NOT_ENUMERABLE) 777 public static String slice(final Object self, final Object start, final Object end) { 778 779 final String str = checkObjectToString(self); 780 if (end == UNDEFINED) { 781 return slice(str, JSType.toInteger(start)); 782 } 783 return slice(str, JSType.toInteger(start), JSType.toInteger(end)); 784 } 785 786 /** 787 * ECMA 15.5.4.13 String.prototype.slice (start, end) specialized for single int parameter 788 * 789 * @param self self reference 790 * @param start start position for slice 791 * @return sliced out substring 792 */ 793 @SpecializedFunction 794 public static String slice(final Object self, final int start) { 795 final String str = checkObjectToString(self); 796 final int from = start < 0 ? Math.max(str.length() + start, 0) : Math.min(start, str.length()); 797 798 return str.substring(from); 799 } 800 801 /** 802 * ECMA 15.5.4.13 String.prototype.slice (start, end) specialized for single double parameter 803 * 804 * @param self self reference 805 * @param start start position for slice 806 * @return sliced out substring 807 */ 808 @SpecializedFunction 809 public static String slice(final Object self, final double start) { 810 return slice(self, (int)start); 811 } 812 813 /** 814 * ECMA 15.5.4.13 String.prototype.slice (start, end) specialized for two int parameters 815 * 816 * @param self self reference 817 * @param start start position for slice 818 * @param end end position for slice 819 * @return sliced out substring 820 */ 821 @SpecializedFunction 822 public static String slice(final Object self, final int start, final int end) { 823 824 final String str = checkObjectToString(self); 825 final int len = str.length(); 826 827 final int from = start < 0 ? Math.max(len + start, 0) : Math.min(start, len); 828 final int to = end < 0 ? Math.max(len + end, 0) : Math.min(end, len); 829 830 return str.substring(Math.min(from, to), to); 831 } 832 833 /** 834 * ECMA 15.5.4.13 String.prototype.slice (start, end) specialized for two double parameters 835 * 836 * @param self self reference 837 * @param start start position for slice 838 * @param end end position for slice 839 * @return sliced out substring 840 */ 841 @SpecializedFunction 842 public static String slice(final Object self, final double start, final double end) { 843 return slice(self, (int)start, (int)end); 844 } 845 846 /** 847 * ECMA 15.5.4.14 String.prototype.split (separator, limit) 848 * 849 * @param self self reference 850 * @param separator separator for split 851 * @param limit limit for splits 852 * @return array object in which splits have been placed 853 */ 854 @Function(attributes = Attribute.NOT_ENUMERABLE) 855 public static ScriptObject split(final Object self, final Object separator, final Object limit) { 856 final String str = checkObjectToString(self); 857 final long lim = limit == UNDEFINED ? JSType.MAX_UINT : JSType.toUint32(limit); 858 859 if (separator == UNDEFINED) { 860 return lim == 0 ? new NativeArray() : new NativeArray(new Object[]{str}); 861 } 862 863 if (separator instanceof NativeRegExp) { 864 return ((NativeRegExp) separator).split(str, lim); 865 } 866 867 // when separator is a string, it is treated as a literal search string to be used for splitting. 868 return splitString(str, JSType.toString(separator), lim); 869 } 870 871 private static ScriptObject splitString(final String str, final String separator, final long limit) { 872 if (separator.isEmpty()) { 873 final int length = (int) Math.min(str.length(), limit); 874 final Object[] array = new Object[length]; 875 for (int i = 0; i < length; i++) { 876 array[i] = String.valueOf(str.charAt(i)); 877 } 878 return new NativeArray(array); 879 } 880 881 final List<String> elements = new LinkedList<>(); 882 final int strLength = str.length(); 883 final int sepLength = separator.length(); 884 int pos = 0; 885 int n = 0; 886 887 while (pos < strLength && n < limit) { 888 final int found = str.indexOf(separator, pos); 889 if (found == -1) { 890 break; 891 } 892 elements.add(str.substring(pos, found)); 893 n++; 894 pos = found + sepLength; 895 } 896 if (pos <= strLength && n < limit) { 897 elements.add(str.substring(pos)); 898 } 899 900 return new NativeArray(elements.toArray()); 901 } 902 903 /** 904 * ECMA B.2.3 String.prototype.substr (start, length) 905 * 906 * @param self self reference 907 * @param start start position 908 * @param length length of section 909 * @return substring given start and length of section 910 */ 911 @Function(attributes = Attribute.NOT_ENUMERABLE) 912 public static String substr(final Object self, final Object start, final Object length) { 913 final String str = JSType.toString(self); 914 final int strLength = str.length(); 915 916 int intStart = JSType.toInteger(start); 917 if (intStart < 0) { 918 intStart = Math.max(intStart + strLength, 0); 919 } 920 921 final int intLen = Math.min(Math.max(length == UNDEFINED ? Integer.MAX_VALUE : JSType.toInteger(length), 0), strLength - intStart); 922 923 return intLen <= 0 ? "" : str.substring(intStart, intStart + intLen); 924 } 925 926 /** 927 * ECMA 15.5.4.15 String.prototype.substring (start, end) 928 * 929 * @param self self reference 930 * @param start start position of substring 931 * @param end end position of substring 932 * @return substring given start and end indexes 933 */ 934 @Function(attributes = Attribute.NOT_ENUMERABLE) 935 public static String substring(final Object self, final Object start, final Object end) { 936 937 final String str = checkObjectToString(self); 938 if (end == UNDEFINED) { 939 return substring(str, JSType.toInteger(start)); 940 } 941 return substring(str, JSType.toInteger(start), JSType.toInteger(end)); 942 } 943 944 /** 945 * ECMA 15.5.4.15 String.prototype.substring (start, end) specialized for int start parameter 946 * 947 * @param self self reference 948 * @param start start position of substring 949 * @return substring given start and end indexes 950 */ 951 @SpecializedFunction 952 public static String substring(final Object self, final int start) { 953 final String str = checkObjectToString(self); 954 if (start < 0) { 955 return str; 956 } else if (start >= str.length()) { 957 return ""; 958 } else { 959 return str.substring(start); 960 } 961 } 962 963 /** 964 * ECMA 15.5.4.15 String.prototype.substring (start, end) specialized for double start parameter 965 * 966 * @param self self reference 967 * @param start start position of substring 968 * @return substring given start and end indexes 969 */ 970 @SpecializedFunction 971 public static String substring(final Object self, final double start) { 972 return substring(self, (int)start); 973 } 974 975 /** 976 * ECMA 15.5.4.15 String.prototype.substring (start, end) specialized for int start and end parameters 977 * 978 * @param self self reference 979 * @param start start position of substring 980 * @param end end position of substring 981 * @return substring given start and end indexes 982 */ 983 @SpecializedFunction 984 public static String substring(final Object self, final int start, final int end) { 985 final String str = checkObjectToString(self); 986 final int len = str.length(); 987 final int validStart = start < 0 ? 0 : start > len ? len : start; 988 final int validEnd = end < 0 ? 0 : end > len ? len : end; 989 990 if (validStart < validEnd) { 991 return str.substring(validStart, validEnd); 992 } 993 return str.substring(validEnd, validStart); 994 } 995 996 /** 997 * ECMA 15.5.4.15 String.prototype.substring (start, end) specialized for double start and end parameters 998 * 999 * @param self self reference 1000 * @param start start position of substring 1001 * @param end end position of substring 1002 * @return substring given start and end indexes 1003 */ 1004 @SpecializedFunction 1005 public static String substring(final Object self, final double start, final double end) { 1006 return substring(self, (int)start, (int)end); 1007 } 1008 1009 /** 1010 * ECMA 15.5.4.16 String.prototype.toLowerCase ( ) 1011 * @param self self reference 1012 * @return string to lower case 1013 */ 1014 @Function(attributes = Attribute.NOT_ENUMERABLE) 1015 public static String toLowerCase(final Object self) { 1016 return checkObjectToString(self).toLowerCase(Locale.ROOT); 1017 } 1018 1019 /** 1020 * ECMA 15.5.4.17 String.prototype.toLocaleLowerCase ( ) 1021 * @param self self reference 1022 * @return string to locale sensitive lower case 1023 */ 1024 @Function(attributes = Attribute.NOT_ENUMERABLE) 1025 public static String toLocaleLowerCase(final Object self) { 1026 return checkObjectToString(self).toLowerCase(Global.getEnv()._locale); 1027 } 1028 1029 /** 1030 * ECMA 15.5.4.18 String.prototype.toUpperCase ( ) 1031 * @param self self reference 1032 * @return string to upper case 1033 */ 1034 @Function(attributes = Attribute.NOT_ENUMERABLE) 1035 public static String toUpperCase(final Object self) { 1036 return checkObjectToString(self).toUpperCase(Locale.ROOT); 1037 } 1038 1039 /** 1040 * ECMA 15.5.4.19 String.prototype.toLocaleUpperCase ( ) 1041 * @param self self reference 1042 * @return string to locale sensitive upper case 1043 */ 1044 @Function(attributes = Attribute.NOT_ENUMERABLE) 1045 public static String toLocaleUpperCase(final Object self) { 1046 return checkObjectToString(self).toUpperCase(Global.getEnv()._locale); 1047 } 1048 1049 /** 1050 * ECMA 15.5.4.20 String.prototype.trim ( ) 1051 * @param self self reference 1052 * @return string trimmed from whitespace 1053 */ 1054 @Function(attributes = Attribute.NOT_ENUMERABLE) 1055 public static String trim(final Object self) { 1056 final String str = checkObjectToString(self); 1057 int start = 0; 1058 int end = str.length() - 1; 1059 1060 while (start <= end && ScriptRuntime.isJSWhitespace(str.charAt(start))) { 1061 start++; 1062 } 1063 while (end > start && ScriptRuntime.isJSWhitespace(str.charAt(end))) { 1064 end--; 1065 } 1066 1067 return str.substring(start, end + 1); 1068 } 1069 1070 /** 1071 * Nashorn extension: String.prototype.trimLeft ( ) 1072 * @param self self reference 1073 * @return string trimmed left from whitespace 1074 */ 1075 @Function(attributes = Attribute.NOT_ENUMERABLE) 1076 public static String trimLeft(final Object self) { 1077 1078 final String str = checkObjectToString(self); 1079 int start = 0; 1080 final int end = str.length() - 1; 1081 1082 while (start <= end && ScriptRuntime.isJSWhitespace(str.charAt(start))) { 1083 start++; 1084 } 1085 1086 return str.substring(start, end + 1); 1087 } 1088 1089 /** 1090 * Nashorn extension: String.prototype.trimRight ( ) 1091 * @param self self reference 1092 * @return string trimmed right from whitespace 1093 */ 1094 @Function(attributes = Attribute.NOT_ENUMERABLE) 1095 public static String trimRight(final Object self) { 1096 1097 final String str = checkObjectToString(self); 1098 final int start = 0; 1099 int end = str.length() - 1; 1100 1101 while (end >= start && ScriptRuntime.isJSWhitespace(str.charAt(end))) { 1102 end--; 1103 } 1104 1105 return str.substring(start, end + 1); 1106 } 1107 1108 private static ScriptObject newObj(final CharSequence str) { 1109 return new NativeString(str); 1110 } 1111 1112 /** 1113 * ECMA 15.5.2.1 new String ( [ value ] ) 1114 * 1115 * Constructor 1116 * 1117 * @param newObj is this constructor invoked with the new operator 1118 * @param self self reference 1119 * @param args arguments (a value) 1120 * 1121 * @return new NativeString, empty string if no args, extraneous args ignored 1122 */ 1123 @Constructor(arity = 1) 1124 public static Object constructor(final boolean newObj, final Object self, final Object... args) { 1125 final CharSequence str = args.length > 0 ? JSType.toCharSequence(args[0]) : ""; 1126 return newObj ? newObj(str) : str.toString(); 1127 } 1128 1129 /** 1130 * ECMA 15.5.2.1 new String ( [ value ] ) - special version with no args 1131 * 1132 * Constructor 1133 * 1134 * @param newObj is this constructor invoked with the new operator 1135 * @param self self reference 1136 * 1137 * @return new NativeString ("") 1138 */ 1139 @SpecializedFunction(isConstructor=true) 1140 public static Object constructor(final boolean newObj, final Object self) { 1141 return newObj ? newObj("") : ""; 1142 } 1143 1144 /** 1145 * ECMA 15.5.2.1 new String ( [ value ] ) - special version with one arg 1146 * 1147 * Constructor 1148 * 1149 * @param newObj is this constructor invoked with the new operator 1150 * @param self self reference 1151 * @param arg argument 1152 * 1153 * @return new NativeString (arg) 1154 */ 1155 @SpecializedFunction(isConstructor=true) 1156 public static Object constructor(final boolean newObj, final Object self, final Object arg) { 1157 final CharSequence str = JSType.toCharSequence(arg); 1158 return newObj ? newObj(str) : str.toString(); 1159 } 1160 1161 /** 1162 * ECMA 15.5.2.1 new String ( [ value ] ) - special version with exactly one {@code int} arg 1163 * 1164 * Constructor 1165 * 1166 * @param newObj is this constructor invoked with the new operator 1167 * @param self self reference 1168 * @param arg the arg 1169 * 1170 * @return new NativeString containing the string representation of the arg 1171 */ 1172 @SpecializedFunction(isConstructor=true) 1173 public static Object constructor(final boolean newObj, final Object self, final int arg) { 1174 final String str = Integer.toString(arg); 1175 return newObj ? newObj(str) : str; 1176 } 1177 1178 /** 1179 * ECMA 15.5.2.1 new String ( [ value ] ) - special version with exactly one {@code int} arg 1180 * 1181 * Constructor 1182 * 1183 * @param newObj is this constructor invoked with the new operator 1184 * @param self self reference 1185 * @param arg the arg 1186 * 1187 * @return new NativeString containing the string representation of the arg 1188 */ 1189 @SpecializedFunction(isConstructor=true) 1190 public static Object constructor(final boolean newObj, final Object self, final long arg) { 1191 final String str = Long.toString(arg); 1192 return newObj ? newObj(str) : str; 1193 } 1194 1195 /** 1196 * ECMA 15.5.2.1 new String ( [ value ] ) - special version with exactly one {@code int} arg 1197 * 1198 * Constructor 1199 * 1200 * @param newObj is this constructor invoked with the new operator 1201 * @param self self reference 1202 * @param arg the arg 1203 * 1204 * @return new NativeString containing the string representation of the arg 1205 */ 1206 @SpecializedFunction(isConstructor=true) 1207 public static Object constructor(final boolean newObj, final Object self, final double arg) { 1208 final String str = JSType.toString(arg); 1209 return newObj ? newObj(str) : str; 1210 } 1211 1212 /** 1213 * ECMA 15.5.2.1 new String ( [ value ] ) - special version with exactly one {@code boolean} arg 1214 * 1215 * Constructor 1216 * 1217 * @param newObj is this constructor invoked with the new operator 1218 * @param self self reference 1219 * @param arg the arg 1220 * 1221 * @return new NativeString containing the string representation of the arg 1222 */ 1223 @SpecializedFunction(isConstructor=true) 1224 public static Object constructor(final boolean newObj, final Object self, final boolean arg) { 1225 final String str = Boolean.toString(arg); 1226 return newObj ? newObj(str) : str; 1227 } 1228 1229 /** 1230 * ECMA 6 21.1.3.27 String.prototype [ @@iterator ]( ) 1231 * 1232 * @param self self reference 1233 * @return a string iterator 1234 */ 1235 @Function(attributes = Attribute.NOT_ENUMERABLE, name = "@@iterator") 1236 public static Object getIterator(final Object self) { 1237 return new StringIterator(checkObjectToString(self), Global.instance()); 1238 } 1239 1240 /** 1241 * Lookup the appropriate method for an invoke dynamic call. 1242 * 1243 * @param request the link request 1244 * @param receiver receiver of call 1245 * @return Link to be invoked at call site. 1246 */ 1247 public static GuardedInvocation lookupPrimitive(final LinkRequest request, final Object receiver) { 1248 return PrimitiveLookup.lookupPrimitive(request, NashornGuards.getStringGuard(), 1249 new NativeString((CharSequence)receiver), WRAPFILTER, PROTOFILTER); 1250 } 1251 1252 @SuppressWarnings("unused") 1253 private static NativeString wrapFilter(final Object receiver) { 1254 return new NativeString((CharSequence)receiver); 1255 } 1256 1257 @SuppressWarnings("unused") 1258 private static Object protoFilter(final Object object) { 1259 return Global.instance().getStringPrototype(); 1260 } 1261 1262 private static CharSequence getCharSequence(final Object self) { 1263 if (JSType.isString(self)) { 1264 return (CharSequence)self; 1265 } else if (self instanceof NativeString) { 1266 return ((NativeString)self).getValue(); 1267 } else if (self != null && self == Global.instance().getStringPrototype()) { 1268 return ""; 1269 } else { 1270 throw typeError("not.a.string", ScriptRuntime.safeToString(self)); 1271 } 1272 } 1273 1274 private static String getString(final Object self) { 1275 if (self instanceof String) { 1276 return (String)self; 1277 } else if (self instanceof ConsString) { 1278 return self.toString(); 1279 } else if (self instanceof NativeString) { 1280 return ((NativeString)self).getStringValue(); 1281 } else if (self != null && self == Global.instance().getStringPrototype()) { 1282 return ""; 1283 } else { 1284 throw typeError("not.a.string", ScriptRuntime.safeToString(self)); 1285 } 1286 } 1287 1288 /** 1289 * Combines ECMA 9.10 CheckObjectCoercible and ECMA 9.8 ToString with a shortcut for strings. 1290 * 1291 * @param self the object 1292 * @return the object as string 1293 */ 1294 private static String checkObjectToString(final Object self) { 1295 if (self instanceof String) { 1296 return (String)self; 1297 } else if (self instanceof ConsString) { 1298 return self.toString(); 1299 } else { 1300 Global.checkObjectCoercible(self); 1301 return JSType.toString(self); 1302 } 1303 } 1304 1305 private boolean isValidStringIndex(final int key) { 1306 return key >= 0 && key < value.length(); 1307 } 1308 1309 private static MethodHandle findOwnMH(final String name, final MethodType type) { 1310 return MH.findStatic(MethodHandles.lookup(), NativeString.class, name, type); 1311 } 1312 1313 @Override 1314 public LinkLogic getLinkLogic(final Class<? extends LinkLogic> clazz) { 1315 if (clazz == CharCodeAtLinkLogic.class) { 1316 return CharCodeAtLinkLogic.INSTANCE; 1317 } 1318 return null; 1319 } 1320 1321 @Override 1322 public boolean hasPerInstanceAssumptions() { 1323 return false; 1324 } 1325 1326 /** 1327 * This is linker logic charCodeAt - when we specialize further methods in NativeString 1328 * It may be expanded. It's link check makes sure that we are dealing with a char 1329 * sequence and that we are in range 1330 */ 1331 private static final class CharCodeAtLinkLogic extends SpecializedFunction.LinkLogic { 1332 private static final CharCodeAtLinkLogic INSTANCE = new CharCodeAtLinkLogic(); 1333 1334 @Override 1335 public boolean canLink(final Object self, final CallSiteDescriptor desc, final LinkRequest request) { 1336 try { 1337 //check that it's a char sequence or throw cce 1338 final CharSequence cs = (CharSequence)self; 1339 //check that the index, representable as an int, is inside the array 1340 final int intIndex = JSType.toInteger(request.getArguments()[2]); 1341 return intIndex >= 0 && intIndex < cs.length(); //can link 1342 } catch (final ClassCastException | IndexOutOfBoundsException e) { 1343 //fallthru 1344 } 1345 return false; 1346 } 1347 1348 /** 1349 * charCodeAt callsites can throw ClassCastException as a mechanism to have them 1350 * relinked - this enabled fast checks of the kind of ((IntArrayData)arrayData).push(x) 1351 * for an IntArrayData only push - if this fails, a CCE will be thrown and we will relink 1352 */ 1353 @Override 1354 public Class<? extends Throwable> getRelinkException() { 1355 return ClassCastException.class; 1356 } 1357 } 1358 }