1/* 2 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26package jdk.nashorn.internal.runtime.arrays; 27 28import jdk.nashorn.internal.runtime.ConsString; 29import jdk.nashorn.internal.runtime.JSType; 30import jdk.nashorn.internal.runtime.ScriptObject; 31 32/** 33 * Array index computation helpers. that both throw exceptions or return 34 * invalid values. 35 * 36 */ 37public final class ArrayIndex { 38 39 private static final int INVALID_ARRAY_INDEX = -1; 40 private static final long MAX_ARRAY_INDEX = 0xfffffffeL; 41 42 private ArrayIndex() { 43 } 44 45 /** 46 * Fast conversion of non-negative integer string to long. 47 * @param key Key as a string. 48 * @return long value of string or {@code -1} if string does not represent a valid index. 49 */ 50 private static long fromString(final String key) { 51 long value = 0; 52 final int length = key.length(); 53 54 // Check for empty string or leading 0 55 if (length == 0 || (length > 1 && key.charAt(0) == '0')) { 56 return INVALID_ARRAY_INDEX; 57 } 58 59 // Fast toNumber. 60 for (int i = 0; i < length; i++) { 61 final char digit = key.charAt(i); 62 63 // If not a digit. 64 if (digit < '0' || digit > '9') { 65 return INVALID_ARRAY_INDEX; 66 } 67 68 // Insert digit. 69 value = value * 10 + digit - '0'; 70 71 // Check for overflow (need to catch before wrap around.) 72 if (value > MAX_ARRAY_INDEX) { 73 return INVALID_ARRAY_INDEX; 74 } 75 } 76 77 return value; 78 } 79 80 /** 81 * Returns a valid array index in an int, if the object represents one. This 82 * routine needs to perform quickly since all keys are tested with it. 83 * 84 * <p>The {@code key} parameter must be a JavaScript primitive type, i.e. one of 85 * {@code String}, {@code Number}, {@code Boolean}, {@code null}, or {@code undefined}. 86 * {@code ScriptObject} instances should be converted to primitive with 87 * {@code String.class} hint before being passed to this method.</p> 88 * 89 * @param key key to check for array index. 90 * @return the array index, or {@code -1} if {@code key} does not represent a valid index. 91 * Note that negative return values other than {@code -1} are considered valid and can be converted to 92 * the actual index using {@link #toLongIndex(int)}. 93 */ 94 public static int getArrayIndex(final Object key) { 95 if (key instanceof Integer) { 96 return getArrayIndex(((Integer) key).intValue()); 97 } else if (key instanceof Double) { 98 return getArrayIndex(((Double) key).doubleValue()); 99 } else if (key instanceof String) { 100 return (int)fromString((String) key); 101 } else if (key instanceof Long) { 102 return getArrayIndex(((Long) key).longValue()); 103 } else if (key instanceof ConsString) { 104 return (int)fromString(key.toString()); 105 } 106 107 assert !(key instanceof ScriptObject); 108 return INVALID_ARRAY_INDEX; 109 } 110 111 /** 112 * Returns a valid array index in an int, if {@code key} represents one. 113 * 114 * @param key key to check 115 * @return the array index, or {@code -1} if {@code key} is not a valid array index. 116 */ 117 public static int getArrayIndex(final int key) { 118 return (key >= 0) ? key : INVALID_ARRAY_INDEX; 119 } 120 121 /** 122 * Returns a valid array index in an int, if the long represents one. 123 * 124 * @param key key to check 125 * @return the array index, or {@code -1} if long is not a valid array index. 126 * Note that negative return values other than {@code -1} are considered valid and can be converted to 127 * the actual index using {@link #toLongIndex(int)}. 128 */ 129 public static int getArrayIndex(final long key) { 130 if (key >= 0 && key <= MAX_ARRAY_INDEX) { 131 return (int)key; 132 } 133 134 return INVALID_ARRAY_INDEX; 135 } 136 137 138 /** 139 * Return a valid index for this double, if it represents one. 140 * 141 * Doubles that aren't representable exactly as longs/ints aren't working 142 * array indexes, however, array[1.1] === array["1.1"] in JavaScript. 143 * 144 * @param key the key to check 145 * @return the array index this double represents or {@code -1} if this isn't a valid index. 146 * Note that negative return values other than {@code -1} are considered valid and can be converted to 147 * the actual index using {@link #toLongIndex(int)}. 148 */ 149 public static int getArrayIndex(final double key) { 150 if (JSType.isRepresentableAsInt(key)) { 151 return getArrayIndex((int) key); 152 } else if (JSType.isRepresentableAsLong(key)) { 153 return getArrayIndex((long) key); 154 } 155 156 return INVALID_ARRAY_INDEX; 157 } 158 159 160 /** 161 * Return a valid array index for this string, if it represents one. 162 * 163 * @param key the key to check 164 * @return the array index this string represents or {@code -1} if this isn't a valid index. 165 * Note that negative return values other than {@code -1} are considered valid and can be converted to 166 * the actual index using {@link #toLongIndex(int)}. 167 */ 168 public static int getArrayIndex(final String key) { 169 return (int)fromString(key); 170 } 171 172 /** 173 * Check whether an index is valid as an array index. This check only tests if 174 * it is the special "invalid array index" type, not if it is e.g. less than zero 175 * or corrupt in some other way 176 * 177 * @param index index to test 178 * @return true if {@code index} is not the special invalid array index type 179 */ 180 public static boolean isValidArrayIndex(final int index) { 181 return index != INVALID_ARRAY_INDEX; 182 } 183 184 /** 185 * Convert an index to a long value. This basically amounts to converting it into a 186 * {@link JSType#toUint32(int)} uint32} as the maximum array index in JavaScript 187 * is 0xfffffffe 188 * 189 * @param index index to convert to long form 190 * @return index as uint32 in a long 191 */ 192 public static long toLongIndex(final int index) { 193 return JSType.toUint32(index); 194 } 195 196 /** 197 * Convert an index to a key string. This is the same as calling {@link #toLongIndex(int)} 198 * and converting the result to String. 199 * 200 * @param index index to convert 201 * @return index as string 202 */ 203 public static String toKey(final int index) { 204 return Long.toString(JSType.toUint32(index)); 205 } 206 207} 208 209