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