diff options
Diffstat (limited to 'src/jdk/nashorn/internal/objects/NativeString.java')
-rw-r--r-- | src/jdk/nashorn/internal/objects/NativeString.java | 1353 |
1 files changed, 0 insertions, 1353 deletions
diff --git a/src/jdk/nashorn/internal/objects/NativeString.java b/src/jdk/nashorn/internal/objects/NativeString.java deleted file mode 100644 index 304ec99c..00000000 --- a/src/jdk/nashorn/internal/objects/NativeString.java +++ /dev/null @@ -1,1353 +0,0 @@ -/* - * 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.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.internal.dynalink.CallSiteDescriptor; -import jdk.internal.dynalink.linker.GuardedInvocation; -import jdk.internal.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.NashornGuards; -import jdk.nashorn.internal.runtime.linker.PrimitiveLookup; - -/** - * 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 */ - static final MethodHandle WRAPFILTER = findOwnMH("wrapFilter", MH.type(NativeString.class, Object.class)); - /** 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(); - } - - @Override - public boolean equals(final Object other) { - if (other instanceof NativeString) { - return getStringValue().equals(((NativeString) other).getStringValue()); - } - - return false; - } - - @Override - public int hashCode() { - return getStringValue().hashCode(); - } - - 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 operator) { - final String name = desc.getNameToken(2); - - // if str.length(), then let the bean linker handle it - if ("length".equals(name) && "getMethod".equals(operator)) { - return null; - } - - return super.findGetMethod(desc, request, operator); - } - - // 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 String 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. - * @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 - protected String[] getOwnKeys(final boolean all, final Set<String> 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(all, nonEnumerable))); - return keys.toArray(new String[keys.size()]); - } - - /** - * 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 [ , ... ] ] ] ) - * @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 - * @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 - * @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 - * @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 - * @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 - * @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 - * @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 ( ) - * @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 ( ) - * @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) - * @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 - * @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 - * @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) - * @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 - * @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 long 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 long pos) { - return charCodeAt(self, (int)pos); - } - - /** - * 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 [ , ... ] ] ] ) - * @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) - * @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 - * @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 - * @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 - * @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) - * @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) - * @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) - * @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); - - int previousLastIndex = 0; - final List<Object> matches = new ArrayList<>(); - - Object result; - while ((result = nativeRegExp.exec(str)) != null) { - final int thisIndex = nativeRegExp.getLastIndex(); - if (thisIndex == previousLastIndex) { - nativeRegExp.setLastIndex(thisIndex + 1); - previousLastIndex = thisIndex + 1; - } else { - previousLastIndex = thisIndex; - } - matches.add(((ScriptObject)result).get(0)); - } - - if (matches.isEmpty()) { - return null; - } - - return new NativeArray(matches.toArray()); - } - - /** - * 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) - * - * @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) - * - * @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 - * - * @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 - * - * @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 - * - * @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 - * - * @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) - * - * @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) - * - * @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) - * - * @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 - * - * @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 - * - * @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 - * - * @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 - * - * @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 ( ) - * @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 ( ) - * @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 ( ) - * @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 ( ) - * @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 ( ) - * @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 ( ) - * @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 ( ) - * @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 - * - * @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 - * - * @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 - * - * @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 {@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 {@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 long arg) { - final String str = Long.toString(arg); - return newObj ? newObj(str) : str; - } - - /** - * 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 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 {@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; - } - - /** - * 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. - * - * @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 - */ - 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 - */ - @Override - public Class<? extends Throwable> getRelinkException() { - return ClassCastException.class; - } - } -} |