-
Notifications
You must be signed in to change notification settings - Fork 266
/
SegmentConstantPool.java
300 lines (283 loc) · 12.5 KB
/
SegmentConstantPool.java
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
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.compress.harmony.unpack200;
import java.util.List;
import org.apache.commons.compress.harmony.pack200.Pack200Exception;
import org.apache.commons.compress.harmony.unpack200.bytecode.ClassFileEntry;
import org.apache.commons.compress.harmony.unpack200.bytecode.ConstantPoolEntry;
/**
* SegmentConstantPool manages the constant pool used for re-creating class files.
*/
public class SegmentConstantPool {
private final CpBands bands;
private final SegmentConstantPoolArrayCache arrayCache = new SegmentConstantPoolArrayCache();
/**
* @param bands TODO
*/
public SegmentConstantPool(final CpBands bands) {
this.bands = bands;
}
// define in archive order
public static final int ALL = 0;
public static final int UTF_8 = 1;
public static final int CP_INT = 2;
public static final int CP_FLOAT = 3;
public static final int CP_LONG = 4;
public static final int CP_DOUBLE = 5;
public static final int CP_STRING = 6;
public static final int CP_CLASS = 7;
public static final int SIGNATURE = 8; // TODO and more to come --
public static final int CP_DESCR = 9;
public static final int CP_FIELD = 10;
public static final int CP_METHOD = 11;
public static final int CP_IMETHOD = 12;
protected static final String REGEX_MATCH_ALL = ".*";
protected static final String INITSTRING = "<init>";
protected static final String REGEX_MATCH_INIT = "^" + INITSTRING + ".*";
public ClassFileEntry getValue(final int cp, final long value) throws Pack200Exception {
final int index = (int) value;
if (index == -1) {
return null;
}
if (index < 0) {
throw new Pack200Exception("Cannot have a negative range");
}
if (cp == UTF_8) {
return bands.cpUTF8Value(index);
}
if (cp == CP_INT) {
return bands.cpIntegerValue(index);
}
if (cp == CP_FLOAT) {
return bands.cpFloatValue(index);
}
if (cp == CP_LONG) {
return bands.cpLongValue(index);
}
if (cp == CP_DOUBLE) {
return bands.cpDoubleValue(index);
}
if (cp == CP_STRING) {
return bands.cpStringValue(index);
}
if (cp == CP_CLASS) {
return bands.cpClassValue(index);
}
if (cp == SIGNATURE) {
return bands.cpSignatureValue(index);
}
if (cp == CP_DESCR) {
return bands.cpNameAndTypeValue(index);
}
throw new Error("Tried to get a value I don't know about: " + cp);
}
/**
* Subset the constant pool of the specified type to be just that which has the specified class name. Answer the
* ConstantPoolEntry at the desiredIndex of the subsetted pool.
*
* @param cp type of constant pool array to search
* @param desiredIndex index of the constant pool
* @param desiredClassName class to use to generate a subset of the pool
* @return ConstantPoolEntry
* @throws Pack200Exception TODO
*/
public ConstantPoolEntry getClassSpecificPoolEntry(final int cp, final long desiredIndex,
final String desiredClassName) throws Pack200Exception {
final int index = (int) desiredIndex;
int realIndex = -1;
String[] array = null;
if (cp == CP_FIELD) {
array = bands.getCpFieldClass();
} else if (cp == CP_METHOD) {
array = bands.getCpMethodClass();
} else if (cp == CP_IMETHOD) {
array = bands.getCpIMethodClass();
} else {
throw new Error("Don't know how to handle " + cp);
}
realIndex = matchSpecificPoolEntryIndex(array, desiredClassName, index);
return getConstantPoolEntry(cp, realIndex);
}
/**
* Given the name of a class, answer the CPClass associated with that class. Answer null if the class doesn't exist.
*
* @param name Class name to look for (form: java/lang/Object)
* @return CPClass for that class name, or null if not found.
*/
public ConstantPoolEntry getClassPoolEntry(final String name) {
final String[] classes = bands.getCpClass();
final int index = matchSpecificPoolEntryIndex(classes, name, 0);
if (index == -1) {
return null;
}
try {
return getConstantPoolEntry(CP_CLASS, index);
} catch (final Pack200Exception ex) {
throw new Error("Error getting class pool entry");
}
}
/**
* Answer the init method for the specified class.
*
* @param cp constant pool to search (must be CP_METHOD)
* @param value index of init method
* @param desiredClassName String class name of the init method
* @return CPMethod init method
* @throws Pack200Exception TODO
*/
public ConstantPoolEntry getInitMethodPoolEntry(final int cp, final long value, final String desiredClassName)
throws Pack200Exception {
int realIndex = -1;
if (cp != CP_METHOD) {
// TODO really an error?
throw new Error("Nothing but CP_METHOD can be an <init>");
}
realIndex = matchSpecificPoolEntryIndex(bands.getCpMethodClass(), bands.getCpMethodDescriptor(),
desiredClassName, REGEX_MATCH_INIT, (int) value);
return getConstantPoolEntry(cp, realIndex);
}
/**
* 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 just those methods / fields defined in the superclass. Similarly,
* _this bytecodes use just those methods/fields defined in this class, and _init bytecodes use just those methods
* that start with {@code <init>}.
*
* 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 indexth element which matches (or equals) the String (depending on the state
* of the boolean)
*
* 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 2, 1st instance of String] Object [position 3, 1st instance of Object]
* Object [position 4, 2nd instance of Object] then matchSpecificPoolEntryIndex(..., "Object", 2, false) will answer
* 4. matchSpecificPoolEntryIndex(..., "String", 0, false) will answer 1.
*
* @param nameArray Array of Strings against which the compareString is tested
* @param compareString String for which to search
* @param desiredIndex nth element with that match (counting from 0)
* @return int index into nameArray, or -1 if not found.
*/
protected int matchSpecificPoolEntryIndex(final String[] nameArray, final String compareString,
final int desiredIndex) {
return matchSpecificPoolEntryIndex(nameArray, nameArray, compareString, REGEX_MATCH_ALL, desiredIndex);
}
/**
* 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 hit: - the primaryArray[index] must be .equals() to the
* primaryCompareString - the secondaryArray[index] .matches() the secondaryCompareString. When the desiredIndex
* number of hits has been reached, the index into the original two arrays of the element hit is returned.
*
* @param primaryArray The first array to search
* @param secondaryArray The second array (must be same .length as primaryArray)
* @param primaryCompareString The String to compare against primaryArray using .equals()
* @param secondaryCompareRegex The String to compare against secondaryArray using .matches()
* @param desiredIndex The nth hit whose position we're seeking
* @return int index that represents the position of the nth hit in primaryArray and secondaryArray
*/
protected int matchSpecificPoolEntryIndex(final String[] primaryArray, final String[] secondaryArray,
final String primaryCompareString, final String secondaryCompareRegex, final int desiredIndex) {
int instanceCount = -1;
final List indexList = arrayCache.indexesForArrayKey(primaryArray, primaryCompareString);
if (indexList.isEmpty()) {
// Primary key not found, no chance of finding secondary
return -1;
}
for (Object element : indexList) {
final int arrayIndex = ((Integer) element).intValue();
if (regexMatches(secondaryCompareRegex, secondaryArray[arrayIndex])) {
instanceCount++;
if (instanceCount == desiredIndex) {
return arrayIndex;
}
}
}
// We didn't return in the for loop, so the desiredMatch
// with desiredIndex must not exist in the arrays.
return -1;
}
/**
* We don't want a dependency on regex in Pack200. The only place one exists is in matchSpecificPoolEntryIndex(). To
* eliminate this dependency, we've implemented the world's stupidest regexMatch. It knows about the two forms we
* care about: .* (aka REGEX_MATCH_ALL) {@code ^<init>;.*} (aka REGEX_MATCH_INIT) and will answer correctly if those
* are passed as the regexString.
*
* @param regexString String against which the compareString will be matched
* @param compareString String to match against the regexString
* @return boolean true if the compareString matches the regexString; otherwise false.
*/
protected static boolean regexMatches(final String regexString, final String compareString) {
if (REGEX_MATCH_ALL.equals(regexString)) {
return true;
}
if (REGEX_MATCH_INIT.equals(regexString)) {
if (compareString.length() < (INITSTRING.length())) {
return false;
}
return (INITSTRING.equals(compareString.substring(0, INITSTRING.length())));
}
throw new Error("regex trying to match a pattern I don't know: " + regexString);
}
public ConstantPoolEntry getConstantPoolEntry(final int cp, final long value) throws Pack200Exception {
final int index = (int) value;
if (index == -1) {
return null;
}
if (index < 0) {
throw new Pack200Exception("Cannot have a negative range");
}
if (cp == UTF_8) {
return bands.cpUTF8Value(index);
}
if (cp == CP_INT) {
return bands.cpIntegerValue(index);
}
if (cp == CP_FLOAT) {
return bands.cpFloatValue(index);
}
if (cp == CP_LONG) {
return bands.cpLongValue(index);
}
if (cp == CP_DOUBLE) {
return bands.cpDoubleValue(index);
}
if (cp == CP_STRING) {
return bands.cpStringValue(index);
}
if (cp == CP_CLASS) {
return bands.cpClassValue(index);
}
if (cp == SIGNATURE) {
throw new Error("I don't know what to do with signatures yet");
// return null /* new CPSignature(bands.getCpSignature()[index]) */;
}
if (cp == CP_DESCR) {
throw new Error("I don't know what to do with descriptors yet");
// return null /* new CPDescriptor(bands.getCpDescriptor()[index])
// */;
}
if (cp == CP_FIELD) {
return bands.cpFieldValue(index);
}
if (cp == CP_METHOD) {
return bands.cpMethodValue(index);
}
if (cp == CP_IMETHOD) {
return bands.cpIMethodValue(index);
}
// etc
throw new Error("Get value incomplete");
}
}