/*
 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package jdk.nashorn.internal.objects;

import static jdk.nashorn.internal.lookup.Lookup.MH;
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
import static jdk.nashorn.internal.runtime.JSType.isRepresentableAsInt;
import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Array;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import jdk.dynalink.CallSiteDescriptor;
import jdk.dynalink.linker.GuardedInvocation;
import jdk.dynalink.linker.LinkRequest;
import jdk.nashorn.internal.lookup.MethodHandleFactory.LookupException;
import jdk.nashorn.internal.objects.annotations.Attribute;
import jdk.nashorn.internal.objects.annotations.Constructor;
import jdk.nashorn.internal.objects.annotations.Function;
import jdk.nashorn.internal.objects.annotations.Getter;
import jdk.nashorn.internal.objects.annotations.ScriptClass;
import jdk.nashorn.internal.objects.annotations.SpecializedFunction;
import jdk.nashorn.internal.objects.annotations.SpecializedFunction.LinkLogic;
import jdk.nashorn.internal.objects.annotations.Where;
import jdk.nashorn.internal.runtime.ConsString;
import jdk.nashorn.internal.runtime.JSType;
import jdk.nashorn.internal.runtime.OptimisticBuiltins;
import jdk.nashorn.internal.runtime.PropertyMap;
import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.runtime.ScriptRuntime;
import jdk.nashorn.internal.runtime.arrays.ArrayIndex;
import jdk.nashorn.internal.runtime.linker.Bootstrap;
import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
import jdk.nashorn.internal.runtime.linker.NashornGuards;
import jdk.nashorn.internal.runtime.linker.PrimitiveLookup;


ECMA 15.5 String Objects.
/** * ECMA 15.5 String Objects. */
@ScriptClass("String") public final class NativeString extends ScriptObject implements OptimisticBuiltins { private final CharSequence value;
Method handle to create an object wrapper for a primitive string
/** Method handle to create an object wrapper for a primitive string */
static final MethodHandle WRAPFILTER = findOwnMH("wrapFilter", MH.type(NativeString.class, Object.class));
Method handle to retrieve the String prototype object
/** Method handle to retrieve the String prototype object */
private static final MethodHandle PROTOFILTER = findOwnMH("protoFilter", MH.type(Object.class, Object.class)); // initialized by nasgen private static PropertyMap $nasgenmap$; private NativeString(final CharSequence value) { this(value, Global.instance()); } NativeString(final CharSequence value, final Global global) { this(value, global.getStringPrototype(), $nasgenmap$); } private NativeString(final CharSequence value, final ScriptObject proto, final PropertyMap map) { super(proto, map); assert JSType.isString(value); this.value = value; } @Override public String safeToString() { return "[String " + toString() + "]"; } @Override public String toString() { return getStringValue(); } private String getStringValue() { return value instanceof String ? (String) value : value.toString(); } private CharSequence getValue() { return value; } @Override public String getClassName() { return "String"; } @Override public Object getLength() { return value.length(); } // This is to support length as method call as well. @Override protected GuardedInvocation findGetMethod(final CallSiteDescriptor desc, final LinkRequest request) { final String name = NashornCallSiteDescriptor.getOperand(desc); // if str.length(), then let the bean linker handle it if ("length".equals(name) && NashornCallSiteDescriptor.isMethodFirstOperation(desc)) { return null; } return super.findGetMethod(desc, request); } // This is to provide array-like access to string characters without creating a NativeString wrapper. @Override protected GuardedInvocation findGetIndexMethod(final CallSiteDescriptor desc, final LinkRequest request) { final Object self = request.getReceiver(); final Class<?> returnType = desc.getMethodType().returnType(); if (returnType == Object.class && JSType.isString(self)) { try { return new GuardedInvocation(MH.findStatic(MethodHandles.lookup(), NativeString.class, "get", desc.getMethodType()), NashornGuards.getStringGuard()); } catch (final LookupException e) { //empty. Shouldn't happen. Fall back to super } } return super.findGetIndexMethod(desc, request); } @SuppressWarnings("unused") private static Object get(final Object self, final Object key) { final CharSequence cs = JSType.toCharSequence(self); final Object primitiveKey = JSType.toPrimitive(key, String.class); final int index = ArrayIndex.getArrayIndex(primitiveKey); if (index >= 0 && index < cs.length()) { return String.valueOf(cs.charAt(index)); } return ((ScriptObject) Global.toObject(self)).get(primitiveKey); } @SuppressWarnings("unused") private static Object get(final Object self, final double key) { if (isRepresentableAsInt(key)) { return get(self, (int)key); } return ((ScriptObject) Global.toObject(self)).get(key); } @SuppressWarnings("unused") private static Object get(final Object self, final long key) { final CharSequence cs = JSType.toCharSequence(self); if (key >= 0 && key < cs.length()) { return String.valueOf(cs.charAt((int)key)); } return ((ScriptObject) Global.toObject(self)).get(key); } private static Object get(final Object self, final int key) { final CharSequence cs = JSType.toCharSequence(self); if (key >= 0 && key < cs.length()) { return String.valueOf(cs.charAt(key)); } return ((ScriptObject) Global.toObject(self)).get(key); } // String characters can be accessed with array-like indexing.. @Override public Object get(final Object key) { final Object primitiveKey = JSType.toPrimitive(key, String.class); final int index = ArrayIndex.getArrayIndex(primitiveKey); if (index >= 0 && index < value.length()) { return String.valueOf(value.charAt(index)); } return super.get(primitiveKey); } @Override public Object get(final double key) { if (isRepresentableAsInt(key)) { return get((int)key); } return super.get(key); } @Override public Object get(final int key) { if (key >= 0 && key < value.length()) { return String.valueOf(value.charAt(key)); } return super.get(key); } @Override public int getInt(final Object key, final int programPoint) { return JSType.toInt32MaybeOptimistic(get(key), programPoint); } @Override public int getInt(final double key, final int programPoint) { return JSType.toInt32MaybeOptimistic(get(key), programPoint); } @Override public int getInt(final int key, final int programPoint) { return JSType.toInt32MaybeOptimistic(get(key), programPoint); } @Override public double getDouble(final Object key, final int programPoint) { return JSType.toNumberMaybeOptimistic(get(key), programPoint); } @Override public double getDouble(final double key, final int programPoint) { return JSType.toNumberMaybeOptimistic(get(key), programPoint); } @Override public double getDouble(final int key, final int programPoint) { return JSType.toNumberMaybeOptimistic(get(key), programPoint); } @Override public boolean has(final Object key) { final Object primitiveKey = JSType.toPrimitive(key, String.class); final int index = ArrayIndex.getArrayIndex(primitiveKey); return isValidStringIndex(index) || super.has(primitiveKey); } @Override public boolean has(final int key) { return isValidStringIndex(key) || super.has(key); } @Override public boolean has(final double key) { final int index = ArrayIndex.getArrayIndex(key); return isValidStringIndex(index) || super.has(key); } @Override public boolean hasOwnProperty(final Object key) { final Object primitiveKey = JSType.toPrimitive(key, String.class); final int index = ArrayIndex.getArrayIndex(primitiveKey); return isValidStringIndex(index) || super.hasOwnProperty(primitiveKey); } @Override public boolean hasOwnProperty(final int key) { return isValidStringIndex(key) || super.hasOwnProperty(key); } @Override public boolean hasOwnProperty(final double key) { final int index = ArrayIndex.getArrayIndex(key); return isValidStringIndex(index) || super.hasOwnProperty(key); } @Override public boolean delete(final int key, final boolean strict) { return checkDeleteIndex(key, strict)? false : super.delete(key, strict); } @Override public boolean delete(final double key, final boolean strict) { final int index = ArrayIndex.getArrayIndex(key); return checkDeleteIndex(index, strict)? false : super.delete(key, strict); } @Override public boolean delete(final Object key, final boolean strict) { final Object primitiveKey = JSType.toPrimitive(key, String.class); final int index = ArrayIndex.getArrayIndex(primitiveKey); return checkDeleteIndex(index, strict)? false : super.delete(primitiveKey, strict); } private boolean checkDeleteIndex(final int index, final boolean strict) { if (isValidStringIndex(index)) { if (strict) { throw typeError("cant.delete.property", Integer.toString(index), ScriptRuntime.safeToString(this)); } return true; } return false; } @Override public Object getOwnPropertyDescriptor(final Object key) { final int index = ArrayIndex.getArrayIndex(key); if (index >= 0 && index < value.length()) { final Global global = Global.instance(); return global.newDataDescriptor(String.valueOf(value.charAt(index)), false, true, false); } return super.getOwnPropertyDescriptor(key); }
return a List of own keys associated with the object.
Params:
  • all – True if to include non-enumerable keys.
  • nonEnumerable – set of non-enumerable properties seen already.Used to filter out shadowed, but enumerable properties from proto children.
Returns:Array of keys.
/** * return a List of own keys associated with the object. * @param all True if to include non-enumerable keys. * @param nonEnumerable set of non-enumerable properties seen already.Used * to filter out shadowed, but enumerable properties from proto children. * @return Array of keys. */
@Override @SuppressWarnings("unchecked") protected <T> T[] getOwnKeys(final Class<T> type, final boolean all, final Set<T> nonEnumerable) { if (type != String.class) { return super.getOwnKeys(type, all, nonEnumerable); } final List<Object> keys = new ArrayList<>(); // add string index keys for (int i = 0; i < value.length(); i++) { keys.add(JSType.toString(i)); } // add super class properties keys.addAll(Arrays.asList(super.getOwnKeys(type, all, nonEnumerable))); return keys.toArray((T[]) Array.newInstance(type, keys.size())); }
ECMA 15.5.3 String.length
Params:
  • self – self reference
Returns: value of length property for string
/** * ECMA 15.5.3 String.length * @param self self reference * @return value of length property for string */
@Getter(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_WRITABLE | Attribute.NOT_CONFIGURABLE) public static Object length(final Object self) { return getCharSequence(self).length(); }
ECMA 15.5.3.2 String.fromCharCode ( [ char0 [ , char1 [ , ... ] ] ] )
Params:
  • self – self reference
  • args – array of arguments to be interpreted as char
Returns:string with arguments translated to charcodes
/** * ECMA 15.5.3.2 String.fromCharCode ( [ char0 [ , char1 [ , ... ] ] ] ) * @param self self reference * @param args array of arguments to be interpreted as char * @return string with arguments translated to charcodes */
@Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1, where = Where.CONSTRUCTOR) public static String fromCharCode(final Object self, final Object... args) { final char[] buf = new char[args.length]; int index = 0; for (final Object arg : args) { buf[index++] = (char)JSType.toUint16(arg); } return new String(buf); }
ECMA 15.5.3.2 - specialization for one char
Params:
  • self – self reference
  • value – one argument to be interpreted as char
Returns:string with one charcode
/** * ECMA 15.5.3.2 - specialization for one char * @param self self reference * @param value one argument to be interpreted as char * @return string with one charcode */
@SpecializedFunction public static Object fromCharCode(final Object self, final Object value) { if (value instanceof Integer) { return fromCharCode(self, (int)value); } return Character.toString((char)JSType.toUint16(value)); }
ECMA 15.5.3.2 - specialization for one char of int type
Params:
  • self – self reference
  • value – one argument to be interpreted as char
Returns:string with one charcode
/** * ECMA 15.5.3.2 - specialization for one char of int type * @param self self reference * @param value one argument to be interpreted as char * @return string with one charcode */
@SpecializedFunction public static String fromCharCode(final Object self, final int value) { return Character.toString((char)(value & 0xffff)); }
ECMA 15.5.3.2 - specialization for two chars of int type
Params:
  • self – self reference
  • ch1 – first char
  • ch2 – second char
Returns:string with one charcode
/** * ECMA 15.5.3.2 - specialization for two chars of int type * @param self self reference * @param ch1 first char * @param ch2 second char * @return string with one charcode */
@SpecializedFunction public static Object fromCharCode(final Object self, final int ch1, final int ch2) { return Character.toString((char)(ch1 & 0xffff)) + Character.toString((char)(ch2 & 0xffff)); }
ECMA 15.5.3.2 - specialization for three chars of int type
Params:
  • self – self reference
  • ch1 – first char
  • ch2 – second char
  • ch3 – third char
Returns:string with one charcode
/** * ECMA 15.5.3.2 - specialization for three chars of int type * @param self self reference * @param ch1 first char * @param ch2 second char * @param ch3 third char * @return string with one charcode */
@SpecializedFunction public static Object fromCharCode(final Object self, final int ch1, final int ch2, final int ch3) { return Character.toString((char)(ch1 & 0xffff)) + Character.toString((char)(ch2 & 0xffff)) + Character.toString((char)(ch3 & 0xffff)); }
ECMA 15.5.3.2 - specialization for four chars of int type
Params:
  • self – self reference
  • ch1 – first char
  • ch2 – second char
  • ch3 – third char
  • ch4 – fourth char
Returns:string with one charcode
/** * ECMA 15.5.3.2 - specialization for four chars of int type * @param self self reference * @param ch1 first char * @param ch2 second char * @param ch3 third char * @param ch4 fourth char * @return string with one charcode */
@SpecializedFunction public static String fromCharCode(final Object self, final int ch1, final int ch2, final int ch3, final int ch4) { return Character.toString((char)(ch1 & 0xffff)) + Character.toString((char)(ch2 & 0xffff)) + Character.toString((char)(ch3 & 0xffff)) + Character.toString((char)(ch4 & 0xffff)); }
ECMA 15.5.3.2 - specialization for one char of double type
Params:
  • self – self reference
  • value – one argument to be interpreted as char
Returns:string with one charcode
/** * ECMA 15.5.3.2 - specialization for one char of double type * @param self self reference * @param value one argument to be interpreted as char * @return string with one charcode */
@SpecializedFunction public static String fromCharCode(final Object self, final double value) { return Character.toString((char)JSType.toUint16(value)); }
ECMA 15.5.4.2 String.prototype.toString ( )
Params:
  • self – self reference
Returns:self as string
/** * ECMA 15.5.4.2 String.prototype.toString ( ) * @param self self reference * @return self as string */
@Function(attributes = Attribute.NOT_ENUMERABLE) public static String toString(final Object self) { return getString(self); }
ECMA 15.5.4.3 String.prototype.valueOf ( )
Params:
  • self – self reference
Returns:self as string
/** * ECMA 15.5.4.3 String.prototype.valueOf ( ) * @param self self reference * @return self as string */
@Function(attributes = Attribute.NOT_ENUMERABLE) public static String valueOf(final Object self) { return getString(self); }
ECMA 15.5.4.4 String.prototype.charAt (pos)
Params:
  • self – self reference
  • pos – position in string
Returns:string representing the char at the given position
/** * ECMA 15.5.4.4 String.prototype.charAt (pos) * @param self self reference * @param pos position in string * @return string representing the char at the given position */
@Function(attributes = Attribute.NOT_ENUMERABLE) public static String charAt(final Object self, final Object pos) { return charAtImpl(checkObjectToString(self), JSType.toInteger(pos)); }
ECMA 15.5.4.4 String.prototype.charAt (pos) - specialized version for double position
Params:
  • self – self reference
  • pos – position in string
Returns:string representing the char at the given position
/** * ECMA 15.5.4.4 String.prototype.charAt (pos) - specialized version for double position * @param self self reference * @param pos position in string * @return string representing the char at the given position */
@SpecializedFunction public static String charAt(final Object self, final double pos) { return charAt(self, (int)pos); }
ECMA 15.5.4.4 String.prototype.charAt (pos) - specialized version for int position
Params:
  • self – self reference
  • pos – position in string
Returns:string representing the char at the given position
/** * ECMA 15.5.4.4 String.prototype.charAt (pos) - specialized version for int position * @param self self reference * @param pos position in string * @return string representing the char at the given position */
@SpecializedFunction public static String charAt(final Object self, final int pos) { return charAtImpl(checkObjectToString(self), pos); } private static String charAtImpl(final String str, final int pos) { return pos < 0 || pos >= str.length() ? "" : String.valueOf(str.charAt(pos)); } private static int getValidChar(final Object self, final int pos) { try { return ((CharSequence)self).charAt(pos); } catch (final IndexOutOfBoundsException e) { throw new ClassCastException(); //invalid char, out of bounds, force relink } }
ECMA 15.5.4.5 String.prototype.charCodeAt (pos)
Params:
  • self – self reference
  • pos – position in string
Returns:number representing charcode at position
/** * ECMA 15.5.4.5 String.prototype.charCodeAt (pos) * @param self self reference * @param pos position in string * @return number representing charcode at position */
@Function(attributes = Attribute.NOT_ENUMERABLE) public static double charCodeAt(final Object self, final Object pos) { final String str = checkObjectToString(self); final int idx = JSType.toInteger(pos); return idx < 0 || idx >= str.length() ? Double.NaN : str.charAt(idx); }
ECMA 15.5.4.5 String.prototype.charCodeAt (pos) - specialized version for double position
Params:
  • self – self reference
  • pos – position in string
Returns:number representing charcode at position
/** * ECMA 15.5.4.5 String.prototype.charCodeAt (pos) - specialized version for double position * @param self self reference * @param pos position in string * @return number representing charcode at position */
@SpecializedFunction(linkLogic=CharCodeAtLinkLogic.class) public static int charCodeAt(final Object self, final double pos) { return charCodeAt(self, (int)pos); //toInt pos is ok }
ECMA 15.5.4.5 String.prototype.charCodeAt (pos) - specialized version for int position
Params:
  • self – self reference
  • pos – position in string
Returns:number representing charcode at position
/** * ECMA 15.5.4.5 String.prototype.charCodeAt (pos) - specialized version for int position * @param self self reference * @param pos position in string * @return number representing charcode at position */
@SpecializedFunction(linkLogic=CharCodeAtLinkLogic.class) public static int charCodeAt(final Object self, final int pos) { return getValidChar(self, pos); }
ECMA 15.5.4.6 String.prototype.concat ( [ string1 [ , string2 [ , ... ] ] ] )
Params:
  • self – self reference
  • args – list of string to concatenate
Returns:concatenated string
/** * ECMA 15.5.4.6 String.prototype.concat ( [ string1 [ , string2 [ , ... ] ] ] ) * @param self self reference * @param args list of string to concatenate * @return concatenated string */
@Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) public static Object concat(final Object self, final Object... args) { CharSequence cs = checkObjectToString(self); if (args != null) { for (final Object obj : args) { cs = new ConsString(cs, JSType.toCharSequence(obj)); } } return cs; }
ECMA 15.5.4.7 String.prototype.indexOf (searchString, position)
Params:
  • self – self reference
  • search – string to search for
  • pos – position to start search
Returns:position of first match or -1
/** * ECMA 15.5.4.7 String.prototype.indexOf (searchString, position) * @param self self reference * @param search string to search for * @param pos position to start search * @return position of first match or -1 */
@Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) public static int indexOf(final Object self, final Object search, final Object pos) { final String str = checkObjectToString(self); return str.indexOf(JSType.toString(search), JSType.toInteger(pos)); }
ECMA 15.5.4.7 String.prototype.indexOf (searchString, position) specialized for no position parameter
Params:
  • self – self reference
  • search – string to search for
Returns:position of first match or -1
/** * ECMA 15.5.4.7 String.prototype.indexOf (searchString, position) specialized for no position parameter * @param self self reference * @param search string to search for * @return position of first match or -1 */
@SpecializedFunction public static int indexOf(final Object self, final Object search) { return indexOf(self, search, 0); }
ECMA 15.5.4.7 String.prototype.indexOf (searchString, position) specialized for double position parameter
Params:
  • self – self reference
  • search – string to search for
  • pos – position to start search
Returns:position of first match or -1
/** * ECMA 15.5.4.7 String.prototype.indexOf (searchString, position) specialized for double position parameter * @param self self reference * @param search string to search for * @param pos position to start search * @return position of first match or -1 */
@SpecializedFunction public static int indexOf(final Object self, final Object search, final double pos) { return indexOf(self, search, (int) pos); }
ECMA 15.5.4.7 String.prototype.indexOf (searchString, position) specialized for int position parameter
Params:
  • self – self reference
  • search – string to search for
  • pos – position to start search
Returns:position of first match or -1
/** * ECMA 15.5.4.7 String.prototype.indexOf (searchString, position) specialized for int position parameter * @param self self reference * @param search string to search for * @param pos position to start search * @return position of first match or -1 */
@SpecializedFunction public static int indexOf(final Object self, final Object search, final int pos) { return checkObjectToString(self).indexOf(JSType.toString(search), pos); }
ECMA 15.5.4.8 String.prototype.lastIndexOf (searchString, position)
Params:
  • self – self reference
  • search – string to search for
  • pos – position to start search
Returns:last position of match or -1
/** * ECMA 15.5.4.8 String.prototype.lastIndexOf (searchString, position) * @param self self reference * @param search string to search for * @param pos position to start search * @return last position of match or -1 */
@Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) public static int lastIndexOf(final Object self, final Object search, final Object pos) { final String str = checkObjectToString(self); final String searchStr = JSType.toString(search); final int length = str.length(); int end; if (pos == UNDEFINED) { end = length; } else { final double numPos = JSType.toNumber(pos); end = Double.isNaN(numPos) ? length : (int)numPos; if (end < 0) { end = 0; } else if (end > length) { end = length; } } return str.lastIndexOf(searchStr, end); }
ECMA 15.5.4.9 String.prototype.localeCompare (that)
Params:
  • self – self reference
  • that – comparison object
Returns:result of locale sensitive comparison operation between self and that
/** * ECMA 15.5.4.9 String.prototype.localeCompare (that) * @param self self reference * @param that comparison object * @return result of locale sensitive comparison operation between {@code self} and {@code that} */
@Function(attributes = Attribute.NOT_ENUMERABLE) public static double localeCompare(final Object self, final Object that) { final String str = checkObjectToString(self); final Collator collator = Collator.getInstance(Global.getEnv()._locale); collator.setStrength(Collator.IDENTICAL); collator.setDecomposition(Collator.CANONICAL_DECOMPOSITION); return collator.compare(str, JSType.toString(that)); }
ECMA 15.5.4.10 String.prototype.match (regexp)
Params:
  • self – self reference
  • regexp – regexp expression
Returns:array of regexp matches
/** * ECMA 15.5.4.10 String.prototype.match (regexp) * @param self self reference * @param regexp regexp expression * @return array of regexp matches */
@Function(attributes = Attribute.NOT_ENUMERABLE) public static ScriptObject match(final Object self, final Object regexp) { final String str = checkObjectToString(self); NativeRegExp nativeRegExp; if (regexp == UNDEFINED) { nativeRegExp = new NativeRegExp(""); } else { nativeRegExp = Global.toRegExp(regexp); } if (!nativeRegExp.getGlobal()) { return nativeRegExp.exec(str); } nativeRegExp.setLastIndex(0); final List<Object> matches = new ArrayList<>(); Object result; // We follow ECMAScript 6 spec here (checking for empty string instead of previous index) // as the ES5 specification is buggy and causes empty strings to be matched twice. while ((result = nativeRegExp.exec(str)) != null) { final String matchStr = JSType.toString(((ScriptObject)result).get(0)); if (matchStr.isEmpty()) { nativeRegExp.setLastIndex(nativeRegExp.getLastIndex() + 1); } matches.add(matchStr); } if (matches.isEmpty()) { return null; } return new NativeArray(matches.toArray()); }
ECMA 15.5.4.11 String.prototype.replace (searchValue, replaceValue)
Params:
  • self – self reference
  • string – item to replace
  • replacement – item to replace it with
Throws:
Returns:string after replacement
/** * ECMA 15.5.4.11 String.prototype.replace (searchValue, replaceValue) * @param self self reference * @param string item to replace * @param replacement item to replace it with * @return string after replacement * @throws Throwable if replacement fails */
@Function(attributes = Attribute.NOT_ENUMERABLE) public static String replace(final Object self, final Object string, final Object replacement) throws Throwable { final String str = checkObjectToString(self); final NativeRegExp nativeRegExp; if (string instanceof NativeRegExp) { nativeRegExp = (NativeRegExp) string; } else { nativeRegExp = NativeRegExp.flatRegExp(JSType.toString(string)); } if (Bootstrap.isCallable(replacement)) { return nativeRegExp.replace(str, "", replacement); } return nativeRegExp.replace(str, JSType.toString(replacement), null); }
ECMA 15.5.4.12 String.prototype.search (regexp)
Params:
  • self – self reference
  • string – string to search for
Returns:offset where match occurred
/** * ECMA 15.5.4.12 String.prototype.search (regexp) * * @param self self reference * @param string string to search for * @return offset where match occurred */
@Function(attributes = Attribute.NOT_ENUMERABLE) public static int search(final Object self, final Object string) { final String str = checkObjectToString(self); final NativeRegExp nativeRegExp = Global.toRegExp(string == UNDEFINED ? "" : string); return nativeRegExp.search(str); }
ECMA 15.5.4.13 String.prototype.slice (start, end)
Params:
  • self – self reference
  • start – start position for slice
  • end – end position for slice
Returns:sliced out substring
/** * ECMA 15.5.4.13 String.prototype.slice (start, end) * * @param self self reference * @param start start position for slice * @param end end position for slice * @return sliced out substring */
@Function(attributes = Attribute.NOT_ENUMERABLE) public static String slice(final Object self, final Object start, final Object end) { final String str = checkObjectToString(self); if (end == UNDEFINED) { return slice(str, JSType.toInteger(start)); } return slice(str, JSType.toInteger(start), JSType.toInteger(end)); }
ECMA 15.5.4.13 String.prototype.slice (start, end) specialized for single int parameter
Params:
  • self – self reference
  • start – start position for slice
Returns:sliced out substring
/** * ECMA 15.5.4.13 String.prototype.slice (start, end) specialized for single int parameter * * @param self self reference * @param start start position for slice * @return sliced out substring */
@SpecializedFunction public static String slice(final Object self, final int start) { final String str = checkObjectToString(self); final int from = start < 0 ? Math.max(str.length() + start, 0) : Math.min(start, str.length()); return str.substring(from); }
ECMA 15.5.4.13 String.prototype.slice (start, end) specialized for single double parameter
Params:
  • self – self reference
  • start – start position for slice
Returns:sliced out substring
/** * ECMA 15.5.4.13 String.prototype.slice (start, end) specialized for single double parameter * * @param self self reference * @param start start position for slice * @return sliced out substring */
@SpecializedFunction public static String slice(final Object self, final double start) { return slice(self, (int)start); }
ECMA 15.5.4.13 String.prototype.slice (start, end) specialized for two int parameters
Params:
  • self – self reference
  • start – start position for slice
  • end – end position for slice
Returns:sliced out substring
/** * ECMA 15.5.4.13 String.prototype.slice (start, end) specialized for two int parameters * * @param self self reference * @param start start position for slice * @param end end position for slice * @return sliced out substring */
@SpecializedFunction public static String slice(final Object self, final int start, final int end) { final String str = checkObjectToString(self); final int len = str.length(); final int from = start < 0 ? Math.max(len + start, 0) : Math.min(start, len); final int to = end < 0 ? Math.max(len + end, 0) : Math.min(end, len); return str.substring(Math.min(from, to), to); }
ECMA 15.5.4.13 String.prototype.slice (start, end) specialized for two double parameters
Params:
  • self – self reference
  • start – start position for slice
  • end – end position for slice
Returns:sliced out substring
/** * ECMA 15.5.4.13 String.prototype.slice (start, end) specialized for two double parameters * * @param self self reference * @param start start position for slice * @param end end position for slice * @return sliced out substring */
@SpecializedFunction public static String slice(final Object self, final double start, final double end) { return slice(self, (int)start, (int)end); }
ECMA 15.5.4.14 String.prototype.split (separator, limit)
Params:
  • self – self reference
  • separator – separator for split
  • limit – limit for splits
Returns:array object in which splits have been placed
/** * ECMA 15.5.4.14 String.prototype.split (separator, limit) * * @param self self reference * @param separator separator for split * @param limit limit for splits * @return array object in which splits have been placed */
@Function(attributes = Attribute.NOT_ENUMERABLE) public static ScriptObject split(final Object self, final Object separator, final Object limit) { final String str = checkObjectToString(self); final long lim = limit == UNDEFINED ? JSType.MAX_UINT : JSType.toUint32(limit); if (separator == UNDEFINED) { return lim == 0 ? new NativeArray() : new NativeArray(new Object[]{str}); } if (separator instanceof NativeRegExp) { return ((NativeRegExp) separator).split(str, lim); } // when separator is a string, it is treated as a literal search string to be used for splitting. return splitString(str, JSType.toString(separator), lim); } private static ScriptObject splitString(final String str, final String separator, final long limit) { if (separator.isEmpty()) { final int length = (int) Math.min(str.length(), limit); final Object[] array = new Object[length]; for (int i = 0; i < length; i++) { array[i] = String.valueOf(str.charAt(i)); } return new NativeArray(array); } final List<String> elements = new LinkedList<>(); final int strLength = str.length(); final int sepLength = separator.length(); int pos = 0; int n = 0; while (pos < strLength && n < limit) { final int found = str.indexOf(separator, pos); if (found == -1) { break; } elements.add(str.substring(pos, found)); n++; pos = found + sepLength; } if (pos <= strLength && n < limit) { elements.add(str.substring(pos)); } return new NativeArray(elements.toArray()); }
ECMA B.2.3 String.prototype.substr (start, length)
Params:
  • self – self reference
  • start – start position
  • length – length of section
Returns:substring given start and length of section
/** * ECMA B.2.3 String.prototype.substr (start, length) * * @param self self reference * @param start start position * @param length length of section * @return substring given start and length of section */
@Function(attributes = Attribute.NOT_ENUMERABLE) public static String substr(final Object self, final Object start, final Object length) { final String str = JSType.toString(self); final int strLength = str.length(); int intStart = JSType.toInteger(start); if (intStart < 0) { intStart = Math.max(intStart + strLength, 0); } final int intLen = Math.min(Math.max(length == UNDEFINED ? Integer.MAX_VALUE : JSType.toInteger(length), 0), strLength - intStart); return intLen <= 0 ? "" : str.substring(intStart, intStart + intLen); }
ECMA 15.5.4.15 String.prototype.substring (start, end)
Params:
  • self – self reference
  • start – start position of substring
  • end – end position of substring
Returns:substring given start and end indexes
/** * ECMA 15.5.4.15 String.prototype.substring (start, end) * * @param self self reference * @param start start position of substring * @param end end position of substring * @return substring given start and end indexes */
@Function(attributes = Attribute.NOT_ENUMERABLE) public static String substring(final Object self, final Object start, final Object end) { final String str = checkObjectToString(self); if (end == UNDEFINED) { return substring(str, JSType.toInteger(start)); } return substring(str, JSType.toInteger(start), JSType.toInteger(end)); }
ECMA 15.5.4.15 String.prototype.substring (start, end) specialized for int start parameter
Params:
  • self – self reference
  • start – start position of substring
Returns:substring given start and end indexes
/** * ECMA 15.5.4.15 String.prototype.substring (start, end) specialized for int start parameter * * @param self self reference * @param start start position of substring * @return substring given start and end indexes */
@SpecializedFunction public static String substring(final Object self, final int start) { final String str = checkObjectToString(self); if (start < 0) { return str; } else if (start >= str.length()) { return ""; } else { return str.substring(start); } }
ECMA 15.5.4.15 String.prototype.substring (start, end) specialized for double start parameter
Params:
  • self – self reference
  • start – start position of substring
Returns:substring given start and end indexes
/** * ECMA 15.5.4.15 String.prototype.substring (start, end) specialized for double start parameter * * @param self self reference * @param start start position of substring * @return substring given start and end indexes */
@SpecializedFunction public static String substring(final Object self, final double start) { return substring(self, (int)start); }
ECMA 15.5.4.15 String.prototype.substring (start, end) specialized for int start and end parameters
Params:
  • self – self reference
  • start – start position of substring
  • end – end position of substring
Returns:substring given start and end indexes
/** * ECMA 15.5.4.15 String.prototype.substring (start, end) specialized for int start and end parameters * * @param self self reference * @param start start position of substring * @param end end position of substring * @return substring given start and end indexes */
@SpecializedFunction public static String substring(final Object self, final int start, final int end) { final String str = checkObjectToString(self); final int len = str.length(); final int validStart = start < 0 ? 0 : start > len ? len : start; final int validEnd = end < 0 ? 0 : end > len ? len : end; if (validStart < validEnd) { return str.substring(validStart, validEnd); } return str.substring(validEnd, validStart); }
ECMA 15.5.4.15 String.prototype.substring (start, end) specialized for double start and end parameters
Params:
  • self – self reference
  • start – start position of substring
  • end – end position of substring
Returns:substring given start and end indexes
/** * ECMA 15.5.4.15 String.prototype.substring (start, end) specialized for double start and end parameters * * @param self self reference * @param start start position of substring * @param end end position of substring * @return substring given start and end indexes */
@SpecializedFunction public static String substring(final Object self, final double start, final double end) { return substring(self, (int)start, (int)end); }
ECMA 15.5.4.16 String.prototype.toLowerCase ( )
Params:
  • self – self reference
Returns:string to lower case
/** * ECMA 15.5.4.16 String.prototype.toLowerCase ( ) * @param self self reference * @return string to lower case */
@Function(attributes = Attribute.NOT_ENUMERABLE) public static String toLowerCase(final Object self) { return checkObjectToString(self).toLowerCase(Locale.ROOT); }
ECMA 15.5.4.17 String.prototype.toLocaleLowerCase ( )
Params:
  • self – self reference
Returns:string to locale sensitive lower case
/** * ECMA 15.5.4.17 String.prototype.toLocaleLowerCase ( ) * @param self self reference * @return string to locale sensitive lower case */
@Function(attributes = Attribute.NOT_ENUMERABLE) public static String toLocaleLowerCase(final Object self) { return checkObjectToString(self).toLowerCase(Global.getEnv()._locale); }
ECMA 15.5.4.18 String.prototype.toUpperCase ( )
Params:
  • self – self reference
Returns:string to upper case
/** * ECMA 15.5.4.18 String.prototype.toUpperCase ( ) * @param self self reference * @return string to upper case */
@Function(attributes = Attribute.NOT_ENUMERABLE) public static String toUpperCase(final Object self) { return checkObjectToString(self).toUpperCase(Locale.ROOT); }
ECMA 15.5.4.19 String.prototype.toLocaleUpperCase ( )
Params:
  • self – self reference
Returns:string to locale sensitive upper case
/** * ECMA 15.5.4.19 String.prototype.toLocaleUpperCase ( ) * @param self self reference * @return string to locale sensitive upper case */
@Function(attributes = Attribute.NOT_ENUMERABLE) public static String toLocaleUpperCase(final Object self) { return checkObjectToString(self).toUpperCase(Global.getEnv()._locale); }
ECMA 15.5.4.20 String.prototype.trim ( )
Params:
  • self – self reference
Returns:string trimmed from whitespace
/** * ECMA 15.5.4.20 String.prototype.trim ( ) * @param self self reference * @return string trimmed from whitespace */
@Function(attributes = Attribute.NOT_ENUMERABLE) public static String trim(final Object self) { final String str = checkObjectToString(self); int start = 0; int end = str.length() - 1; while (start <= end && ScriptRuntime.isJSWhitespace(str.charAt(start))) { start++; } while (end > start && ScriptRuntime.isJSWhitespace(str.charAt(end))) { end--; } return str.substring(start, end + 1); }
Nashorn extension: String.prototype.trimLeft ( )
Params:
  • self – self reference
Returns:string trimmed left from whitespace
/** * Nashorn extension: String.prototype.trimLeft ( ) * @param self self reference * @return string trimmed left from whitespace */
@Function(attributes = Attribute.NOT_ENUMERABLE) public static String trimLeft(final Object self) { final String str = checkObjectToString(self); int start = 0; final int end = str.length() - 1; while (start <= end && ScriptRuntime.isJSWhitespace(str.charAt(start))) { start++; } return str.substring(start, end + 1); }
Nashorn extension: String.prototype.trimRight ( )
Params:
  • self – self reference
Returns:string trimmed right from whitespace
/** * Nashorn extension: String.prototype.trimRight ( ) * @param self self reference * @return string trimmed right from whitespace */
@Function(attributes = Attribute.NOT_ENUMERABLE) public static String trimRight(final Object self) { final String str = checkObjectToString(self); final int start = 0; int end = str.length() - 1; while (end >= start && ScriptRuntime.isJSWhitespace(str.charAt(end))) { end--; } return str.substring(start, end + 1); } private static ScriptObject newObj(final CharSequence str) { return new NativeString(str); }
ECMA 15.5.2.1 new String ( [ value ] ) Constructor
Params:
  • newObj – is this constructor invoked with the new operator
  • self – self reference
  • args – arguments (a value)
Returns:new NativeString, empty string if no args, extraneous args ignored
/** * ECMA 15.5.2.1 new String ( [ value ] ) * * Constructor * * @param newObj is this constructor invoked with the new operator * @param self self reference * @param args arguments (a value) * * @return new NativeString, empty string if no args, extraneous args ignored */
@Constructor(arity = 1) public static Object constructor(final boolean newObj, final Object self, final Object... args) { final CharSequence str = args.length > 0 ? JSType.toCharSequence(args[0]) : ""; return newObj ? newObj(str) : str.toString(); }
ECMA 15.5.2.1 new String ( [ value ] ) - special version with no args Constructor
Params:
  • newObj – is this constructor invoked with the new operator
  • self – self reference
Returns:new NativeString ("")
/** * ECMA 15.5.2.1 new String ( [ value ] ) - special version with no args * * Constructor * * @param newObj is this constructor invoked with the new operator * @param self self reference * * @return new NativeString ("") */
@SpecializedFunction(isConstructor=true) public static Object constructor(final boolean newObj, final Object self) { return newObj ? newObj("") : ""; }
ECMA 15.5.2.1 new String ( [ value ] ) - special version with one arg Constructor
Params:
  • newObj – is this constructor invoked with the new operator
  • self – self reference
  • arg – argument
Returns:new NativeString (arg)
/** * ECMA 15.5.2.1 new String ( [ value ] ) - special version with one arg * * Constructor * * @param newObj is this constructor invoked with the new operator * @param self self reference * @param arg argument * * @return new NativeString (arg) */
@SpecializedFunction(isConstructor=true) public static Object constructor(final boolean newObj, final Object self, final Object arg) { final CharSequence str = JSType.toCharSequence(arg); return newObj ? newObj(str) : str.toString(); }
ECMA 15.5.2.1 new String ( [ value ] ) - special version with exactly one int arg Constructor
Params:
  • newObj – is this constructor invoked with the new operator
  • self – self reference
  • arg – the arg
Returns:new NativeString containing the string representation of the arg
/** * ECMA 15.5.2.1 new String ( [ value ] ) - special version with exactly one {@code int} arg * * Constructor * * @param newObj is this constructor invoked with the new operator * @param self self reference * @param arg the arg * * @return new NativeString containing the string representation of the arg */
@SpecializedFunction(isConstructor=true) public static Object constructor(final boolean newObj, final Object self, final int arg) { final String str = Integer.toString(arg); return newObj ? newObj(str) : str; }
ECMA 15.5.2.1 new String ( [ value ] ) - special version with exactly one double arg Constructor
Params:
  • newObj – is this constructor invoked with the new operator
  • self – self reference
  • arg – the arg
Returns:new NativeString containing the string representation of the arg
/** * ECMA 15.5.2.1 new String ( [ value ] ) - special version with exactly one {@code double} arg * * Constructor * * @param newObj is this constructor invoked with the new operator * @param self self reference * @param arg the arg * * @return new NativeString containing the string representation of the arg */
@SpecializedFunction(isConstructor=true) public static Object constructor(final boolean newObj, final Object self, final double arg) { final String str = JSType.toString(arg); return newObj ? newObj(str) : str; }
ECMA 15.5.2.1 new String ( [ value ] ) - special version with exactly one boolean arg Constructor
Params:
  • newObj – is this constructor invoked with the new operator
  • self – self reference
  • arg – the arg
Returns:new NativeString containing the string representation of the arg
/** * ECMA 15.5.2.1 new String ( [ value ] ) - special version with exactly one {@code boolean} arg * * Constructor * * @param newObj is this constructor invoked with the new operator * @param self self reference * @param arg the arg * * @return new NativeString containing the string representation of the arg */
@SpecializedFunction(isConstructor=true) public static Object constructor(final boolean newObj, final Object self, final boolean arg) { final String str = Boolean.toString(arg); return newObj ? newObj(str) : str; }
ECMA 6 21.1.3.27 String.prototype [ @@iterator ]( )
Params:
  • self – self reference
Returns:a string iterator
/** * ECMA 6 21.1.3.27 String.prototype [ @@iterator ]( ) * * @param self self reference * @return a string iterator */
@Function(attributes = Attribute.NOT_ENUMERABLE, name = "@@iterator") public static Object getIterator(final Object self) { return new StringIterator(checkObjectToString(self), Global.instance()); }
Lookup the appropriate method for an invoke dynamic call.
Params:
  • request – the link request
  • receiver – receiver of call
Returns:Link to be invoked at call site.
/** * Lookup the appropriate method for an invoke dynamic call. * * @param request the link request * @param receiver receiver of call * @return Link to be invoked at call site. */
public static GuardedInvocation lookupPrimitive(final LinkRequest request, final Object receiver) { return PrimitiveLookup.lookupPrimitive(request, NashornGuards.getStringGuard(), new NativeString((CharSequence)receiver), WRAPFILTER, PROTOFILTER); } @SuppressWarnings("unused") private static NativeString wrapFilter(final Object receiver) { return new NativeString((CharSequence)receiver); } @SuppressWarnings("unused") private static Object protoFilter(final Object object) { return Global.instance().getStringPrototype(); } private static CharSequence getCharSequence(final Object self) { if (JSType.isString(self)) { return (CharSequence)self; } else if (self instanceof NativeString) { return ((NativeString)self).getValue(); } else if (self != null && self == Global.instance().getStringPrototype()) { return ""; } else { throw typeError("not.a.string", ScriptRuntime.safeToString(self)); } } private static String getString(final Object self) { if (self instanceof String) { return (String)self; } else if (self instanceof ConsString) { return self.toString(); } else if (self instanceof NativeString) { return ((NativeString)self).getStringValue(); } else if (self != null && self == Global.instance().getStringPrototype()) { return ""; } else { throw typeError("not.a.string", ScriptRuntime.safeToString(self)); } }
Combines ECMA 9.10 CheckObjectCoercible and ECMA 9.8 ToString with a shortcut for strings.
Params:
  • self – the object
Returns:the object as string
/** * Combines ECMA 9.10 CheckObjectCoercible and ECMA 9.8 ToString with a shortcut for strings. * * @param self the object * @return the object as string */
private static String checkObjectToString(final Object self) { if (self instanceof String) { return (String)self; } else if (self instanceof ConsString) { return self.toString(); } else { Global.checkObjectCoercible(self); return JSType.toString(self); } } private boolean isValidStringIndex(final int key) { return key >= 0 && key < value.length(); } private static MethodHandle findOwnMH(final String name, final MethodType type) { return MH.findStatic(MethodHandles.lookup(), NativeString.class, name, type); } @Override public LinkLogic getLinkLogic(final Class<? extends LinkLogic> clazz) { if (clazz == CharCodeAtLinkLogic.class) { return CharCodeAtLinkLogic.INSTANCE; } return null; } @Override public boolean hasPerInstanceAssumptions() { return false; }
This is linker logic charCodeAt - when we specialize further methods in NativeString It may be expanded. It's link check makes sure that we are dealing with a char sequence and that we are in range
/** * This is linker logic charCodeAt - when we specialize further methods in NativeString * It may be expanded. It's link check makes sure that we are dealing with a char * sequence and that we are in range */
private static final class CharCodeAtLinkLogic extends SpecializedFunction.LinkLogic { private static final CharCodeAtLinkLogic INSTANCE = new CharCodeAtLinkLogic(); @Override public boolean canLink(final Object self, final CallSiteDescriptor desc, final LinkRequest request) { try { //check that it's a char sequence or throw cce final CharSequence cs = (CharSequence)self; //check that the index, representable as an int, is inside the array final int intIndex = JSType.toInteger(request.getArguments()[2]); return intIndex >= 0 && intIndex < cs.length(); //can link } catch (final ClassCastException | IndexOutOfBoundsException e) { //fallthru } return false; }
charCodeAt callsites can throw ClassCastException as a mechanism to have them relinked - this enabled fast checks of the kind of ((IntArrayData)arrayData).push(x) for an IntArrayData only push - if this fails, a CCE will be thrown and we will relink
/** * charCodeAt callsites can throw ClassCastException as a mechanism to have them * relinked - this enabled fast checks of the kind of ((IntArrayData)arrayData).push(x) * for an IntArrayData only push - if this fails, a CCE will be thrown and we will relink */
@Override public Class<? extends Throwable> getRelinkException() { return ClassCastException.class; } } }