1/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.dx.rop.cst;
18
19import com.android.dx.util.ExceptionWithContext;
20import com.android.dx.util.Hex;
21import com.android.dx.util.MutabilityControl;
22
23/**
24 * Standard implementation of {@link ConstantPool}, which directly stores
25 * an array of {@link Constant} objects and can be made immutable.
26 */
27public final class StdConstantPool
28        extends MutabilityControl implements ConstantPool {
29    /** {@code non-null;} array of entries */
30    private final Constant[] entries;
31
32    /**
33     * Constructs an instance. All indices initially contain {@code null}.
34     *
35     * @param size the size of the pool; this corresponds to the
36     * class file field {@code constant_pool_count}, and is in fact
37     * always at least one more than the actual size of the constant pool,
38     * as element {@code 0} is always invalid.
39     */
40    public StdConstantPool(int size) {
41        super(size > 1);
42
43        if (size < 1) {
44            throw new IllegalArgumentException("size < 1");
45        }
46
47        entries = new Constant[size];
48    }
49
50    /** {@inheritDoc} */
51    public int size() {
52        return entries.length;
53    }
54
55    /** {@inheritDoc} */
56    public Constant getOrNull(int n) {
57        try {
58            return entries[n];
59        } catch (IndexOutOfBoundsException ex) {
60            // Translate the exception.
61            return throwInvalid(n);
62        }
63    }
64
65    /** {@inheritDoc} */
66    public Constant get0Ok(int n) {
67        if (n == 0) {
68            return null;
69        }
70
71        return get(n);
72    }
73
74    /** {@inheritDoc} */
75    public Constant get(int n) {
76        try {
77            Constant result = entries[n];
78
79            if (result == null) {
80                throwInvalid(n);
81            }
82
83            return result;
84        } catch (IndexOutOfBoundsException ex) {
85            // Translate the exception.
86            return throwInvalid(n);
87        }
88    }
89
90    /**
91     * Sets the entry at the given index.
92     *
93     * @param n {@code >= 1, < size();} which entry
94     * @param cst {@code null-ok;} the constant to store
95     */
96    public void set(int n, Constant cst) {
97        throwIfImmutable();
98
99        boolean cat2 = (cst != null) && cst.isCategory2();
100
101        if (n < 1) {
102            throw new IllegalArgumentException("n < 1");
103        }
104
105        if (cat2) {
106            // Storing a category-2 entry nulls out the next index.
107            if (n == (entries.length - 1)) {
108                throw new IllegalArgumentException("(n == size - 1) && " +
109                                                   "cst.isCategory2()");
110            }
111            entries[n + 1] = null;
112        }
113
114        if ((cst != null) && (entries[n] == null)) {
115            /*
116             * Overwriting the second half of a category-2 entry nulls out
117             * the first half.
118             */
119            Constant prev = entries[n - 1];
120            if ((prev != null) && prev.isCategory2()) {
121                entries[n - 1] = null;
122            }
123        }
124
125        entries[n] = cst;
126    }
127
128    /**
129     * Throws the right exception for an invalid cpi.
130     *
131     * @param idx the bad cpi
132     * @return never
133     * @throws ExceptionWithContext always thrown
134     */
135    private static Constant throwInvalid(int idx) {
136        throw new ExceptionWithContext("invalid constant pool index " +
137                                       Hex.u2(idx));
138    }
139}
140