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