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