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