NativeString.java revision 1092:569b6de2d343
1104349Sphk/*
2104349Sphk * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
3104349Sphk * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4104349Sphk *
5104349Sphk * This code is free software; you can redistribute it and/or modify it
6104349Sphk * under the terms of the GNU General Public License version 2 only, as
7104349Sphk * published by the Free Software Foundation.  Oracle designates this
8104349Sphk * particular file as subject to the "Classpath" exception as provided
9104349Sphk * by Oracle in the LICENSE file that accompanied this code.
10104349Sphk *
11104349Sphk * This code is distributed in the hope that it will be useful, but WITHOUT
12104349Sphk * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13104349Sphk * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14104349Sphk * version 2 for more details (a copy is included in the LICENSE file that
15104349Sphk * accompanied this code).
16104349Sphk *
17104349Sphk * You should have received a copy of the GNU General Public License version
18104349Sphk * 2 along with this work; if not, write to the Free Software Foundation,
19104349Sphk * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20178848Scokane *
21104349Sphk * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22104349Sphk * or visit www.oracle.com if you need additional information or have any
23104349Sphk * questions.
24104349Sphk */
25104349Sphk
26104349Sphkpackage jdk.nashorn.internal.objects;
27104349Sphk
28104349Sphkimport static jdk.nashorn.internal.lookup.Lookup.MH;
29104349Sphkimport static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
30104349Sphkimport static jdk.nashorn.internal.runtime.JSType.isRepresentableAsInt;
31104349Sphkimport static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
32104349Sphk
33104349Sphkimport java.lang.invoke.MethodHandle;
34104349Sphkimport java.lang.invoke.MethodHandles;
35104349Sphkimport java.lang.invoke.MethodType;
36104349Sphkimport java.text.Collator;
37104349Sphkimport java.util.ArrayList;
38104349Sphkimport java.util.Arrays;
39104349Sphkimport java.util.LinkedList;
40104349Sphkimport java.util.List;
41104349Sphkimport java.util.Locale;
42104349Sphkimport java.util.Set;
43104349Sphkimport jdk.internal.dynalink.CallSiteDescriptor;
44104349Sphkimport jdk.internal.dynalink.linker.GuardedInvocation;
45104349Sphkimport jdk.internal.dynalink.linker.LinkRequest;
46104349Sphkimport jdk.nashorn.internal.lookup.MethodHandleFactory.LookupException;
47104349Sphkimport jdk.nashorn.internal.objects.annotations.Attribute;
48104349Sphkimport jdk.nashorn.internal.objects.annotations.Constructor;
49104349Sphkimport jdk.nashorn.internal.objects.annotations.Function;
50104349Sphkimport jdk.nashorn.internal.objects.annotations.Getter;
51104349Sphkimport jdk.nashorn.internal.objects.annotations.ScriptClass;
52104349Sphkimport jdk.nashorn.internal.objects.annotations.SpecializedFunction;
53104349Sphkimport jdk.nashorn.internal.objects.annotations.SpecializedFunction.LinkLogic;
54104349Sphkimport jdk.nashorn.internal.objects.annotations.Where;
55104349Sphkimport jdk.nashorn.internal.runtime.ConsString;
56104349Sphkimport jdk.nashorn.internal.runtime.JSType;
57104349Sphkimport jdk.nashorn.internal.runtime.OptimisticBuiltins;
58104349Sphkimport jdk.nashorn.internal.runtime.PropertyMap;
59104349Sphkimport jdk.nashorn.internal.runtime.ScriptFunction;
60104349Sphkimport jdk.nashorn.internal.runtime.ScriptObject;
61104349Sphkimport jdk.nashorn.internal.runtime.ScriptRuntime;
62104349Sphkimport jdk.nashorn.internal.runtime.arrays.ArrayIndex;
63104349Sphkimport jdk.nashorn.internal.runtime.linker.NashornGuards;
64104349Sphkimport jdk.nashorn.internal.runtime.linker.PrimitiveLookup;
65104349Sphk
66104349Sphk
67104349Sphk/**
68104349Sphk * ECMA 15.5 String Objects.
69104349Sphk */
70104349Sphk@ScriptClass("String")
71104349Sphkpublic final class NativeString extends ScriptObject implements OptimisticBuiltins {
72104349Sphk
73104349Sphk    private final CharSequence value;
74104349Sphk
75104349Sphk    /** Method handle to create an object wrapper for a primitive string */
76104349Sphk    static final MethodHandle WRAPFILTER = findOwnMH("wrapFilter", MH.type(NativeString.class, Object.class));
77104349Sphk    /** Method handle to retrieve the String prototype object */
78104349Sphk    private static final MethodHandle PROTOFILTER = findOwnMH("protoFilter", MH.type(Object.class, Object.class));
79104349Sphk
80104349Sphk    // initialized by nasgen
81104349Sphk    private static PropertyMap $nasgenmap$;
82104349Sphk
83104349Sphk    private NativeString(final CharSequence value) {
84104349Sphk        this(value, Global.instance());
85104349Sphk    }
86104349Sphk
87104349Sphk    NativeString(final CharSequence value, final Global global) {
88104349Sphk        this(value, global.getStringPrototype(), $nasgenmap$);
89104349Sphk    }
90104349Sphk
91104349Sphk    private NativeString(final CharSequence value, final ScriptObject proto, final PropertyMap map) {
92104349Sphk        super(proto, map);
93104349Sphk        assert value instanceof String || value instanceof ConsString;
94104349Sphk        this.value = value;
95104349Sphk    }
96104349Sphk
97104349Sphk    @Override
98104349Sphk    public String safeToString() {
99104349Sphk        return "[String " + toString() + "]";
100104349Sphk    }
101104349Sphk
102104349Sphk    @Override
103104349Sphk    public String toString() {
104104349Sphk        return getStringValue();
105104349Sphk    }
106104349Sphk
107104349Sphk    @Override
108104349Sphk    public boolean equals(final Object other) {
109104349Sphk        if (other instanceof NativeString) {
110104349Sphk            return getStringValue().equals(((NativeString) other).getStringValue());
111104349Sphk        }
112104349Sphk
113104349Sphk        return false;
114178848Scokane    }
115178848Scokane
116104349Sphk    @Override
117104349Sphk    public int hashCode() {
118104349Sphk        return getStringValue().hashCode();
119104349Sphk    }
120104349Sphk
121104349Sphk    private String getStringValue() {
122104349Sphk        return value instanceof String ? (String) value : value.toString();
123104349Sphk    }
124104349Sphk
125104349Sphk    private CharSequence getValue() {
126104349Sphk        return value;
127104349Sphk    }
128178848Scokane
129178848Scokane    @Override
130178848Scokane    public String getClassName() {
131178848Scokane        return "String";
132104349Sphk    }
133104349Sphk
134104349Sphk    @Override
135104349Sphk    public Object getLength() {
136178848Scokane        return value.length();
137178848Scokane    }
138178848Scokane
139178848Scokane    // This is to support length as method call as well.
140178848Scokane    @Override
141178848Scokane    protected GuardedInvocation findGetMethod(final CallSiteDescriptor desc, final LinkRequest request, final String operator) {
142178848Scokane        final String name = desc.getNameToken(2);
143178848Scokane
144178848Scokane        // if str.length(), then let the bean linker handle it
145178848Scokane        if ("length".equals(name) && "getMethod".equals(operator)) {
146178848Scokane            return null;
147178848Scokane        }
148178848Scokane
149178848Scokane        return super.findGetMethod(desc, request, operator);
150178848Scokane    }
151178848Scokane
152178848Scokane    // This is to provide array-like access to string characters without creating a NativeString wrapper.
153178848Scokane    @Override
154178848Scokane    protected GuardedInvocation findGetIndexMethod(final CallSiteDescriptor desc, final LinkRequest request) {
155178848Scokane        final Object self = request.getReceiver();
156178848Scokane        final Class<?> returnType = desc.getMethodType().returnType();
157178848Scokane
158178848Scokane        if (returnType == Object.class && (self instanceof String || self instanceof ConsString)) {
159178848Scokane            try {
160178848Scokane                return new GuardedInvocation(MH.findStatic(MethodHandles.lookup(), NativeString.class, "get", desc.getMethodType()), NashornGuards.getInstanceOf2Guard(String.class, ConsString.class));
161178848Scokane            } catch (final LookupException e) {
162178848Scokane                //empty. Shouldn't happen. Fall back to super
163178848Scokane            }
164178848Scokane        }
165178848Scokane        return super.findGetIndexMethod(desc, request);
166178848Scokane    }
167104349Sphk
168104349Sphk    @SuppressWarnings("unused")
169178848Scokane    private static Object get(final Object self, final Object key) {
170178848Scokane        final CharSequence cs = JSType.toCharSequence(self);
171104349Sphk        final Object primitiveKey = JSType.toPrimitive(key, String.class);
172104349Sphk        final int index = ArrayIndex.getArrayIndex(primitiveKey);
173104349Sphk        if (index >= 0 && index < cs.length()) {
174104349Sphk            return String.valueOf(cs.charAt(index));
175104349Sphk        }
176104349Sphk        return ((ScriptObject) Global.toObject(self)).get(primitiveKey);
177104349Sphk    }
178104349Sphk
179104349Sphk    @SuppressWarnings("unused")
180104349Sphk    private static Object get(final Object self, final double key) {
181104349Sphk        if (isRepresentableAsInt(key)) {
182104349Sphk            return get(self, (int)key);
183104349Sphk        }
184104349Sphk        return ((ScriptObject) Global.toObject(self)).get(key);
185104349Sphk    }
186104349Sphk
187104349Sphk    @SuppressWarnings("unused")
188104349Sphk    private static Object get(final Object self, final long key) {
189104349Sphk        final CharSequence cs = JSType.toCharSequence(self);
190104349Sphk        if (key >= 0 && key < cs.length()) {
191104349Sphk            return String.valueOf(cs.charAt((int)key));
192104349Sphk        }
193104349Sphk        return ((ScriptObject) Global.toObject(self)).get(key);
194104349Sphk    }
195104349Sphk
196104349Sphk    private static Object get(final Object self, final int key) {
197104349Sphk        final CharSequence cs = JSType.toCharSequence(self);
198104349Sphk        if (key >= 0 && key < cs.length()) {
199104349Sphk            return String.valueOf(cs.charAt(key));
200104349Sphk        }
201104349Sphk        return ((ScriptObject) Global.toObject(self)).get(key);
202104349Sphk    }
203104349Sphk
204104349Sphk    // String characters can be accessed with array-like indexing..
205104349Sphk    @Override
206104349Sphk    public Object get(final Object key) {
207104349Sphk        final Object primitiveKey = JSType.toPrimitive(key, String.class);
208104349Sphk        final int index = ArrayIndex.getArrayIndex(primitiveKey);
209104349Sphk        if (index >= 0 && index < value.length()) {
210104349Sphk            return String.valueOf(value.charAt(index));
211104349Sphk        }
212104349Sphk        return super.get(primitiveKey);
213104349Sphk    }
214104349Sphk
215104349Sphk    @Override
216104349Sphk    public Object get(final double key) {
217104349Sphk        if (isRepresentableAsInt(key)) {
218104349Sphk            return get((int)key);
219104349Sphk        }
220104349Sphk        return super.get(key);
221104349Sphk    }
222104349Sphk
223104349Sphk    @Override
224104349Sphk    public Object get(final long key) {
225104349Sphk        if (key >= 0 && key < value.length()) {
226104349Sphk            return String.valueOf(value.charAt((int)key));
227104349Sphk        }
228104349Sphk        return super.get(key);
229104349Sphk    }
230104349Sphk
231104349Sphk    @Override
232104349Sphk    public Object get(final int key) {
233104349Sphk        if (key >= 0 && key < value.length()) {
234104349Sphk            return String.valueOf(value.charAt(key));
235104349Sphk        }
236104349Sphk        return super.get(key);
237104349Sphk    }
238104349Sphk
239104349Sphk    @Override
240104349Sphk    public int getInt(final Object key, final int programPoint) {
241104349Sphk        return JSType.toInt32MaybeOptimistic(get(key), programPoint);
242104349Sphk    }
243104349Sphk
244104349Sphk    @Override
245104349Sphk    public int getInt(final double key, final int programPoint) {
246104349Sphk        return JSType.toInt32MaybeOptimistic(get(key), programPoint);
247104349Sphk    }
248104349Sphk
249104349Sphk    @Override
250104349Sphk    public int getInt(final long key, final int programPoint) {
251104349Sphk        return JSType.toInt32MaybeOptimistic(get(key), programPoint);
252104349Sphk    }
253104349Sphk
254104349Sphk    @Override
255104349Sphk    public int getInt(final int key, final int programPoint) {
256104349Sphk        return JSType.toInt32MaybeOptimistic(get(key), programPoint);
257104349Sphk    }
258104349Sphk
259104349Sphk    @Override
260104349Sphk    public long getLong(final Object key, final int programPoint) {
261104349Sphk        return JSType.toLongMaybeOptimistic(get(key), programPoint);
262104349Sphk    }
263104349Sphk
264104349Sphk    @Override
265104349Sphk    public long getLong(final double key, final int programPoint) {
266178848Scokane        return JSType.toLongMaybeOptimistic(get(key), programPoint);
267178848Scokane    }
268178848Scokane
269178848Scokane    @Override
270178848Scokane    public long getLong(final long key, final int programPoint) {
271178848Scokane        return JSType.toLongMaybeOptimistic(get(key), programPoint);
272178848Scokane    }
273178848Scokane
274178848Scokane    @Override
275178848Scokane    public long getLong(final int key, final int programPoint) {
276104349Sphk        return JSType.toLongMaybeOptimistic(get(key), programPoint);
277178848Scokane    }
278178848Scokane
279178848Scokane    @Override
280178848Scokane    public double getDouble(final Object key, final int programPoint) {
281178848Scokane        return JSType.toNumberMaybeOptimistic(get(key), programPoint);
282178848Scokane    }
283104349Sphk
284178848Scokane    @Override
285178848Scokane    public double getDouble(final double key, final int programPoint) {
286178848Scokane        return JSType.toNumberMaybeOptimistic(get(key), programPoint);
287178848Scokane    }
288104349Sphk
289104349Sphk    @Override
290178848Scokane    public double getDouble(final long key, final int programPoint) {
291104349Sphk        return JSType.toNumberMaybeOptimistic(get(key), programPoint);
292104349Sphk    }
293178848Scokane
294178848Scokane    @Override
295178848Scokane    public double getDouble(final int key, final int programPoint) {
296178848Scokane        return JSType.toNumberMaybeOptimistic(get(key), programPoint);
297178848Scokane    }
298178848Scokane
299178848Scokane    @Override
300178848Scokane    public boolean has(final Object key) {
301178848Scokane        final Object primitiveKey = JSType.toPrimitive(key, String.class);
302178848Scokane        final int index = ArrayIndex.getArrayIndex(primitiveKey);
303178848Scokane        return isValidStringIndex(index) || super.has(primitiveKey);
304178848Scokane    }
305178848Scokane
306178848Scokane    @Override
307178848Scokane    public boolean has(final int key) {
308104349Sphk        return isValidStringIndex(key) || super.has(key);
309104349Sphk    }
310178848Scokane
311104349Sphk    @Override
312104349Sphk    public boolean has(final long key) {
313104349Sphk        final int index = ArrayIndex.getArrayIndex(key);
314104349Sphk        return isValidStringIndex(index) || super.has(key);
315104349Sphk    }
316104349Sphk
317    @Override
318    public boolean has(final double key) {
319        final int index = ArrayIndex.getArrayIndex(key);
320        return isValidStringIndex(index) || super.has(key);
321    }
322
323    @Override
324    public boolean hasOwnProperty(final Object key) {
325        final Object primitiveKey = JSType.toPrimitive(key, String.class);
326        final int index = ArrayIndex.getArrayIndex(primitiveKey);
327        return isValidStringIndex(index) || super.hasOwnProperty(primitiveKey);
328    }
329
330    @Override
331    public boolean hasOwnProperty(final int key) {
332        return isValidStringIndex(key) || super.hasOwnProperty(key);
333    }
334
335    @Override
336    public boolean hasOwnProperty(final long key) {
337        final int index = ArrayIndex.getArrayIndex(key);
338        return isValidStringIndex(index) || super.hasOwnProperty(key);
339    }
340
341    @Override
342    public boolean hasOwnProperty(final double key) {
343        final int index = ArrayIndex.getArrayIndex(key);
344        return isValidStringIndex(index) || super.hasOwnProperty(key);
345    }
346
347    @Override
348    public boolean delete(final int key, final boolean strict) {
349        return checkDeleteIndex(key, strict)? false : super.delete(key, strict);
350    }
351
352    @Override
353    public boolean delete(final long key, final boolean strict) {
354        final int index = ArrayIndex.getArrayIndex(key);
355        return checkDeleteIndex(index, strict)? false : super.delete(key, strict);
356    }
357
358    @Override
359    public boolean delete(final double key, final boolean strict) {
360        final int index = ArrayIndex.getArrayIndex(key);
361        return checkDeleteIndex(index, strict)? false : super.delete(key, strict);
362    }
363
364    @Override
365    public boolean delete(final Object key, final boolean strict) {
366        final Object primitiveKey = JSType.toPrimitive(key, String.class);
367        final int index = ArrayIndex.getArrayIndex(primitiveKey);
368        return checkDeleteIndex(index, strict)? false : super.delete(primitiveKey, strict);
369    }
370
371    private boolean checkDeleteIndex(final int index, final boolean strict) {
372        if (isValidStringIndex(index)) {
373            if (strict) {
374                throw typeError("cant.delete.property", Integer.toString(index), ScriptRuntime.safeToString(this));
375            }
376            return true;
377        }
378
379        return false;
380    }
381
382    @Override
383    public Object getOwnPropertyDescriptor(final String key) {
384        final int index = ArrayIndex.getArrayIndex(key);
385        if (index >= 0 && index < value.length()) {
386            final Global global = Global.instance();
387            return global.newDataDescriptor(String.valueOf(value.charAt(index)), false, true, false);
388        }
389
390        return super.getOwnPropertyDescriptor(key);
391    }
392
393    /**
394     * return a List of own keys associated with the object.
395     * @param all True if to include non-enumerable keys.
396     * @param nonEnumerable set of non-enumerable properties seen already.Used
397     * to filter out shadowed, but enumerable properties from proto children.
398     * @return Array of keys.
399     */
400    @Override
401    protected String[] getOwnKeys(final boolean all, final Set<String> nonEnumerable) {
402        final List<Object> keys = new ArrayList<>();
403
404        // add string index keys
405        for (int i = 0; i < value.length(); i++) {
406            keys.add(JSType.toString(i));
407        }
408
409        // add super class properties
410        keys.addAll(Arrays.asList(super.getOwnKeys(all, nonEnumerable)));
411        return keys.toArray(new String[keys.size()]);
412    }
413
414    /**
415     * ECMA 15.5.3 String.length
416     * @param self self reference
417     * @return     value of length property for string
418     */
419    @Getter(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_WRITABLE | Attribute.NOT_CONFIGURABLE)
420    public static Object length(final Object self) {
421        return getCharSequence(self).length();
422    }
423
424    /**
425     * ECMA 15.5.3.2 String.fromCharCode ( [ char0 [ , char1 [ , ... ] ] ] )
426     * @param self  self reference
427     * @param args  array of arguments to be interpreted as char
428     * @return string with arguments translated to charcodes
429     */
430    @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1, where = Where.CONSTRUCTOR)
431    public static String fromCharCode(final Object self, final Object... args) {
432        final char[] buf = new char[args.length];
433        int index = 0;
434        for (final Object arg : args) {
435            buf[index++] = (char)JSType.toUint16(arg);
436        }
437        return new String(buf);
438    }
439
440    /**
441     * ECMA 15.5.3.2 - specialization for one char
442     * @param self  self reference
443     * @param value one argument to be interpreted as char
444     * @return string with one charcode
445     */
446    @SpecializedFunction
447    public static Object fromCharCode(final Object self, final Object value) {
448        if (value instanceof Integer) {
449            return fromCharCode(self, (int)value);
450        }
451        return Character.toString((char)JSType.toUint16(value));
452    }
453
454    /**
455     * ECMA 15.5.3.2 - specialization for one char of int type
456     * @param self  self reference
457     * @param value one argument to be interpreted as char
458     * @return string with one charcode
459     */
460    @SpecializedFunction
461    public static String fromCharCode(final Object self, final int value) {
462        return Character.toString((char)(value & 0xffff));
463    }
464
465    /**
466     * ECMA 15.5.3.2 - specialization for two chars of int type
467     * @param self  self reference
468     * @param ch1 first char
469     * @param ch2 second char
470     * @return string with one charcode
471     */
472    @SpecializedFunction
473    public static Object fromCharCode(final Object self, final int ch1, final int ch2) {
474        return Character.toString((char)(ch1 & 0xffff)) + Character.toString((char)(ch2 & 0xffff));
475    }
476
477    /**
478     * ECMA 15.5.3.2 - specialization for three chars of int type
479     * @param self  self reference
480     * @param ch1 first char
481     * @param ch2 second char
482     * @param ch3 third char
483     * @return string with one charcode
484     */
485    @SpecializedFunction
486    public static Object fromCharCode(final Object self, final int ch1, final int ch2, final int ch3) {
487        return Character.toString((char)(ch1 & 0xffff)) + Character.toString((char)(ch2 & 0xffff)) + Character.toString((char)(ch3 & 0xffff));
488    }
489
490    /**
491     * ECMA 15.5.3.2 - specialization for four chars of int type
492     * @param self  self reference
493     * @param ch1 first char
494     * @param ch2 second char
495     * @param ch3 third char
496     * @param ch4 fourth char
497     * @return string with one charcode
498     */
499    @SpecializedFunction
500    public static String fromCharCode(final Object self, final int ch1, final int ch2, final int ch3, final int ch4) {
501        return Character.toString((char)(ch1 & 0xffff)) + Character.toString((char)(ch2 & 0xffff)) + Character.toString((char)(ch3 & 0xffff)) + Character.toString((char)(ch4 & 0xffff));
502    }
503
504    /**
505     * ECMA 15.5.3.2 - specialization for one char of double type
506     * @param self  self reference
507     * @param value one argument to be interpreted as char
508     * @return string with one charcode
509     */
510    @SpecializedFunction
511    public static String fromCharCode(final Object self, final double value) {
512        return Character.toString((char)JSType.toUint16(value));
513    }
514
515    /**
516     * ECMA 15.5.4.2 String.prototype.toString ( )
517     * @param self self reference
518     * @return self as string
519     */
520    @Function(attributes = Attribute.NOT_ENUMERABLE)
521    public static String toString(final Object self) {
522        return getString(self);
523    }
524
525    /**
526     * ECMA 15.5.4.3 String.prototype.valueOf ( )
527     * @param self self reference
528     * @return self as string
529     */
530    @Function(attributes = Attribute.NOT_ENUMERABLE)
531    public static String valueOf(final Object self) {
532        return getString(self);
533    }
534
535    /**
536     * ECMA 15.5.4.4 String.prototype.charAt (pos)
537     * @param self self reference
538     * @param pos  position in string
539     * @return string representing the char at the given position
540     */
541    @Function(attributes = Attribute.NOT_ENUMERABLE)
542    public static String charAt(final Object self, final Object pos) {
543        return charAtImpl(checkObjectToString(self), JSType.toInteger(pos));
544    }
545
546    /**
547     * ECMA 15.5.4.4 String.prototype.charAt (pos) - specialized version for double position
548     * @param self self reference
549     * @param pos  position in string
550     * @return string representing the char at the given position
551     */
552    @SpecializedFunction
553    public static String charAt(final Object self, final double pos) {
554        return charAt(self, (int)pos);
555    }
556
557    /**
558     * ECMA 15.5.4.4 String.prototype.charAt (pos) - specialized version for int position
559     * @param self self reference
560     * @param pos  position in string
561     * @return string representing the char at the given position
562     */
563    @SpecializedFunction
564    public static String charAt(final Object self, final int pos) {
565        return charAtImpl(checkObjectToString(self), pos);
566    }
567
568    private static String charAtImpl(final String str, final int pos) {
569        return pos < 0 || pos >= str.length() ? "" : String.valueOf(str.charAt(pos));
570    }
571
572    private static int getValidChar(final Object self, final int pos) {
573        try {
574            return ((CharSequence)self).charAt(pos);
575        } catch (final IndexOutOfBoundsException e) {
576            throw new ClassCastException(); //invalid char, out of bounds, force relink
577        }
578    }
579
580    /**
581     * ECMA 15.5.4.5 String.prototype.charCodeAt (pos)
582     * @param self self reference
583     * @param pos  position in string
584     * @return number representing charcode at position
585     */
586    @Function(attributes = Attribute.NOT_ENUMERABLE)
587    public static double charCodeAt(final Object self, final Object pos) {
588        final String str = checkObjectToString(self);
589        final int    idx = JSType.toInteger(pos);
590        return idx < 0 || idx >= str.length() ? Double.NaN : str.charAt(idx);
591    }
592
593    /**
594     * ECMA 15.5.4.5 String.prototype.charCodeAt (pos) - specialized version for double position
595     * @param self self reference
596     * @param pos  position in string
597     * @return number representing charcode at position
598     */
599    @SpecializedFunction(linkLogic=CharCodeAtLinkLogic.class)
600    public static int charCodeAt(final Object self, final double pos) {
601        return charCodeAt(self, (int)pos); //toInt pos is ok
602    }
603
604    /**
605     * ECMA 15.5.4.5 String.prototype.charCodeAt (pos) - specialized version for long position
606     * @param self self reference
607     * @param pos  position in string
608     * @return number representing charcode at position
609     */
610    @SpecializedFunction(linkLogic=CharCodeAtLinkLogic.class)
611    public static int charCodeAt(final Object self, final long pos) {
612        return charCodeAt(self, (int)pos);
613    }
614
615    /**
616     * ECMA 15.5.4.5 String.prototype.charCodeAt (pos) - specialized version for int position
617     * @param self self reference
618     * @param pos  position in string
619     * @return number representing charcode at position
620     */
621
622    @SpecializedFunction(linkLogic=CharCodeAtLinkLogic.class)
623    public static int charCodeAt(final Object self, final int pos) {
624        return getValidChar(self, pos);
625    }
626
627    /**
628     * ECMA 15.5.4.6 String.prototype.concat ( [ string1 [ , string2 [ , ... ] ] ] )
629     * @param self self reference
630     * @param args list of string to concatenate
631     * @return concatenated string
632     */
633    @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
634    public static Object concat(final Object self, final Object... args) {
635        CharSequence cs = checkObjectToString(self);
636        if (args != null) {
637            for (final Object obj : args) {
638                cs = new ConsString(cs, JSType.toCharSequence(obj));
639            }
640        }
641        return cs;
642    }
643
644    /**
645     * ECMA 15.5.4.7 String.prototype.indexOf (searchString, position)
646     * @param self   self reference
647     * @param search string to search for
648     * @param pos    position to start search
649     * @return position of first match or -1
650     */
651    @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
652    public static int indexOf(final Object self, final Object search, final Object pos) {
653        final String str = checkObjectToString(self);
654        return str.indexOf(JSType.toString(search), JSType.toInteger(pos));
655    }
656
657    /**
658     * ECMA 15.5.4.7 String.prototype.indexOf (searchString, position) specialized for no position parameter
659     * @param self   self reference
660     * @param search string to search for
661     * @return position of first match or -1
662     */
663    @SpecializedFunction
664    public static int indexOf(final Object self, final Object search) {
665        return indexOf(self, search, 0);
666    }
667
668    /**
669     * ECMA 15.5.4.7 String.prototype.indexOf (searchString, position) specialized for double position parameter
670     * @param self   self reference
671     * @param search string to search for
672     * @param pos    position to start search
673     * @return position of first match or -1
674     */
675    @SpecializedFunction
676    public static int indexOf(final Object self, final Object search, final double pos) {
677        return indexOf(self, search, (int) pos);
678    }
679
680    /**
681     * ECMA 15.5.4.7 String.prototype.indexOf (searchString, position) specialized for int position parameter
682     * @param self   self reference
683     * @param search string to search for
684     * @param pos    position to start search
685     * @return position of first match or -1
686     */
687    @SpecializedFunction
688    public static int indexOf(final Object self, final Object search, final int pos) {
689        return checkObjectToString(self).indexOf(JSType.toString(search), pos);
690    }
691
692    /**
693     * ECMA 15.5.4.8 String.prototype.lastIndexOf (searchString, position)
694     * @param self   self reference
695     * @param search string to search for
696     * @param pos    position to start search
697     * @return last position of match or -1
698     */
699    @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
700    public static int lastIndexOf(final Object self, final Object search, final Object pos) {
701
702        final String str       = checkObjectToString(self);
703        final String searchStr = JSType.toString(search);
704        final int length       = str.length();
705
706        int end;
707
708        if (pos == UNDEFINED) {
709            end = length;
710        } else {
711            final double numPos = JSType.toNumber(pos);
712            end = Double.isNaN(numPos) ? length : (int)numPos;
713            if (end < 0) {
714                end = 0;
715            } else if (end > length) {
716                end = length;
717            }
718        }
719
720
721        return str.lastIndexOf(searchStr, end);
722    }
723
724    /**
725     * ECMA 15.5.4.9 String.prototype.localeCompare (that)
726     * @param self self reference
727     * @param that comparison object
728     * @return result of locale sensitive comparison operation between {@code self} and {@code that}
729     */
730    @Function(attributes = Attribute.NOT_ENUMERABLE)
731    public static double localeCompare(final Object self, final Object that) {
732
733        final String   str      = checkObjectToString(self);
734        final Collator collator = Collator.getInstance(Global.getEnv()._locale);
735
736        collator.setStrength(Collator.IDENTICAL);
737        collator.setDecomposition(Collator.CANONICAL_DECOMPOSITION);
738
739        return collator.compare(str, JSType.toString(that));
740    }
741
742    /**
743     * ECMA 15.5.4.10 String.prototype.match (regexp)
744     * @param self   self reference
745     * @param regexp regexp expression
746     * @return array of regexp matches
747     */
748    @Function(attributes = Attribute.NOT_ENUMERABLE)
749    public static ScriptObject match(final Object self, final Object regexp) {
750
751        final String str = checkObjectToString(self);
752
753        NativeRegExp nativeRegExp;
754        if (regexp == UNDEFINED) {
755            nativeRegExp = new NativeRegExp("");
756        } else {
757            nativeRegExp = Global.toRegExp(regexp);
758        }
759
760        if (!nativeRegExp.getGlobal()) {
761            return nativeRegExp.exec(str);
762        }
763
764        nativeRegExp.setLastIndex(0);
765
766        int previousLastIndex = 0;
767        final List<Object> matches = new ArrayList<>();
768
769        Object result;
770        while ((result = nativeRegExp.exec(str)) != null) {
771            final int thisIndex = nativeRegExp.getLastIndex();
772            if (thisIndex == previousLastIndex) {
773                nativeRegExp.setLastIndex(thisIndex + 1);
774                previousLastIndex = thisIndex + 1;
775            } else {
776                previousLastIndex = thisIndex;
777            }
778            matches.add(((ScriptObject)result).get(0));
779        }
780
781        if (matches.isEmpty()) {
782            return null;
783        }
784
785        return new NativeArray(matches.toArray());
786    }
787
788    /**
789     * ECMA 15.5.4.11 String.prototype.replace (searchValue, replaceValue)
790     * @param self        self reference
791     * @param string      item to replace
792     * @param replacement item to replace it with
793     * @return string after replacement
794     * @throws Throwable if replacement fails
795     */
796    @Function(attributes = Attribute.NOT_ENUMERABLE)
797    public static String replace(final Object self, final Object string, final Object replacement) throws Throwable {
798
799        final String str = checkObjectToString(self);
800
801        final NativeRegExp nativeRegExp;
802        if (string instanceof NativeRegExp) {
803            nativeRegExp = (NativeRegExp) string;
804        } else {
805            nativeRegExp = NativeRegExp.flatRegExp(JSType.toString(string));
806        }
807
808        if (replacement instanceof ScriptFunction) {
809            return nativeRegExp.replace(str, "", (ScriptFunction)replacement);
810        }
811
812        return nativeRegExp.replace(str, JSType.toString(replacement), null);
813    }
814
815    /**
816     * ECMA 15.5.4.12 String.prototype.search (regexp)
817     *
818     * @param self    self reference
819     * @param string  string to search for
820     * @return offset where match occurred
821     */
822    @Function(attributes = Attribute.NOT_ENUMERABLE)
823    public static int search(final Object self, final Object string) {
824
825        final String       str          = checkObjectToString(self);
826        final NativeRegExp nativeRegExp = Global.toRegExp(string == UNDEFINED ? "" : string);
827
828        return nativeRegExp.search(str);
829    }
830
831    /**
832     * ECMA 15.5.4.13 String.prototype.slice (start, end)
833     *
834     * @param self  self reference
835     * @param start start position for slice
836     * @param end   end position for slice
837     * @return sliced out substring
838     */
839    @Function(attributes = Attribute.NOT_ENUMERABLE)
840    public static String slice(final Object self, final Object start, final Object end) {
841
842        final String str      = checkObjectToString(self);
843        if (end == UNDEFINED) {
844            return slice(str, JSType.toInteger(start));
845        }
846        return slice(str, JSType.toInteger(start), JSType.toInteger(end));
847    }
848
849    /**
850     * ECMA 15.5.4.13 String.prototype.slice (start, end) specialized for single int parameter
851     *
852     * @param self  self reference
853     * @param start start position for slice
854     * @return sliced out substring
855     */
856    @SpecializedFunction
857    public static String slice(final Object self, final int start) {
858        final String str = checkObjectToString(self);
859        final int from = start < 0 ? Math.max(str.length() + start, 0) : Math.min(start, str.length());
860
861        return str.substring(from);
862    }
863
864    /**
865     * ECMA 15.5.4.13 String.prototype.slice (start, end) specialized for single double parameter
866     *
867     * @param self  self reference
868     * @param start start position for slice
869     * @return sliced out substring
870     */
871    @SpecializedFunction
872    public static String slice(final Object self, final double start) {
873        return slice(self, (int)start);
874    }
875
876    /**
877     * ECMA 15.5.4.13 String.prototype.slice (start, end) specialized for two int parameters
878     *
879     * @param self  self reference
880     * @param start start position for slice
881     * @param end   end position for slice
882     * @return sliced out substring
883     */
884    @SpecializedFunction
885    public static String slice(final Object self, final int start, final int end) {
886
887        final String str = checkObjectToString(self);
888        final int len    = str.length();
889
890        final int from = start < 0 ? Math.max(len + start, 0) : Math.min(start, len);
891        final int to   = end < 0   ? Math.max(len + end, 0)   : Math.min(end, len);
892
893        return str.substring(Math.min(from, to), to);
894    }
895
896    /**
897     * ECMA 15.5.4.13 String.prototype.slice (start, end) specialized for two double parameters
898     *
899     * @param self  self reference
900     * @param start start position for slice
901     * @param end   end position for slice
902     * @return sliced out substring
903     */
904    @SpecializedFunction
905    public static String slice(final Object self, final double start, final double end) {
906        return slice(self, (int)start, (int)end);
907    }
908
909    /**
910     * ECMA 15.5.4.14 String.prototype.split (separator, limit)
911     *
912     * @param self      self reference
913     * @param separator separator for split
914     * @param limit     limit for splits
915     * @return array object in which splits have been placed
916     */
917    @Function(attributes = Attribute.NOT_ENUMERABLE)
918    public static ScriptObject split(final Object self, final Object separator, final Object limit) {
919        final String str = checkObjectToString(self);
920        final long lim = limit == UNDEFINED ? JSType.MAX_UINT : JSType.toUint32(limit);
921
922        if (separator == UNDEFINED) {
923            return lim == 0 ? new NativeArray() : new NativeArray(new Object[]{str});
924        }
925
926        if (separator instanceof NativeRegExp) {
927            return ((NativeRegExp) separator).split(str, lim);
928        }
929
930        // when separator is a string, it is treated as a literal search string to be used for splitting.
931        return splitString(str, JSType.toString(separator), lim);
932    }
933
934    private static ScriptObject splitString(final String str, final String separator, final long limit) {
935        if (separator.isEmpty()) {
936            final int length = (int) Math.min(str.length(), limit);
937            final Object[] array = new Object[length];
938            for (int i = 0; i < length; i++) {
939                array[i] = String.valueOf(str.charAt(i));
940            }
941            return new NativeArray(array);
942        }
943
944        final List<String> elements = new LinkedList<>();
945        final int strLength = str.length();
946        final int sepLength = separator.length();
947        int pos = 0;
948        int n = 0;
949
950        while (pos < strLength && n < limit) {
951            final int found = str.indexOf(separator, pos);
952            if (found == -1) {
953                break;
954            }
955            elements.add(str.substring(pos, found));
956            n++;
957            pos = found + sepLength;
958        }
959        if (pos <= strLength && n < limit) {
960            elements.add(str.substring(pos));
961        }
962
963        return new NativeArray(elements.toArray());
964    }
965
966    /**
967     * ECMA B.2.3 String.prototype.substr (start, length)
968     *
969     * @param self   self reference
970     * @param start  start position
971     * @param length length of section
972     * @return substring given start and length of section
973     */
974    @Function(attributes = Attribute.NOT_ENUMERABLE)
975    public static String substr(final Object self, final Object start, final Object length) {
976        final String str       = JSType.toString(self);
977        final int    strLength = str.length();
978
979        int intStart = JSType.toInteger(start);
980        if (intStart < 0) {
981            intStart = Math.max(intStart + strLength, 0);
982        }
983
984        final int intLen = Math.min(Math.max(length == UNDEFINED ? Integer.MAX_VALUE : JSType.toInteger(length), 0), strLength - intStart);
985
986        return intLen <= 0 ? "" : str.substring(intStart, intStart + intLen);
987    }
988
989    /**
990     * ECMA 15.5.4.15 String.prototype.substring (start, end)
991     *
992     * @param self  self reference
993     * @param start start position of substring
994     * @param end   end position of substring
995     * @return substring given start and end indexes
996     */
997    @Function(attributes = Attribute.NOT_ENUMERABLE)
998    public static String substring(final Object self, final Object start, final Object end) {
999
1000        final String str = checkObjectToString(self);
1001        if (end == UNDEFINED) {
1002            return substring(str, JSType.toInteger(start));
1003        }
1004        return substring(str, JSType.toInteger(start), JSType.toInteger(end));
1005    }
1006
1007    /**
1008     * ECMA 15.5.4.15 String.prototype.substring (start, end) specialized for int start parameter
1009     *
1010     * @param self  self reference
1011     * @param start start position of substring
1012     * @return substring given start and end indexes
1013     */
1014    @SpecializedFunction
1015    public static String substring(final Object self, final int start) {
1016        final String str = checkObjectToString(self);
1017        if (start < 0) {
1018            return str;
1019        } else if (start >= str.length()) {
1020            return "";
1021        } else {
1022            return str.substring(start);
1023        }
1024    }
1025
1026    /**
1027     * ECMA 15.5.4.15 String.prototype.substring (start, end) specialized for double start parameter
1028     *
1029     * @param self  self reference
1030     * @param start start position of substring
1031     * @return substring given start and end indexes
1032     */
1033    @SpecializedFunction
1034    public static String substring(final Object self, final double start) {
1035        return substring(self, (int)start);
1036    }
1037
1038    /**
1039     * ECMA 15.5.4.15 String.prototype.substring (start, end) specialized for int start and end parameters
1040     *
1041     * @param self  self reference
1042     * @param start start position of substring
1043     * @param end   end position of substring
1044     * @return substring given start and end indexes
1045     */
1046    @SpecializedFunction
1047    public static String substring(final Object self, final int start, final int end) {
1048        final String str = checkObjectToString(self);
1049        final int len = str.length();
1050        final int validStart = start < 0 ? 0 : start > len ? len : start;
1051        final int validEnd   = end < 0 ? 0 : end > len ? len : end;
1052
1053        if (validStart < validEnd) {
1054            return str.substring(validStart, validEnd);
1055        }
1056        return str.substring(validEnd, validStart);
1057    }
1058
1059    /**
1060     * ECMA 15.5.4.15 String.prototype.substring (start, end) specialized for double start and end parameters
1061     *
1062     * @param self  self reference
1063     * @param start start position of substring
1064     * @param end   end position of substring
1065     * @return substring given start and end indexes
1066     */
1067    @SpecializedFunction
1068    public static String substring(final Object self, final double start, final double end) {
1069        return substring(self, (int)start, (int)end);
1070    }
1071
1072    /**
1073     * ECMA 15.5.4.16 String.prototype.toLowerCase ( )
1074     * @param self self reference
1075     * @return string to lower case
1076     */
1077    @Function(attributes = Attribute.NOT_ENUMERABLE)
1078    public static String toLowerCase(final Object self) {
1079        return checkObjectToString(self).toLowerCase(Locale.ROOT);
1080    }
1081
1082    /**
1083     * ECMA 15.5.4.17 String.prototype.toLocaleLowerCase ( )
1084     * @param self self reference
1085     * @return string to locale sensitive lower case
1086     */
1087    @Function(attributes = Attribute.NOT_ENUMERABLE)
1088    public static String toLocaleLowerCase(final Object self) {
1089        return checkObjectToString(self).toLowerCase(Global.getEnv()._locale);
1090    }
1091
1092    /**
1093     * ECMA 15.5.4.18 String.prototype.toUpperCase ( )
1094     * @param self self reference
1095     * @return string to upper case
1096     */
1097    @Function(attributes = Attribute.NOT_ENUMERABLE)
1098    public static String toUpperCase(final Object self) {
1099        return checkObjectToString(self).toUpperCase(Locale.ROOT);
1100    }
1101
1102    /**
1103     * ECMA 15.5.4.19 String.prototype.toLocaleUpperCase ( )
1104     * @param self self reference
1105     * @return string to locale sensitive upper case
1106     */
1107    @Function(attributes = Attribute.NOT_ENUMERABLE)
1108    public static String toLocaleUpperCase(final Object self) {
1109        return checkObjectToString(self).toUpperCase(Global.getEnv()._locale);
1110    }
1111
1112    /**
1113     * ECMA 15.5.4.20 String.prototype.trim ( )
1114     * @param self self reference
1115     * @return string trimmed from whitespace
1116     */
1117    @Function(attributes = Attribute.NOT_ENUMERABLE)
1118    public static String trim(final Object self) {
1119        final String str = checkObjectToString(self);
1120        int start = 0;
1121        int end   = str.length() - 1;
1122
1123        while (start <= end && ScriptRuntime.isJSWhitespace(str.charAt(start))) {
1124            start++;
1125        }
1126        while (end > start && ScriptRuntime.isJSWhitespace(str.charAt(end))) {
1127            end--;
1128        }
1129
1130        return str.substring(start, end + 1);
1131    }
1132
1133    /**
1134     * Nashorn extension: String.prototype.trimLeft ( )
1135     * @param self self reference
1136     * @return string trimmed left from whitespace
1137     */
1138    @Function(attributes = Attribute.NOT_ENUMERABLE)
1139    public static String trimLeft(final Object self) {
1140
1141        final String str = checkObjectToString(self);
1142        int start = 0;
1143        final int end   = str.length() - 1;
1144
1145        while (start <= end && ScriptRuntime.isJSWhitespace(str.charAt(start))) {
1146            start++;
1147        }
1148
1149        return str.substring(start, end + 1);
1150    }
1151
1152    /**
1153     * Nashorn extension: String.prototype.trimRight ( )
1154     * @param self self reference
1155     * @return string trimmed right from whitespace
1156     */
1157    @Function(attributes = Attribute.NOT_ENUMERABLE)
1158    public static String trimRight(final Object self) {
1159
1160        final String str = checkObjectToString(self);
1161        final int start = 0;
1162        int end   = str.length() - 1;
1163
1164        while (end >= start && ScriptRuntime.isJSWhitespace(str.charAt(end))) {
1165            end--;
1166        }
1167
1168        return str.substring(start, end + 1);
1169    }
1170
1171    private static ScriptObject newObj(final CharSequence str) {
1172        return new NativeString(str);
1173    }
1174
1175    /**
1176     * ECMA 15.5.2.1 new String ( [ value ] )
1177     *
1178     * Constructor
1179     *
1180     * @param newObj is this constructor invoked with the new operator
1181     * @param self   self reference
1182     * @param args   arguments (a value)
1183     *
1184     * @return new NativeString, empty string if no args, extraneous args ignored
1185     */
1186    @Constructor(arity = 1)
1187    public static Object constructor(final boolean newObj, final Object self, final Object... args) {
1188        final CharSequence str = args.length > 0 ? JSType.toCharSequence(args[0]) : "";
1189        return newObj ? newObj(str) : str.toString();
1190    }
1191
1192    /**
1193     * ECMA 15.5.2.1 new String ( [ value ] ) - special version with no args
1194     *
1195     * Constructor
1196     *
1197     * @param newObj is this constructor invoked with the new operator
1198     * @param self   self reference
1199     *
1200     * @return new NativeString ("")
1201     */
1202    @SpecializedFunction(isConstructor=true)
1203    public static Object constructor(final boolean newObj, final Object self) {
1204        return newObj ? newObj("") : "";
1205    }
1206
1207    /**
1208     * ECMA 15.5.2.1 new String ( [ value ] ) - special version with one arg
1209     *
1210     * Constructor
1211     *
1212     * @param newObj is this constructor invoked with the new operator
1213     * @param self   self reference
1214     * @param arg    argument
1215     *
1216     * @return new NativeString (arg)
1217     */
1218    @SpecializedFunction(isConstructor=true)
1219    public static Object constructor(final boolean newObj, final Object self, final Object arg) {
1220        final CharSequence str = JSType.toCharSequence(arg);
1221        return newObj ? newObj(str) : str.toString();
1222    }
1223
1224    /**
1225     * ECMA 15.5.2.1 new String ( [ value ] ) - special version with exactly one {@code int} arg
1226     *
1227     * Constructor
1228     *
1229     * @param newObj is this constructor invoked with the new operator
1230     * @param self   self reference
1231     * @param arg    the arg
1232     *
1233     * @return new NativeString containing the string representation of the arg
1234     */
1235    @SpecializedFunction(isConstructor=true)
1236    public static Object constructor(final boolean newObj, final Object self, final int arg) {
1237        final String str = Integer.toString(arg);
1238        return newObj ? newObj(str) : str;
1239    }
1240
1241    /**
1242     * ECMA 15.5.2.1 new String ( [ value ] ) - special version with exactly one {@code int} arg
1243     *
1244     * Constructor
1245     *
1246     * @param newObj is this constructor invoked with the new operator
1247     * @param self   self reference
1248     * @param arg    the arg
1249     *
1250     * @return new NativeString containing the string representation of the arg
1251     */
1252    @SpecializedFunction(isConstructor=true)
1253    public static Object constructor(final boolean newObj, final Object self, final long arg) {
1254        final String str = Long.toString(arg);
1255        return newObj ? newObj(str) : str;
1256    }
1257
1258    /**
1259     * ECMA 15.5.2.1 new String ( [ value ] ) - special version with exactly one {@code int} arg
1260     *
1261     * Constructor
1262     *
1263     * @param newObj is this constructor invoked with the new operator
1264     * @param self   self reference
1265     * @param arg    the arg
1266     *
1267     * @return new NativeString containing the string representation of the arg
1268     */
1269    @SpecializedFunction(isConstructor=true)
1270    public static Object constructor(final boolean newObj, final Object self, final double arg) {
1271        final String str = JSType.toString(arg);
1272        return newObj ? newObj(str) : str;
1273    }
1274
1275    /**
1276     * ECMA 15.5.2.1 new String ( [ value ] ) - special version with exactly one {@code boolean} arg
1277     *
1278     * Constructor
1279     *
1280     * @param newObj is this constructor invoked with the new operator
1281     * @param self   self reference
1282     * @param arg    the arg
1283     *
1284     * @return new NativeString containing the string representation of the arg
1285     */
1286    @SpecializedFunction(isConstructor=true)
1287    public static Object constructor(final boolean newObj, final Object self, final boolean arg) {
1288        final String str = Boolean.toString(arg);
1289        return newObj ? newObj(str) : str;
1290    }
1291
1292    /**
1293     * Lookup the appropriate method for an invoke dynamic call.
1294     *
1295     * @param request  the link request
1296     * @param receiver receiver of call
1297     * @return Link to be invoked at call site.
1298     */
1299    public static GuardedInvocation lookupPrimitive(final LinkRequest request, final Object receiver) {
1300        final MethodHandle guard = NashornGuards.getInstanceOf2Guard(String.class, ConsString.class);
1301        return PrimitiveLookup.lookupPrimitive(request, guard, new NativeString((CharSequence)receiver), WRAPFILTER, PROTOFILTER);
1302    }
1303
1304    @SuppressWarnings("unused")
1305    private static NativeString wrapFilter(final Object receiver) {
1306        return new NativeString((CharSequence)receiver);
1307    }
1308
1309    @SuppressWarnings("unused")
1310    private static Object protoFilter(final Object object) {
1311        return Global.instance().getStringPrototype();
1312    }
1313
1314    private static CharSequence getCharSequence(final Object self) {
1315        if (self instanceof String || self instanceof ConsString) {
1316            return (CharSequence)self;
1317        } else if (self instanceof NativeString) {
1318            return ((NativeString)self).getValue();
1319        } else if (self != null && self == Global.instance().getStringPrototype()) {
1320            return "";
1321        } else {
1322            throw typeError("not.a.string", ScriptRuntime.safeToString(self));
1323        }
1324    }
1325
1326    private static String getString(final Object self) {
1327        if (self instanceof String) {
1328            return (String)self;
1329        } else if (self instanceof ConsString) {
1330            return self.toString();
1331        } else if (self instanceof NativeString) {
1332            return ((NativeString)self).getStringValue();
1333        } else if (self != null && self == Global.instance().getStringPrototype()) {
1334            return "";
1335        } else {
1336            throw typeError("not.a.string", ScriptRuntime.safeToString(self));
1337        }
1338    }
1339
1340    /**
1341     * Combines ECMA 9.10 CheckObjectCoercible and ECMA 9.8 ToString with a shortcut for strings.
1342     *
1343     * @param self the object
1344     * @return the object as string
1345     */
1346    private static String checkObjectToString(final Object self) {
1347        if (self instanceof String) {
1348            return (String)self;
1349        } else if (self instanceof ConsString) {
1350            return self.toString();
1351        } else {
1352            Global.checkObjectCoercible(self);
1353            return JSType.toString(self);
1354        }
1355    }
1356
1357    private boolean isValidStringIndex(final int key) {
1358        return key >= 0 && key < value.length();
1359    }
1360
1361    private static MethodHandle findOwnMH(final String name, final MethodType type) {
1362        return MH.findStatic(MethodHandles.lookup(), NativeString.class, name, type);
1363    }
1364
1365    @Override
1366    public LinkLogic getLinkLogic(final Class<? extends LinkLogic> clazz) {
1367        if (clazz == CharCodeAtLinkLogic.class) {
1368            return CharCodeAtLinkLogic.INSTANCE;
1369        }
1370        return null;
1371    }
1372
1373    @Override
1374    public boolean hasPerInstanceAssumptions() {
1375        return false;
1376    }
1377
1378    /**
1379     * This is linker logic charCodeAt - when we specialize further methods in NativeString
1380     * It may be expanded. It's link check makes sure that we are dealing with a char
1381     * sequence and that we are in range
1382     */
1383    private static final class CharCodeAtLinkLogic extends SpecializedFunction.LinkLogic {
1384        private static final CharCodeAtLinkLogic INSTANCE = new CharCodeAtLinkLogic();
1385
1386        @Override
1387        public boolean canLink(final Object self, final CallSiteDescriptor desc, final LinkRequest request) {
1388            try {
1389                //check that it's a char sequence or throw cce
1390                final CharSequence cs = (CharSequence)self;
1391                //check that the index, representable as an int, is inside the array
1392                final int intIndex = JSType.toInteger(request.getArguments()[2]);
1393                return intIndex >= 0 && intIndex < cs.length(); //can link
1394            } catch (final ClassCastException | IndexOutOfBoundsException e) {
1395                //fallthru
1396            }
1397            return false;
1398        }
1399
1400        /**
1401         * charCodeAt callsites can throw ClassCastException as a mechanism to have them
1402         * relinked - this enabled fast checks of the kind of ((IntArrayData)arrayData).push(x)
1403         * for an IntArrayData only push - if this fails, a CCE will be thrown and we will relink
1404         */
1405        @Override
1406        public Class<? extends Throwable> getRelinkException() {
1407            return ClassCastException.class;
1408        }
1409    }
1410}
1411