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