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 long position
 543      * @param self self reference
 544      * @param pos  position in string
 545      * @return number representing charcode at position
 546      */
 547     @SpecializedFunction(linkLogic=CharCodeAtLinkLogic.class)
 548     public static int charCodeAt(final Object self, final long pos) {
 549         return charCodeAt(self, (int)pos);
 550     }
 551 
 552     /**
 553      * ECMA 15.5.4.5 String.prototype.charCodeAt (pos) - specialized version for int position
 554      * @param self self reference
 555      * @param pos  position in string
 556      * @return number representing charcode at position
 557      */
 558 
 559     @SpecializedFunction(linkLogic=CharCodeAtLinkLogic.class)
 560     public static int charCodeAt(final Object self, final int pos) {
 561         return getValidChar(self, pos);
 562     }
 563 
 564     /**
 565      * ECMA 15.5.4.6 String.prototype.concat ( [ string1 [ , string2 [ , ... ] ] ] )
 566      * @param self self reference
 567      * @param args list of string to concatenate
 568      * @return concatenated string
 569      */
 570     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
 571     public static Object concat(final Object self, final Object... args) {
 572         CharSequence cs = checkObjectToString(self);
 573         if (args != null) {
 574             for (final Object obj : args) {
 575                 cs = new ConsString(cs, JSType.toCharSequence(obj));
 576             }
 577         }
 578         return cs;
 579     }
 580 
 581     /**
 582      * ECMA 15.5.4.7 String.prototype.indexOf (searchString, position)
 583      * @param self   self reference
 584      * @param search string to search for
 585      * @param pos    position to start search
 586      * @return position of first match or -1
 587      */
 588     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
 589     public static int indexOf(final Object self, final Object search, final Object pos) {
 590         final String str = checkObjectToString(self);
 591         return str.indexOf(JSType.toString(search), JSType.toInteger(pos));
 592     }
 593 
 594     /**
 595      * ECMA 15.5.4.7 String.prototype.indexOf (searchString, position) specialized for no position parameter
 596      * @param self   self reference
 597      * @param search string to search for
 598      * @return position of first match or -1
 599      */
 600     @SpecializedFunction
 601     public static int indexOf(final Object self, final Object search) {
 602         return indexOf(self, search, 0);
 603     }
 604 
 605     /**
 606      * ECMA 15.5.4.7 String.prototype.indexOf (searchString, position) specialized for double position parameter
 607      * @param self   self reference
 608      * @param search string to search for
 609      * @param pos    position to start search
 610      * @return position of first match or -1
 611      */
 612     @SpecializedFunction
 613     public static int indexOf(final Object self, final Object search, final double pos) {
 614         return indexOf(self, search, (int) pos);
 615     }
 616 
 617     /**
 618      * ECMA 15.5.4.7 String.prototype.indexOf (searchString, position) specialized for int position parameter
 619      * @param self   self reference
 620      * @param search string to search for
 621      * @param pos    position to start search
 622      * @return position of first match or -1
 623      */
 624     @SpecializedFunction
 625     public static int indexOf(final Object self, final Object search, final int pos) {
 626         return checkObjectToString(self).indexOf(JSType.toString(search), pos);
 627     }
 628 
 629     /**
 630      * ECMA 15.5.4.8 String.prototype.lastIndexOf (searchString, position)
 631      * @param self   self reference
 632      * @param search string to search for
 633      * @param pos    position to start search
 634      * @return last position of match or -1
 635      */
 636     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
 637     public static int lastIndexOf(final Object self, final Object search, final Object pos) {
 638 
 639         final String str       = checkObjectToString(self);
 640         final String searchStr = JSType.toString(search);
 641         final int length       = str.length();
 642 
 643         int end;
 644 
 645         if (pos == UNDEFINED) {
 646             end = length;
 647         } else {
 648             final double numPos = JSType.toNumber(pos);
 649             end = Double.isNaN(numPos) ? length : (int)numPos;
 650             if (end < 0) {
 651                 end = 0;
 652             } else if (end > length) {
 653                 end = length;
 654             }
 655         }
 656 
 657 
 658         return str.lastIndexOf(searchStr, end);
 659     }
 660 
 661     /**
 662      * ECMA 15.5.4.9 String.prototype.localeCompare (that)
 663      * @param self self reference
 664      * @param that comparison object
 665      * @return result of locale sensitive comparison operation between {@code self} and {@code that}
 666      */
 667     @Function(attributes = Attribute.NOT_ENUMERABLE)
 668     public static double localeCompare(final Object self, final Object that) {
 669 
 670         final String   str      = checkObjectToString(self);
 671         final Collator collator = Collator.getInstance(Global.getEnv()._locale);
 672 
 673         collator.setStrength(Collator.IDENTICAL);
 674         collator.setDecomposition(Collator.CANONICAL_DECOMPOSITION);
 675 
 676         return collator.compare(str, JSType.toString(that));
 677     }
 678 
 679     /**
 680      * ECMA 15.5.4.10 String.prototype.match (regexp)
 681      * @param self   self reference
 682      * @param regexp regexp expression
 683      * @return array of regexp matches
 684      */
 685     @Function(attributes = Attribute.NOT_ENUMERABLE)
 686     public static ScriptObject match(final Object self, final Object regexp) {
 687 
 688         final String str = checkObjectToString(self);
 689 
 690         NativeRegExp nativeRegExp;
 691         if (regexp == UNDEFINED) {
 692             nativeRegExp = new NativeRegExp("");
 693         } else {
 694             nativeRegExp = Global.toRegExp(regexp);
 695         }
 696 
 697         if (!nativeRegExp.getGlobal()) {
 698             return nativeRegExp.exec(str);
 699         }
 700 
 701         nativeRegExp.setLastIndex(0);
 702 
 703         int previousLastIndex = 0;
 704         final List<Object> matches = new ArrayList<>();
 705 
 706         Object result;
 707         while ((result = nativeRegExp.exec(str)) != null) {
 708             final int thisIndex = nativeRegExp.getLastIndex();
 709             if (thisIndex == previousLastIndex) {
 710                 nativeRegExp.setLastIndex(thisIndex + 1);
 711                 previousLastIndex = thisIndex + 1;
 712             } else {
 713                 previousLastIndex = thisIndex;
 714             }
 715             matches.add(((ScriptObject)result).get(0));
 716         }
 717 
 718         if (matches.isEmpty()) {
 719             return null;
 720         }
 721 
 722         return new NativeArray(matches.toArray());
 723     }
 724 
 725     /**
 726      * ECMA 15.5.4.11 String.prototype.replace (searchValue, replaceValue)
 727      * @param self        self reference
 728      * @param string      item to replace
 729      * @param replacement item to replace it with
 730      * @return string after replacement
 731      * @throws Throwable if replacement fails
 732      */
 733     @Function(attributes = Attribute.NOT_ENUMERABLE)
 734     public static String replace(final Object self, final Object string, final Object replacement) throws Throwable {
 735 
 736         final String str = checkObjectToString(self);
 737 
 738         final NativeRegExp nativeRegExp;
 739         if (string instanceof NativeRegExp) {
 740             nativeRegExp = (NativeRegExp) string;
 741         } else {
 742             nativeRegExp = NativeRegExp.flatRegExp(JSType.toString(string));
 743         }
 744 
 745         if (Bootstrap.isCallable(replacement)) {
 746             return nativeRegExp.replace(str, "", replacement);
 747         }
 748 
 749         return nativeRegExp.replace(str, JSType.toString(replacement), null);
 750     }
 751 
 752     /**
 753      * ECMA 15.5.4.12 String.prototype.search (regexp)
 754      *
 755      * @param self    self reference
 756      * @param string  string to search for
 757      * @return offset where match occurred
 758      */
 759     @Function(attributes = Attribute.NOT_ENUMERABLE)
 760     public static int search(final Object self, final Object string) {
 761 
 762         final String       str          = checkObjectToString(self);
 763         final NativeRegExp nativeRegExp = Global.toRegExp(string == UNDEFINED ? "" : string);
 764 
 765         return nativeRegExp.search(str);
 766     }
 767 
 768     /**
 769      * ECMA 15.5.4.13 String.prototype.slice (start, end)
 770      *
 771      * @param self  self reference
 772      * @param start start position for slice
 773      * @param end   end position for slice
 774      * @return sliced out substring
 775      */
 776     @Function(attributes = Attribute.NOT_ENUMERABLE)
 777     public static String slice(final Object self, final Object start, final Object end) {
 778 
 779         final String str      = checkObjectToString(self);
 780         if (end == UNDEFINED) {
 781             return slice(str, JSType.toInteger(start));
 782         }
 783         return slice(str, JSType.toInteger(start), JSType.toInteger(end));
 784     }
 785 
 786     /**
 787      * ECMA 15.5.4.13 String.prototype.slice (start, end) specialized for single int parameter
 788      *
 789      * @param self  self reference
 790      * @param start start position for slice
 791      * @return sliced out substring
 792      */
 793     @SpecializedFunction
 794     public static String slice(final Object self, final int start) {
 795         final String str = checkObjectToString(self);
 796         final int from = start < 0 ? Math.max(str.length() + start, 0) : Math.min(start, str.length());
 797 
 798         return str.substring(from);
 799     }
 800 
 801     /**
 802      * ECMA 15.5.4.13 String.prototype.slice (start, end) specialized for single double parameter
 803      *
 804      * @param self  self reference
 805      * @param start start position for slice
 806      * @return sliced out substring
 807      */
 808     @SpecializedFunction
 809     public static String slice(final Object self, final double start) {
 810         return slice(self, (int)start);
 811     }
 812 
 813     /**
 814      * ECMA 15.5.4.13 String.prototype.slice (start, end) specialized for two int parameters
 815      *
 816      * @param self  self reference
 817      * @param start start position for slice
 818      * @param end   end position for slice
 819      * @return sliced out substring
 820      */
 821     @SpecializedFunction
 822     public static String slice(final Object self, final int start, final int end) {
 823 
 824         final String str = checkObjectToString(self);
 825         final int len    = str.length();
 826 
 827         final int from = start < 0 ? Math.max(len + start, 0) : Math.min(start, len);
 828         final int to   = end < 0   ? Math.max(len + end, 0)   : Math.min(end, len);
 829 
 830         return str.substring(Math.min(from, to), to);
 831     }
 832 
 833     /**
 834      * ECMA 15.5.4.13 String.prototype.slice (start, end) specialized for two double parameters
 835      *
 836      * @param self  self reference
 837      * @param start start position for slice
 838      * @param end   end position for slice
 839      * @return sliced out substring
 840      */
 841     @SpecializedFunction
 842     public static String slice(final Object self, final double start, final double end) {
 843         return slice(self, (int)start, (int)end);
 844     }
 845 
 846     /**
 847      * ECMA 15.5.4.14 String.prototype.split (separator, limit)
 848      *
 849      * @param self      self reference
 850      * @param separator separator for split
 851      * @param limit     limit for splits
 852      * @return array object in which splits have been placed
 853      */
 854     @Function(attributes = Attribute.NOT_ENUMERABLE)
 855     public static ScriptObject split(final Object self, final Object separator, final Object limit) {
 856         final String str = checkObjectToString(self);
 857         final long lim = limit == UNDEFINED ? JSType.MAX_UINT : JSType.toUint32(limit);
 858 
 859         if (separator == UNDEFINED) {
 860             return lim == 0 ? new NativeArray() : new NativeArray(new Object[]{str});
 861         }
 862 
 863         if (separator instanceof NativeRegExp) {
 864             return ((NativeRegExp) separator).split(str, lim);
 865         }
 866 
 867         // when separator is a string, it is treated as a literal search string to be used for splitting.
 868         return splitString(str, JSType.toString(separator), lim);
 869     }
 870 
 871     private static ScriptObject splitString(final String str, final String separator, final long limit) {
 872         if (separator.isEmpty()) {
 873             final int length = (int) Math.min(str.length(), limit);
 874             final Object[] array = new Object[length];
 875             for (int i = 0; i < length; i++) {
 876                 array[i] = String.valueOf(str.charAt(i));
 877             }
 878             return new NativeArray(array);
 879         }
 880 
 881         final List<String> elements = new LinkedList<>();
 882         final int strLength = str.length();
 883         final int sepLength = separator.length();
 884         int pos = 0;
 885         int n = 0;
 886 
 887         while (pos < strLength && n < limit) {
 888             final int found = str.indexOf(separator, pos);
 889             if (found == -1) {
 890                 break;
 891             }
 892             elements.add(str.substring(pos, found));
 893             n++;
 894             pos = found + sepLength;
 895         }
 896         if (pos <= strLength && n < limit) {
 897             elements.add(str.substring(pos));
 898         }
 899 
 900         return new NativeArray(elements.toArray());
 901     }
 902 
 903     /**
 904      * ECMA B.2.3 String.prototype.substr (start, length)
 905      *
 906      * @param self   self reference
 907      * @param start  start position
 908      * @param length length of section
 909      * @return substring given start and length of section
 910      */
 911     @Function(attributes = Attribute.NOT_ENUMERABLE)
 912     public static String substr(final Object self, final Object start, final Object length) {
 913         final String str       = JSType.toString(self);
 914         final int    strLength = str.length();
 915 
 916         int intStart = JSType.toInteger(start);
 917         if (intStart < 0) {
 918             intStart = Math.max(intStart + strLength, 0);
 919         }
 920 
 921         final int intLen = Math.min(Math.max(length == UNDEFINED ? Integer.MAX_VALUE : JSType.toInteger(length), 0), strLength - intStart);
 922 
 923         return intLen <= 0 ? "" : str.substring(intStart, intStart + intLen);
 924     }
 925 
 926     /**
 927      * ECMA 15.5.4.15 String.prototype.substring (start, end)
 928      *
 929      * @param self  self reference
 930      * @param start start position of substring
 931      * @param end   end position of substring
 932      * @return substring given start and end indexes
 933      */
 934     @Function(attributes = Attribute.NOT_ENUMERABLE)
 935     public static String substring(final Object self, final Object start, final Object end) {
 936 
 937         final String str = checkObjectToString(self);
 938         if (end == UNDEFINED) {
 939             return substring(str, JSType.toInteger(start));
 940         }
 941         return substring(str, JSType.toInteger(start), JSType.toInteger(end));
 942     }
 943 
 944     /**
 945      * ECMA 15.5.4.15 String.prototype.substring (start, end) specialized for int start parameter
 946      *
 947      * @param self  self reference
 948      * @param start start position of substring
 949      * @return substring given start and end indexes
 950      */
 951     @SpecializedFunction
 952     public static String substring(final Object self, final int start) {
 953         final String str = checkObjectToString(self);
 954         if (start < 0) {
 955             return str;
 956         } else if (start >= str.length()) {
 957             return "";
 958         } else {
 959             return str.substring(start);
 960         }
 961     }
 962 
 963     /**
 964      * ECMA 15.5.4.15 String.prototype.substring (start, end) specialized for double start parameter
 965      *
 966      * @param self  self reference
 967      * @param start start position of substring
 968      * @return substring given start and end indexes
 969      */
 970     @SpecializedFunction
 971     public static String substring(final Object self, final double start) {
 972         return substring(self, (int)start);
 973     }
 974 
 975     /**
 976      * ECMA 15.5.4.15 String.prototype.substring (start, end) specialized for int start and end parameters
 977      *
 978      * @param self  self reference
 979      * @param start start position of substring
 980      * @param end   end position of substring
 981      * @return substring given start and end indexes
 982      */
 983     @SpecializedFunction
 984     public static String substring(final Object self, final int start, final int end) {
 985         final String str = checkObjectToString(self);
 986         final int len = str.length();
 987         final int validStart = start < 0 ? 0 : start > len ? len : start;
 988         final int validEnd   = end < 0 ? 0 : end > len ? len : end;
 989 
 990         if (validStart < validEnd) {
 991             return str.substring(validStart, validEnd);
 992         }
 993         return str.substring(validEnd, validStart);
 994     }
 995 
 996     /**
 997      * ECMA 15.5.4.15 String.prototype.substring (start, end) specialized for double start and end parameters
 998      *
 999      * @param self  self reference
1000      * @param start start position of substring
1001      * @param end   end position of substring
1002      * @return substring given start and end indexes
1003      */
1004     @SpecializedFunction
1005     public static String substring(final Object self, final double start, final double end) {
1006         return substring(self, (int)start, (int)end);
1007     }
1008 
1009     /**
1010      * ECMA 15.5.4.16 String.prototype.toLowerCase ( )
1011      * @param self self reference
1012      * @return string to lower case
1013      */
1014     @Function(attributes = Attribute.NOT_ENUMERABLE)
1015     public static String toLowerCase(final Object self) {
1016         return checkObjectToString(self).toLowerCase(Locale.ROOT);
1017     }
1018 
1019     /**
1020      * ECMA 15.5.4.17 String.prototype.toLocaleLowerCase ( )
1021      * @param self self reference
1022      * @return string to locale sensitive lower case
1023      */
1024     @Function(attributes = Attribute.NOT_ENUMERABLE)
1025     public static String toLocaleLowerCase(final Object self) {
1026         return checkObjectToString(self).toLowerCase(Global.getEnv()._locale);
1027     }
1028 
1029     /**
1030      * ECMA 15.5.4.18 String.prototype.toUpperCase ( )
1031      * @param self self reference
1032      * @return string to upper case
1033      */
1034     @Function(attributes = Attribute.NOT_ENUMERABLE)
1035     public static String toUpperCase(final Object self) {
1036         return checkObjectToString(self).toUpperCase(Locale.ROOT);
1037     }
1038 
1039     /**
1040      * ECMA 15.5.4.19 String.prototype.toLocaleUpperCase ( )
1041      * @param self self reference
1042      * @return string to locale sensitive upper case
1043      */
1044     @Function(attributes = Attribute.NOT_ENUMERABLE)
1045     public static String toLocaleUpperCase(final Object self) {
1046         return checkObjectToString(self).toUpperCase(Global.getEnv()._locale);
1047     }
1048 
1049     /**
1050      * ECMA 15.5.4.20 String.prototype.trim ( )
1051      * @param self self reference
1052      * @return string trimmed from whitespace
1053      */
1054     @Function(attributes = Attribute.NOT_ENUMERABLE)
1055     public static String trim(final Object self) {
1056         final String str = checkObjectToString(self);
1057         int start = 0;
1058         int end   = str.length() - 1;
1059 
1060         while (start <= end && ScriptRuntime.isJSWhitespace(str.charAt(start))) {
1061             start++;
1062         }
1063         while (end > start && ScriptRuntime.isJSWhitespace(str.charAt(end))) {
1064             end--;
1065         }
1066 
1067         return str.substring(start, end + 1);
1068     }
1069 
1070     /**
1071      * Nashorn extension: String.prototype.trimLeft ( )
1072      * @param self self reference
1073      * @return string trimmed left from whitespace
1074      */
1075     @Function(attributes = Attribute.NOT_ENUMERABLE)
1076     public static String trimLeft(final Object self) {
1077 
1078         final String str = checkObjectToString(self);
1079         int start = 0;
1080         final int end   = str.length() - 1;
1081 
1082         while (start <= end && ScriptRuntime.isJSWhitespace(str.charAt(start))) {
1083             start++;
1084         }
1085 
1086         return str.substring(start, end + 1);
1087     }
1088 
1089     /**
1090      * Nashorn extension: String.prototype.trimRight ( )
1091      * @param self self reference
1092      * @return string trimmed right from whitespace
1093      */
1094     @Function(attributes = Attribute.NOT_ENUMERABLE)
1095     public static String trimRight(final Object self) {
1096 
1097         final String str = checkObjectToString(self);
1098         final int start = 0;
1099         int end   = str.length() - 1;
1100 
1101         while (end >= start && ScriptRuntime.isJSWhitespace(str.charAt(end))) {
1102             end--;
1103         }
1104 
1105         return str.substring(start, end + 1);
1106     }
1107 
1108     private static ScriptObject newObj(final CharSequence str) {
1109         return new NativeString(str);
1110     }
1111 
1112     /**
1113      * ECMA 15.5.2.1 new String ( [ value ] )
1114      *
1115      * Constructor
1116      *
1117      * @param newObj is this constructor invoked with the new operator
1118      * @param self   self reference
1119      * @param args   arguments (a value)
1120      *
1121      * @return new NativeString, empty string if no args, extraneous args ignored
1122      */
1123     @Constructor(arity = 1)
1124     public static Object constructor(final boolean newObj, final Object self, final Object... args) {
1125         final CharSequence str = args.length > 0 ? JSType.toCharSequence(args[0]) : "";
1126         return newObj ? newObj(str) : str.toString();
1127     }
1128 
1129     /**
1130      * ECMA 15.5.2.1 new String ( [ value ] ) - special version with no args
1131      *
1132      * Constructor
1133      *
1134      * @param newObj is this constructor invoked with the new operator
1135      * @param self   self reference
1136      *
1137      * @return new NativeString ("")
1138      */
1139     @SpecializedFunction(isConstructor=true)
1140     public static Object constructor(final boolean newObj, final Object self) {
1141         return newObj ? newObj("") : "";
1142     }
1143 
1144     /**
1145      * ECMA 15.5.2.1 new String ( [ value ] ) - special version with one arg
1146      *
1147      * Constructor
1148      *
1149      * @param newObj is this constructor invoked with the new operator
1150      * @param self   self reference
1151      * @param arg    argument
1152      *
1153      * @return new NativeString (arg)
1154      */
1155     @SpecializedFunction(isConstructor=true)
1156     public static Object constructor(final boolean newObj, final Object self, final Object arg) {
1157         final CharSequence str = JSType.toCharSequence(arg);
1158         return newObj ? newObj(str) : str.toString();
1159     }
1160 
1161     /**
1162      * ECMA 15.5.2.1 new String ( [ value ] ) - special version with exactly one {@code int} arg
1163      *
1164      * Constructor
1165      *
1166      * @param newObj is this constructor invoked with the new operator
1167      * @param self   self reference
1168      * @param arg    the arg
1169      *
1170      * @return new NativeString containing the string representation of the arg
1171      */
1172     @SpecializedFunction(isConstructor=true)
1173     public static Object constructor(final boolean newObj, final Object self, final int arg) {
1174         final String str = Integer.toString(arg);
1175         return newObj ? newObj(str) : str;
1176     }
1177 
1178     /**
1179      * ECMA 15.5.2.1 new String ( [ value ] ) - special version with exactly one {@code int} arg
1180      *
1181      * Constructor
1182      *
1183      * @param newObj is this constructor invoked with the new operator
1184      * @param self   self reference
1185      * @param arg    the arg
1186      *
1187      * @return new NativeString containing the string representation of the arg
1188      */
1189     @SpecializedFunction(isConstructor=true)
1190     public static Object constructor(final boolean newObj, final Object self, final long arg) {
1191         final String str = Long.toString(arg);
1192         return newObj ? newObj(str) : str;
1193     }
1194 
1195     /**
1196      * ECMA 15.5.2.1 new String ( [ value ] ) - special version with exactly one {@code int} arg
1197      *
1198      * Constructor
1199      *
1200      * @param newObj is this constructor invoked with the new operator
1201      * @param self   self reference
1202      * @param arg    the arg
1203      *
1204      * @return new NativeString containing the string representation of the arg
1205      */
1206     @SpecializedFunction(isConstructor=true)
1207     public static Object constructor(final boolean newObj, final Object self, final double arg) {
1208         final String str = JSType.toString(arg);
1209         return newObj ? newObj(str) : str;
1210     }
1211 
1212     /**
1213      * ECMA 15.5.2.1 new String ( [ value ] ) - special version with exactly one {@code boolean} arg
1214      *
1215      * Constructor
1216      *
1217      * @param newObj is this constructor invoked with the new operator
1218      * @param self   self reference
1219      * @param arg    the arg
1220      *
1221      * @return new NativeString containing the string representation of the arg
1222      */
1223     @SpecializedFunction(isConstructor=true)
1224     public static Object constructor(final boolean newObj, final Object self, final boolean arg) {
1225         final String str = Boolean.toString(arg);
1226         return newObj ? newObj(str) : str;
1227     }
1228 
1229     /**
1230      * ECMA 6 21.1.3.27 String.prototype [ @@iterator ]( )
1231      *
1232      * @param self self reference
1233      * @return a string iterator
1234      */
1235     @Function(attributes = Attribute.NOT_ENUMERABLE, name = "@@iterator")
1236     public static Object getIterator(final Object self) {
1237         return new StringIterator(checkObjectToString(self), Global.instance());
1238     }
1239 
1240     /**
1241      * Lookup the appropriate method for an invoke dynamic call.
1242      *
1243      * @param request  the link request
1244      * @param receiver receiver of call
1245      * @return Link to be invoked at call site.
1246      */
1247     public static GuardedInvocation lookupPrimitive(final LinkRequest request, final Object receiver) {
1248         return PrimitiveLookup.lookupPrimitive(request, NashornGuards.getStringGuard(),
1249                 new NativeString((CharSequence)receiver), WRAPFILTER, PROTOFILTER);
1250     }
1251 
1252     @SuppressWarnings("unused")
1253     private static NativeString wrapFilter(final Object receiver) {
1254         return new NativeString((CharSequence)receiver);
1255     }
1256 
1257     @SuppressWarnings("unused")
1258     private static Object protoFilter(final Object object) {
1259         return Global.instance().getStringPrototype();
1260     }
1261 
1262     private static CharSequence getCharSequence(final Object self) {
1263         if (JSType.isString(self)) {
1264             return (CharSequence)self;
1265         } else if (self instanceof NativeString) {
1266             return ((NativeString)self).getValue();
1267         } else if (self != null && self == Global.instance().getStringPrototype()) {
1268             return "";
1269         } else {
1270             throw typeError("not.a.string", ScriptRuntime.safeToString(self));
1271         }
1272     }
1273 
1274     private static String getString(final Object self) {
1275         if (self instanceof String) {
1276             return (String)self;
1277         } else if (self instanceof ConsString) {
1278             return self.toString();
1279         } else if (self instanceof NativeString) {
1280             return ((NativeString)self).getStringValue();
1281         } else if (self != null && self == Global.instance().getStringPrototype()) {
1282             return "";
1283         } else {
1284             throw typeError("not.a.string", ScriptRuntime.safeToString(self));
1285         }
1286     }
1287 
1288     /**
1289      * Combines ECMA 9.10 CheckObjectCoercible and ECMA 9.8 ToString with a shortcut for strings.
1290      *
1291      * @param self the object
1292      * @return the object as string
1293      */
1294     private static String checkObjectToString(final Object self) {
1295         if (self instanceof String) {
1296             return (String)self;
1297         } else if (self instanceof ConsString) {
1298             return self.toString();
1299         } else {
1300             Global.checkObjectCoercible(self);
1301             return JSType.toString(self);
1302         }
1303     }
1304 
1305     private boolean isValidStringIndex(final int key) {
1306         return key >= 0 && key < value.length();
1307     }
1308 
1309     private static MethodHandle findOwnMH(final String name, final MethodType type) {
1310         return MH.findStatic(MethodHandles.lookup(), NativeString.class, name, type);
1311     }
1312 
1313     @Override
1314     public LinkLogic getLinkLogic(final Class<? extends LinkLogic> clazz) {
1315         if (clazz == CharCodeAtLinkLogic.class) {
1316             return CharCodeAtLinkLogic.INSTANCE;
1317         }
1318         return null;
1319     }
1320 
1321     @Override
1322     public boolean hasPerInstanceAssumptions() {
1323         return false;
1324     }
1325 
1326     /**
1327      * This is linker logic charCodeAt - when we specialize further methods in NativeString
1328      * It may be expanded. It's link check makes sure that we are dealing with a char
1329      * sequence and that we are in range
1330      */
1331     private static final class CharCodeAtLinkLogic extends SpecializedFunction.LinkLogic {
1332         private static final CharCodeAtLinkLogic INSTANCE = new CharCodeAtLinkLogic();
1333 
1334         @Override
1335         public boolean canLink(final Object self, final CallSiteDescriptor desc, final LinkRequest request) {
1336             try {
1337                 //check that it's a char sequence or throw cce
1338                 final CharSequence cs = (CharSequence)self;
1339                 //check that the index, representable as an int, is inside the array
1340                 final int intIndex = JSType.toInteger(request.getArguments()[2]);
1341                 return intIndex >= 0 && intIndex < cs.length(); //can link
1342             } catch (final ClassCastException | IndexOutOfBoundsException e) {
1343                 //fallthru
1344             }
1345             return false;
1346         }
1347 
1348         /**
1349          * charCodeAt callsites can throw ClassCastException as a mechanism to have them
1350          * relinked - this enabled fast checks of the kind of ((IntArrayData)arrayData).push(x)
1351          * for an IntArrayData only push - if this fails, a CCE will be thrown and we will relink
1352          */
1353         @Override
1354         public Class<? extends Throwable> getRelinkException() {
1355             return ClassCastException.class;
1356         }
1357     }
1358 }