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