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