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