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