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