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