001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * https://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.apache.commons.compress.harmony.unpack200; 020 021import java.util.List; 022 023import org.apache.commons.compress.harmony.pack200.Pack200Exception; 024import org.apache.commons.compress.harmony.unpack200.bytecode.ClassFileEntry; 025import org.apache.commons.compress.harmony.unpack200.bytecode.ConstantPoolEntry; 026 027/** 028 * Manages the constant pool used for re-creating class files. 029 */ 030public class SegmentConstantPool { 031 032 /** 033 * Value {@value}. 034 */ 035 public static final int ALL = 0; 036 037 /** 038 * Value {@value}. 039 */ 040 public static final int UTF_8 = 1; 041 042 /** 043 * Value {@value}. 044 */ 045 public static final int CP_INT = 2; 046 047 // define in archive order 048 049 /** 050 * Value {@value}. 051 */ 052 public static final int CP_FLOAT = 3; 053 054 /** 055 * Value {@value}. 056 */ 057 public static final int CP_LONG = 4; 058 059 /** 060 * Value {@value}. 061 */ 062 public static final int CP_DOUBLE = 5; 063 064 /** 065 * Value {@value}. 066 */ 067 public static final int CP_STRING = 6; 068 069 /** 070 * Value {@value}. 071 */ 072 public static final int CP_CLASS = 7; 073 074 /** 075 * Value {@value}. 076 */ 077 public static final int SIGNATURE = 8; // TODO and more to come -- 078 079 /** 080 * Value {@value}. 081 */ 082 public static final int CP_DESCR = 9; 083 084 /** 085 * Value {@value}. 086 */ 087 public static final int CP_FIELD = 10; 088 089 /** 090 * Value {@value}. 091 */ 092 public static final int CP_METHOD = 11; 093 094 /** 095 * Value {@value}. 096 */ 097 public static final int CP_IMETHOD = 12; 098 099 /** 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}