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