blob: f979aaa9a83d1ab3e8df09eb15076f9e0e5c735f [file] [log] [blame]
/*
* 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.runtime.arrays;
import jdk.nashorn.internal.runtime.ConsString;
import jdk.nashorn.internal.runtime.JSType;
import jdk.nashorn.internal.runtime.ScriptObject;
/**
* Array index computation helpers. that both throw exceptions or return
* invalid values.
*
*/
public final class ArrayIndex {
private static final int INVALID_ARRAY_INDEX = -1;
private static final long MAX_ARRAY_INDEX = 0xfffffffeL;
private ArrayIndex() {
}
/**
* Fast conversion of non-negative integer string to long.
* @param key Key as a string.
* @return long value of string or {@code -1} if string does not represent a valid index.
*/
private static long fromString(final String key) {
long value = 0;
final int length = key.length();
// Check for empty string or leading 0
if (length == 0 || (length > 1 && key.charAt(0) == '0')) {
return INVALID_ARRAY_INDEX;
}
// Fast toNumber.
for (int i = 0; i < length; i++) {
final char digit = key.charAt(i);
// If not a digit.
if (digit < '0' || digit > '9') {
return INVALID_ARRAY_INDEX;
}
// Insert digit.
value = value * 10 + digit - '0';
// Check for overflow (need to catch before wrap around.)
if (value > MAX_ARRAY_INDEX) {
return INVALID_ARRAY_INDEX;
}
}
return value;
}
/**
* Returns a valid array index in an int, if the object represents one. This
* routine needs to perform quickly since all keys are tested with it.
*
* <p>The {@code key} parameter must be a JavaScript primitive type, i.e. one of
* {@code String}, {@code Number}, {@code Boolean}, {@code null}, or {@code undefined}.
* {@code ScriptObject} instances should be converted to primitive with
* {@code String.class} hint before being passed to this method.</p>
*
* @param key key to check for array index.
* @return the array index, or {@code -1} if {@code key} does not represent a valid index.
* Note that negative return values other than {@code -1} are considered valid and can be converted to
* the actual index using {@link #toLongIndex(int)}.
*/
public static int getArrayIndex(final Object key) {
if (key instanceof Integer) {
return getArrayIndex(((Integer) key).intValue());
} else if (key instanceof Double) {
return getArrayIndex(((Double) key).doubleValue());
} else if (key instanceof String) {
return (int)fromString((String) key);
} else if (key instanceof Long) {
return getArrayIndex(((Long) key).longValue());
} else if (key instanceof ConsString) {
return (int)fromString(key.toString());
}
assert !(key instanceof ScriptObject);
return INVALID_ARRAY_INDEX;
}
/**
* Returns a valid array index in an int, if {@code key} represents one.
*
* @param key key to check
* @return the array index, or {@code -1} if {@code key} is not a valid array index.
*/
public static int getArrayIndex(final int key) {
return (key >= 0) ? key : INVALID_ARRAY_INDEX;
}
/**
* Returns a valid array index in an int, if the long represents one.
*
* @param key key to check
* @return the array index, or {@code -1} if long is not a valid array index.
* Note that negative return values other than {@code -1} are considered valid and can be converted to
* the actual index using {@link #toLongIndex(int)}.
*/
public static int getArrayIndex(final long key) {
if (key >= 0 && key <= MAX_ARRAY_INDEX) {
return (int)key;
}
return INVALID_ARRAY_INDEX;
}
/**
* Return a valid index for this double, if it represents one.
*
* Doubles that aren't representable exactly as longs/ints aren't working
* array indexes, however, array[1.1] === array["1.1"] in JavaScript.
*
* @param key the key to check
* @return the array index this double represents or {@code -1} if this isn't a valid index.
* Note that negative return values other than {@code -1} are considered valid and can be converted to
* the actual index using {@link #toLongIndex(int)}.
*/
public static int getArrayIndex(final double key) {
if (JSType.isRepresentableAsInt(key)) {
return getArrayIndex((int) key);
} else if (JSType.isRepresentableAsLong(key)) {
return getArrayIndex((long) key);
}
return INVALID_ARRAY_INDEX;
}
/**
* Return a valid array index for this string, if it represents one.
*
* @param key the key to check
* @return the array index this string represents or {@code -1} if this isn't a valid index.
* Note that negative return values other than {@code -1} are considered valid and can be converted to
* the actual index using {@link #toLongIndex(int)}.
*/
public static int getArrayIndex(final String key) {
return (int)fromString(key);
}
/**
* Check whether an index is valid as an array index. This check only tests if
* it is the special "invalid array index" type, not if it is e.g. less than zero
* or corrupt in some other way
*
* @param index index to test
* @return true if {@code index} is not the special invalid array index type
*/
public static boolean isValidArrayIndex(final int index) {
return index != INVALID_ARRAY_INDEX;
}
/**
* Convert an index to a long value. This basically amounts to converting it into a
* {@link JSType#toUint32(int)} uint32} as the maximum array index in JavaScript
* is 0xfffffffe
*
* @param index index to convert to long form
* @return index as uint32 in a long
*/
public static long toLongIndex(final int index) {
return JSType.toUint32(index);
}
/**
* Convert an index to a key string. This is the same as calling {@link #toLongIndex(int)}
* and converting the result to String.
*
* @param index index to convert
* @return index as string
*/
public static String toKey(final int index) {
return Long.toString(JSType.toUint32(index));
}
}