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 
17 package com.android.dx.rop.cst;
18 
19 import com.android.dex.util.ExceptionWithContext;
20 import com.android.dx.util.Hex;
21 import 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  */
27 public 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      */
StdConstantPool(int size)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     @Override
size()52     public int size() {
53         return entries.length;
54     }
55 
56     /** {@inheritDoc} */
57     @Override
getOrNull(int n)58     public Constant getOrNull(int n) {
59         try {
60             return entries[n];
61         } catch (IndexOutOfBoundsException ex) {
62             // Translate the exception.
63             return throwInvalid(n);
64         }
65     }
66 
67     /** {@inheritDoc} */
68     @Override
get0Ok(int n)69     public Constant get0Ok(int n) {
70         if (n == 0) {
71             return null;
72         }
73 
74         return get(n);
75     }
76 
77     /** {@inheritDoc} */
78     @Override
get(int n)79     public Constant get(int n) {
80         try {
81             Constant result = entries[n];
82 
83             if (result == null) {
84                 throwInvalid(n);
85             }
86 
87             return result;
88         } catch (IndexOutOfBoundsException ex) {
89             // Translate the exception.
90             return throwInvalid(n);
91         }
92     }
93 
94     /**
95      * Get all entries in this constant pool.
96      *
97      * @return the returned array may contain null entries.
98      */
99     @Override
getEntries()100     public Constant[] getEntries() {
101         return entries;
102     }
103 
104     /**
105      * Sets the entry at the given index.
106      *
107      * @param n {@code >= 1, < size();} which entry
108      * @param cst {@code null-ok;} the constant to store
109      */
set(int n, Constant cst)110     public void set(int n, Constant cst) {
111         throwIfImmutable();
112 
113         boolean cat2 = (cst != null) && cst.isCategory2();
114 
115         if (n < 1) {
116             throw new IllegalArgumentException("n < 1");
117         }
118 
119         if (cat2) {
120             // Storing a category-2 entry nulls out the next index.
121             if (n == (entries.length - 1)) {
122                 throw new IllegalArgumentException("(n == size - 1) && " +
123                                                    "cst.isCategory2()");
124             }
125             entries[n + 1] = null;
126         }
127 
128         if ((cst != null) && (entries[n] == null)) {
129             /*
130              * Overwriting the second half of a category-2 entry nulls out
131              * the first half.
132              */
133             Constant prev = entries[n - 1];
134             if ((prev != null) && prev.isCategory2()) {
135                 entries[n - 1] = null;
136             }
137         }
138 
139         entries[n] = cst;
140     }
141 
142     /**
143      * Throws the right exception for an invalid cpi.
144      *
145      * @param idx the bad cpi
146      * @return never
147      * @throws ExceptionWithContext always thrown
148      */
throwInvalid(int idx)149     private static Constant throwInvalid(int idx) {
150         throw new ExceptionWithContext("invalid constant pool index " +
151                                        Hex.u2(idx));
152     }
153 }
154