aboutsummaryrefslogtreecommitdiff
path: root/src/jdk/nashorn/internal/runtime/arrays/ArrayIndex.java
blob: 5c857e119a133cf5e78c1f8c6d4f5ebf76451a34 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
/*
 * 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 ANDing it
     * with {@link JSType#MAX_UINT}, 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 index & JSType.MAX_UINT;
    }

    /**
     * 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(index & JSType.MAX_UINT);
    }

}