1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * https://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19 package org.apache.commons.compress.harmony.unpack200;
20
21 import java.util.List;
22
23 import org.apache.commons.compress.harmony.pack200.Pack200Exception;
24 import org.apache.commons.compress.harmony.unpack200.bytecode.ClassFileEntry;
25 import org.apache.commons.compress.harmony.unpack200.bytecode.ConstantPoolEntry;
26
27 /**
28 * Manages the constant pool used for re-creating class files.
29 */
30 public class SegmentConstantPool {
31
32 /**
33 * Value {@value}.
34 */
35 public static final int ALL = 0;
36
37 /**
38 * Value {@value}.
39 */
40 public static final int UTF_8 = 1;
41
42 /**
43 * Value {@value}.
44 */
45 public static final int CP_INT = 2;
46
47 // define in archive order
48
49 /**
50 * Value {@value}.
51 */
52 public static final int CP_FLOAT = 3;
53
54 /**
55 * Value {@value}.
56 */
57 public static final int CP_LONG = 4;
58
59 /**
60 * Value {@value}.
61 */
62 public static final int CP_DOUBLE = 5;
63
64 /**
65 * Value {@value}.
66 */
67 public static final int CP_STRING = 6;
68
69 /**
70 * Value {@value}.
71 */
72 public static final int CP_CLASS = 7;
73
74 /**
75 * Value {@value}.
76 */
77 public static final int SIGNATURE = 8; // TODO and more to come --
78
79 /**
80 * Value {@value}.
81 */
82 public static final int CP_DESCR = 9;
83
84 /**
85 * Value {@value}.
86 */
87 public static final int CP_FIELD = 10;
88
89 /**
90 * Value {@value}.
91 */
92 public static final int CP_METHOD = 11;
93
94 /**
95 * Value {@value}.
96 */
97 public static final int CP_IMETHOD = 12;
98
99 /**
100 * Value {@value}.
101 */
102 protected static final String REGEX_MATCH_ALL = ".*";
103
104 /**
105 * Value {@value}.
106 */
107 protected static final String INITSTRING = "<init>";
108
109 /**
110 * Value {@value}.
111 */
112 protected static final String REGEX_MATCH_INIT = "^" + INITSTRING + ".*";
113
114 /**
115 * We don't want a dependency on regex in Pack200. The only place one exists is in matchSpecificPoolEntryIndex(). To eliminate this dependency, we've
116 * implemented the world's stupidest regexMatch. It knows about the two forms we care about: .* (aka REGEX_MATCH_ALL) {@code ^<init>;.*} (aka
117 * REGEX_MATCH_INIT) and will answer correctly if those are passed as the regexString.
118 *
119 * @param regexString String against which the compareString will be matched
120 * @param compareString String to match against the regexString
121 * @return boolean true if the compareString matches the regexString; otherwise false.
122 */
123 protected static boolean regexMatches(final String regexString, final String compareString) {
124 if (REGEX_MATCH_ALL.equals(regexString)) {
125 return true;
126 }
127 if (REGEX_MATCH_INIT.equals(regexString)) {
128 if (compareString.length() < INITSTRING.length()) {
129 return false;
130 }
131 return INITSTRING.equals(compareString.substring(0, INITSTRING.length()));
132 }
133 throw new Error("regex trying to match a pattern I don't know: " + regexString);
134 }
135
136 static int toIndex(final long index) throws Pack200Exception {
137 if (index < 0) {
138 throw new Pack200Exception("Cannot have a negative index.");
139 }
140 return toIntExact(index);
141 }
142
143 static int toIntExact(final long index) throws Pack200Exception {
144 try {
145 return Math.toIntExact(index);
146 } catch (final ArithmeticException e) {
147 throw new Pack200Exception("index", e);
148 }
149 }
150
151 private final CpBands bands;
152
153 private final SegmentConstantPoolArrayCache arrayCache = new SegmentConstantPoolArrayCache();
154
155 /**
156 * Constructs a new instance.
157 *
158 * @param bands Constant pool bands.
159 */
160 public SegmentConstantPool(final CpBands bands) {
161 this.bands = bands;
162 }
163
164 /**
165 * Gets the CPClass associated with a class name. Returns null if the class doesn't exist.
166 *
167 * @param name Class name to look for (form: java/lang/Object)
168 * @return CPClass for that class name, or null if not found.
169 * @throws Pack200Exception if a type is not supported or an index not in the range [0, {@link Integer#MAX_VALUE}].
170 */
171 public ConstantPoolEntry getClassPoolEntry(final String name) throws Pack200Exception {
172 final int index = matchSpecificPoolEntryIndex(bands.getCpClass(), name, 0);
173 return index == -1 ? null : getConstantPoolEntry(CP_CLASS, index);
174 }
175
176 /**
177 * Gets the subset constant pool of the specified type to be just that which has the specified class name. Answer the ConstantPoolEntry at the desiredIndex
178 * of the subset pool.
179 *
180 * @param cp type of constant pool array to search.
181 * @param desiredIndex index of the constant pool.
182 * @param desiredClassName class to use to generate a subset of the pool.
183 * @return ConstantPoolEntry
184 * @throws Pack200Exception if a type is not supported or an index not in the range [0, {@link Integer#MAX_VALUE}].
185 */
186 public ConstantPoolEntry getClassSpecificPoolEntry(final int cp, final long desiredIndex, final String desiredClassName) throws Pack200Exception {
187 final String[] array;
188 switch (cp) {
189 case CP_FIELD:
190 array = bands.getCpFieldClass();
191 break;
192 case CP_METHOD:
193 array = bands.getCpMethodClass();
194 break;
195 case CP_IMETHOD:
196 array = bands.getCpIMethodClass();
197 break;
198 default:
199 throw new Pack200Exception("Type is not supported yet: " + cp);
200 }
201 return getConstantPoolEntry(cp, matchSpecificPoolEntryIndex(array, desiredClassName, toIndex(desiredIndex)));
202 }
203
204 /**
205 * Gets the constant pool entry of the given type and index.
206 *
207 * @param type Constant pool type.
208 * @param index Index into a specific constant pool.
209 * @return a constant pool entry.
210 * @throws Pack200Exception if a type is not supported or the index not in the range [0, {@link Integer#MAX_VALUE}].
211 */
212 public ConstantPoolEntry getConstantPoolEntry(final int type, final long index) throws Pack200Exception {
213 if (index == -1) {
214 return null;
215 }
216 final int actualIndex = toIndex(index);
217 switch (type) {
218 case UTF_8:
219 return bands.cpUTF8Value(actualIndex);
220 case CP_INT:
221 return bands.cpIntegerValue(actualIndex);
222 case CP_FLOAT:
223 return bands.cpFloatValue(actualIndex);
224 case CP_LONG:
225 return bands.cpLongValue(actualIndex);
226 case CP_DOUBLE:
227 return bands.cpDoubleValue(actualIndex);
228 case CP_STRING:
229 return bands.cpStringValue(actualIndex);
230 case CP_CLASS:
231 return bands.cpClassValue(actualIndex);
232 case SIGNATURE:
233 throw new Pack200Exception("Type SIGNATURE is not supported yet: " + SIGNATURE);
234 // return null /* new CPSignature(bands.getCpSignature()[index]) */;
235 case CP_DESCR:
236 throw new Pack200Exception("Type CP_DESCR is not supported yet: " + CP_DESCR);
237 // return null /* new CPDescriptor(bands.getCpDescriptor()[index])
238 // */;
239 case CP_FIELD:
240 return bands.cpFieldValue(actualIndex);
241 case CP_METHOD:
242 return bands.cpMethodValue(actualIndex);
243 case CP_IMETHOD:
244 return bands.cpIMethodValue(actualIndex);
245 default:
246 break;
247 }
248 // etc
249 throw new Pack200Exception("Type is not supported yet: " + type);
250 }
251
252 /**
253 * Gets the {@code init} method for the specified class.
254 *
255 * @param cp constant pool to search, must be {@link #CP_METHOD}.
256 * @param value index of {@code init} method.
257 * @param desiredClassName String class name of the {@code init} method.
258 * @return CPMethod {@code init} method.
259 * @throws Pack200Exception if a type is not supported or an index not in the range [0, {@link Integer#MAX_VALUE}].
260 */
261 public ConstantPoolEntry getInitMethodPoolEntry(final int cp, final long value, final String desiredClassName) throws Pack200Exception {
262 if (cp != CP_METHOD) {
263 throw new Pack200Exception("Nothing but CP_METHOD can be an <init>");
264 }
265 final int realIndex = matchSpecificPoolEntryIndex(bands.getCpMethodClass(), bands.getCpMethodDescriptor(), desiredClassName, REGEX_MATCH_INIT,
266 toIndex(value));
267 return getConstantPoolEntry(cp, realIndex);
268 }
269
270 public ClassFileEntry getValue(final int cp, final long longIndex) throws Pack200Exception {
271 final int index = (int) longIndex;
272 if (index == -1) {
273 return null;
274 }
275 if (index < 0) {
276 throw new Pack200Exception("Cannot have a negative range");
277 }
278 switch (cp) {
279 case UTF_8:
280 return bands.cpUTF8Value(index);
281 case CP_INT:
282 return bands.cpIntegerValue(index);
283 case CP_FLOAT:
284 return bands.cpFloatValue(index);
285 case CP_LONG:
286 return bands.cpLongValue(index);
287 case CP_DOUBLE:
288 return bands.cpDoubleValue(index);
289 case CP_STRING:
290 return bands.cpStringValue(index);
291 case CP_CLASS:
292 return bands.cpClassValue(index);
293 case SIGNATURE:
294 return bands.cpSignatureValue(index);
295 case CP_DESCR:
296 return bands.cpNameAndTypeValue(index);
297 default:
298 break;
299 }
300 throw new Error("Tried to get a value I don't know about: " + cp);
301 }
302
303 /**
304 * A number of things make use of subsets of structures. In one particular example, _super bytecodes will use a subset of method or field classes which have
305 * just those methods / fields defined in the superclass. Similarly, _this bytecodes use just those methods/fields defined in this class, and _init
306 * bytecodes use just those methods that start with {@code <init>}.
307 * <p>
308 * This method takes an array of names, a String to match for, an index and a boolean as parameters, and answers the array position in the array of the
309 * indexth element which matches (or equals) the String (depending on the state of the boolean)
310 * </p>
311 * <p>
312 * In other words, if the class array consists of: Object [position 0, 0th instance of Object] String [position 1, 0th instance of String] String [position
313 * 2, 1st instance of String] Object [position 3, 1st instance of Object] Object [position 4, 2nd instance of Object] then matchSpecificPoolEntryIndex(...,
314 * "Object", 2, false) will answer 4. matchSpecificPoolEntryIndex(..., "String", 0, false) will answer 1.
315 * </p>
316 *
317 * @param nameArray Array of Strings against which the compareString is tested
318 * @param compareString String for which to search
319 * @param desiredIndex nth element with that match (counting from 0)
320 * @return int index into nameArray, or -1 if not found.
321 */
322 protected int matchSpecificPoolEntryIndex(final String[] nameArray, final String compareString, final int desiredIndex) {
323 return matchSpecificPoolEntryIndex(nameArray, nameArray, compareString, REGEX_MATCH_ALL, desiredIndex);
324 }
325
326 /**
327 * This method's function is to look through pairs of arrays. It keeps track of the number of hits it finds using the following basis of comparison for a
328 * hit: - the primaryArray[index] must be .equals() to the primaryCompareString - the secondaryArray[index] .matches() the secondaryCompareString. When the
329 * desiredIndex number of hits has been reached, the index into the original two arrays of the element hit is returned.
330 *
331 * @param primaryArray The first array to search
332 * @param secondaryArray The second array (must be same .length as primaryArray)
333 * @param primaryCompareString The String to compare against primaryArray using .equals()
334 * @param secondaryCompareRegex The String to compare against secondaryArray using .matches()
335 * @param desiredIndex The nth hit whose position we're seeking
336 * @return int index that represents the position of the nth hit in primaryArray and secondaryArray
337 */
338 protected int matchSpecificPoolEntryIndex(final String[] primaryArray, final String[] secondaryArray, final String primaryCompareString,
339 final String secondaryCompareRegex, final int desiredIndex) {
340 int instanceCount = -1;
341 final List<Integer> indexList = arrayCache.indexesForArrayKey(primaryArray, primaryCompareString);
342 if (indexList.isEmpty()) {
343 // Primary key not found, no chance of finding secondary
344 return -1;
345 }
346
347 for (final Integer element : indexList) {
348 final int arrayIndex = element.intValue();
349 if (regexMatches(secondaryCompareRegex, secondaryArray[arrayIndex])) {
350 instanceCount++;
351 if (instanceCount == desiredIndex) {
352 return arrayIndex;
353 }
354 }
355 }
356 // We didn't return in the for loop, so the desiredMatch
357 // with desiredIndex must not exist in the arrays.
358 return -1;
359 }
360 }