001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.compress.harmony.unpack200; 018 019import java.util.List; 020 021import org.apache.commons.compress.harmony.pack200.Pack200Exception; 022import org.apache.commons.compress.harmony.unpack200.bytecode.ClassFileEntry; 023import org.apache.commons.compress.harmony.unpack200.bytecode.ConstantPoolEntry; 024 025/** 026 * SegmentConstantPool manages the constant pool used for re-creating class files. 027 */ 028public class SegmentConstantPool { 029 030 public static final int ALL = 0; 031 public static final int UTF_8 = 1; 032 033 public static final int CP_INT = 2; 034 035 // define in archive order 036 037 public static final int CP_FLOAT = 3; 038 public static final int CP_LONG = 4; 039 public static final int CP_DOUBLE = 5; 040 public static final int CP_STRING = 6; 041 public static final int CP_CLASS = 7; 042 public static final int SIGNATURE = 8; // TODO and more to come -- 043 public static final int CP_DESCR = 9; 044 public static final int CP_FIELD = 10; 045 public static final int CP_METHOD = 11; 046 public static final int CP_IMETHOD = 12; 047 protected static final String REGEX_MATCH_ALL = ".*"; 048 protected static final String INITSTRING = "<init>"; 049 protected static final String REGEX_MATCH_INIT = "^" + INITSTRING + ".*"; 050 051 /** 052 * We don't want a dependency on regex in Pack200. The only place one exists is in matchSpecificPoolEntryIndex(). To 053 * eliminate this dependency, we've implemented the world's stupidest regexMatch. It knows about the two forms we 054 * care about: .* (aka REGEX_MATCH_ALL) {@code ^<init>;.*} (aka REGEX_MATCH_INIT) and will answer correctly if those 055 * are passed as the regexString. 056 * 057 * @param regexString String against which the compareString will be matched 058 * @param compareString String to match against the regexString 059 * @return boolean true if the compareString matches the regexString; otherwise false. 060 */ 061 protected static boolean regexMatches(final String regexString, final String compareString) { 062 if (REGEX_MATCH_ALL.equals(regexString)) { 063 return true; 064 } 065 if (REGEX_MATCH_INIT.equals(regexString)) { 066 if (compareString.length() < INITSTRING.length()) { 067 return false; 068 } 069 return INITSTRING.equals(compareString.substring(0, INITSTRING.length())); 070 } 071 throw new Error("regex trying to match a pattern I don't know: " + regexString); 072 } 073 private final CpBands bands; 074 private final SegmentConstantPoolArrayCache arrayCache = new SegmentConstantPoolArrayCache(); 075 076 /** 077 * @param bands TODO 078 */ 079 public SegmentConstantPool(final CpBands bands) { 080 this.bands = bands; 081 } 082 083 /** 084 * Given the name of a class, answer the CPClass associated with that class. Answer null if the class doesn't exist. 085 * 086 * @param name Class name to look for (form: java/lang/Object) 087 * @return CPClass for that class name, or null if not found. 088 */ 089 public ConstantPoolEntry getClassPoolEntry(final String name) { 090 final String[] classes = bands.getCpClass(); 091 final int index = matchSpecificPoolEntryIndex(classes, name, 0); 092 if (index == -1) { 093 return null; 094 } 095 try { 096 return getConstantPoolEntry(CP_CLASS, index); 097 } catch (final Pack200Exception ex) { 098 throw new Error("Error getting class pool entry"); 099 } 100 } 101 102 /** 103 * Subset the constant pool of the specified type to be just that which has the specified class name. Answer the 104 * ConstantPoolEntry at the desiredIndex of the subsetted pool. 105 * 106 * @param cp type of constant pool array to search 107 * @param desiredIndex index of the constant pool 108 * @param desiredClassName class to use to generate a subset of the pool 109 * @return ConstantPoolEntry 110 * @throws Pack200Exception TODO 111 */ 112 public ConstantPoolEntry getClassSpecificPoolEntry(final int cp, final long desiredIndex, 113 final String desiredClassName) throws Pack200Exception { 114 final int index = (int) desiredIndex; 115 int realIndex = -1; 116 String[] array; 117 switch (cp) { 118 case CP_FIELD: 119 array = bands.getCpFieldClass(); 120 break; 121 case CP_METHOD: 122 array = bands.getCpMethodClass(); 123 break; 124 case CP_IMETHOD: 125 array = bands.getCpIMethodClass(); 126 break; 127 default: 128 throw new Error("Don't know how to handle " + cp); 129 } 130 realIndex = matchSpecificPoolEntryIndex(array, desiredClassName, index); 131 return getConstantPoolEntry(cp, realIndex); 132 } 133 134 public ConstantPoolEntry getConstantPoolEntry(final int cp, final long value) throws Pack200Exception { 135 final int index = (int) value; 136 if (index == -1) { 137 return null; 138 } 139 if (index < 0) { 140 throw new Pack200Exception("Cannot have a negative range"); 141 } 142 switch (cp) { 143 case UTF_8: 144 return bands.cpUTF8Value(index); 145 case CP_INT: 146 return bands.cpIntegerValue(index); 147 case CP_FLOAT: 148 return bands.cpFloatValue(index); 149 case CP_LONG: 150 return bands.cpLongValue(index); 151 case CP_DOUBLE: 152 return bands.cpDoubleValue(index); 153 case CP_STRING: 154 return bands.cpStringValue(index); 155 case CP_CLASS: 156 return bands.cpClassValue(index); 157 case SIGNATURE: 158 throw new Error("I don't know what to do with signatures yet"); 159 // return null /* new CPSignature(bands.getCpSignature()[index]) */; 160 case CP_DESCR: 161 throw new Error("I don't know what to do with descriptors yet"); 162 // return null /* new CPDescriptor(bands.getCpDescriptor()[index]) 163 // */; 164 case CP_FIELD: 165 return bands.cpFieldValue(index); 166 case CP_METHOD: 167 return bands.cpMethodValue(index); 168 case CP_IMETHOD: 169 return bands.cpIMethodValue(index); 170 default: 171 break; 172 } 173 // etc 174 throw new Error("Get value incomplete"); 175 } 176 177 /** 178 * Answer the init method for the specified class. 179 * 180 * @param cp constant pool to search (must be CP_METHOD) 181 * @param value index of init method 182 * @param desiredClassName String class name of the init method 183 * @return CPMethod init method 184 * @throws Pack200Exception TODO 185 */ 186 public ConstantPoolEntry getInitMethodPoolEntry(final int cp, final long value, final String desiredClassName) 187 throws Pack200Exception { 188 int realIndex = -1; 189 if (cp != CP_METHOD) { 190 // TODO really an error? 191 throw new Error("Nothing but CP_METHOD can be an <init>"); 192 } 193 realIndex = matchSpecificPoolEntryIndex(bands.getCpMethodClass(), bands.getCpMethodDescriptor(), 194 desiredClassName, REGEX_MATCH_INIT, (int) value); 195 return getConstantPoolEntry(cp, realIndex); 196 } 197 198 public ClassFileEntry getValue(final int cp, final long value) throws Pack200Exception { 199 final int index = (int) value; 200 if (index == -1) { 201 return null; 202 } 203 if (index < 0) { 204 throw new Pack200Exception("Cannot have a negative range"); 205 } 206 switch (cp) { 207 case UTF_8: 208 return bands.cpUTF8Value(index); 209 case CP_INT: 210 return bands.cpIntegerValue(index); 211 case CP_FLOAT: 212 return bands.cpFloatValue(index); 213 case CP_LONG: 214 return bands.cpLongValue(index); 215 case CP_DOUBLE: 216 return bands.cpDoubleValue(index); 217 case CP_STRING: 218 return bands.cpStringValue(index); 219 case CP_CLASS: 220 return bands.cpClassValue(index); 221 case SIGNATURE: 222 return bands.cpSignatureValue(index); 223 case CP_DESCR: 224 return bands.cpNameAndTypeValue(index); 225 default: 226 break; 227 } 228 throw new Error("Tried to get a value I don't know about: " + cp); 229 } 230 231 /** 232 * A number of things make use of subsets of structures. In one particular example, _super bytecodes will use a 233 * subset of method or field classes which have just those methods / fields defined in the superclass. Similarly, 234 * _this bytecodes use just those methods/fields defined in this class, and _init bytecodes use just those methods 235 * that start with {@code <init>}. 236 * 237 * This method takes an array of names, a String to match for, an index and a boolean as parameters, and answers the 238 * array position in the array of the indexth element which matches (or equals) the String (depending on the state 239 * of the boolean) 240 * 241 * In other words, if the class array consists of: Object [position 0, 0th instance of Object] String [position 1, 242 * 0th instance of String] String [position 2, 1st instance of String] Object [position 3, 1st instance of Object] 243 * Object [position 4, 2nd instance of Object] then matchSpecificPoolEntryIndex(..., "Object", 2, false) will answer 244 * 4. matchSpecificPoolEntryIndex(..., "String", 0, false) will answer 1. 245 * 246 * @param nameArray Array of Strings against which the compareString is tested 247 * @param compareString String for which to search 248 * @param desiredIndex nth element with that match (counting from 0) 249 * @return int index into nameArray, or -1 if not found. 250 */ 251 protected int matchSpecificPoolEntryIndex(final String[] nameArray, final String compareString, 252 final int desiredIndex) { 253 return matchSpecificPoolEntryIndex(nameArray, nameArray, compareString, REGEX_MATCH_ALL, desiredIndex); 254 } 255 256 /** 257 * This method's function is to look through pairs of arrays. It keeps track of the number of hits it finds using 258 * the following basis of comparison for a hit: - the primaryArray[index] must be .equals() to the 259 * primaryCompareString - the secondaryArray[index] .matches() the secondaryCompareString. When the desiredIndex 260 * number of hits has been reached, the index into the original two arrays of the element hit is returned. 261 * 262 * @param primaryArray The first array to search 263 * @param secondaryArray The second array (must be same .length as primaryArray) 264 * @param primaryCompareString The String to compare against primaryArray using .equals() 265 * @param secondaryCompareRegex The String to compare against secondaryArray using .matches() 266 * @param desiredIndex The nth hit whose position we're seeking 267 * @return int index that represents the position of the nth hit in primaryArray and secondaryArray 268 */ 269 protected int matchSpecificPoolEntryIndex(final String[] primaryArray, final String[] secondaryArray, 270 final String primaryCompareString, final String secondaryCompareRegex, final int desiredIndex) { 271 int instanceCount = -1; 272 final List<Integer> indexList = arrayCache.indexesForArrayKey(primaryArray, primaryCompareString); 273 if (indexList.isEmpty()) { 274 // Primary key not found, no chance of finding secondary 275 return -1; 276 } 277 278 for (final Integer element : indexList) { 279 final int arrayIndex = element.intValue(); 280 if (regexMatches(secondaryCompareRegex, secondaryArray[arrayIndex])) { 281 instanceCount++; 282 if (instanceCount == desiredIndex) { 283 return arrayIndex; 284 } 285 } 286 } 287 // We didn't return in the for loop, so the desiredMatch 288 // with desiredIndex must not exist in the arrays. 289 return -1; 290 } 291}