| /* |
| * 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)); |
| } |
| |
| } |
| |