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