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 final String desiredRegex = REGEX_MATCH_INIT; 158 if (cp != CP_METHOD) { 159 // TODO really an error? 160 throw new Error("Nothing but CP_METHOD can be an <init>"); 161 } 162 realIndex = matchSpecificPoolEntryIndex(bands.getCpMethodClass(), bands.getCpMethodDescriptor(), 163 desiredClassName, desiredRegex, (int) value); 164 return getConstantPoolEntry(cp, realIndex); 165 } 166 167 /** 168 * A number of things make use of subsets of structures. In one particular example, _super bytecodes will use a 169 * subset of method or field classes which have just those methods / fields defined in the superclass. Similarly, 170 * _this bytecodes use just those methods/fields defined in this class, and _init bytecodes use just those methods 171 * that start with {@code <init>}. 172 * 173 * This method takes an array of names, a String to match for, an index and a boolean as parameters, and answers the 174 * array position in the array of the indexth element which matches (or equals) the String (depending on the state 175 * of the boolean) 176 * 177 * In other words, if the class array consists of: Object [position 0, 0th instance of Object] String [position 1, 178 * 0th instance of String] String [position 2, 1st instance of String] Object [position 3, 1st instance of Object] 179 * Object [position 4, 2nd instance of Object] then matchSpecificPoolEntryIndex(..., "Object", 2, false) will answer 180 * 4. matchSpecificPoolEntryIndex(..., "String", 0, false) will answer 1. 181 * 182 * @param nameArray Array of Strings against which the compareString is tested 183 * @param compareString String for which to search 184 * @param desiredIndex nth element with that match (counting from 0) 185 * @return int index into nameArray, or -1 if not found. 186 */ 187 protected int matchSpecificPoolEntryIndex(final String[] nameArray, final String compareString, 188 final int desiredIndex) { 189 return matchSpecificPoolEntryIndex(nameArray, nameArray, compareString, REGEX_MATCH_ALL, desiredIndex); 190 } 191 192 /** 193 * This method's function is to look through pairs of arrays. It keeps track of the number of hits it finds using 194 * the following basis of comparison for a hit: - the primaryArray[index] must be .equals() to the 195 * primaryCompareString - the secondaryArray[index] .matches() the secondaryCompareString. When the desiredIndex 196 * number of hits has been reached, the index into the original two arrays of the element hit is returned. 197 * 198 * @param primaryArray The first array to search 199 * @param secondaryArray The second array (must be same .length as primaryArray) 200 * @param primaryCompareString The String to compare against primaryArray using .equals() 201 * @param secondaryCompareRegex The String to compare against secondaryArray using .matches() 202 * @param desiredIndex The nth hit whose position we're seeking 203 * @return int index that represents the position of the nth hit in primaryArray and secondaryArray 204 */ 205 protected int matchSpecificPoolEntryIndex(final String[] primaryArray, final String[] secondaryArray, 206 final String primaryCompareString, final String secondaryCompareRegex, final int desiredIndex) { 207 int instanceCount = -1; 208 final List indexList = arrayCache.indexesForArrayKey(primaryArray, primaryCompareString); 209 if (indexList.isEmpty()) { 210 // Primary key not found, no chance of finding secondary 211 return -1; 212 } 213 214 for (int index = 0; index < indexList.size(); index++) { 215 final int arrayIndex = ((Integer) indexList.get(index)).intValue(); 216 if (regexMatches(secondaryCompareRegex, secondaryArray[arrayIndex])) { 217 instanceCount++; 218 if (instanceCount == desiredIndex) { 219 return arrayIndex; 220 } 221 } 222 } 223 // We didn't return in the for loop, so the desiredMatch 224 // with desiredIndex must not exist in the arrays. 225 return -1; 226 } 227 228 /** 229 * We don't want a dependency on regex in Pack200. The only place one exists is in matchSpecificPoolEntryIndex(). To 230 * eliminate this dependency, we've implemented the world's stupidest regexMatch. It knows about the two forms we 231 * care about: .* (aka REGEX_MATCH_ALL) {@code ^<init>;.*} (aka REGEX_MATCH_INIT) and will answer correctly if those 232 * are passed as the regexString. 233 * 234 * @param regexString String against which the compareString will be matched 235 * @param compareString String to match against the regexString 236 * @return boolean true if the compareString matches the regexString; otherwise false. 237 */ 238 protected static boolean regexMatches(final String regexString, final String compareString) { 239 if (REGEX_MATCH_ALL.equals(regexString)) { 240 return true; 241 } 242 if (REGEX_MATCH_INIT.equals(regexString)) { 243 if (compareString.length() < (INITSTRING.length())) { 244 return false; 245 } 246 return (INITSTRING.equals(compareString.substring(0, INITSTRING.length()))); 247 } 248 throw new Error("regex trying to match a pattern I don't know: " + regexString); 249 } 250 251 public ConstantPoolEntry getConstantPoolEntry(final int cp, final long value) throws Pack200Exception { 252 final int index = (int) value; 253 if (index == -1) { 254 return null; 255 } 256 if (index < 0) { 257 throw new Pack200Exception("Cannot have a negative range"); 258 } 259 if (cp == UTF_8) { 260 return bands.cpUTF8Value(index); 261 } 262 if (cp == CP_INT) { 263 return bands.cpIntegerValue(index); 264 } 265 if (cp == CP_FLOAT) { 266 return bands.cpFloatValue(index); 267 } 268 if (cp == CP_LONG) { 269 return bands.cpLongValue(index); 270 } 271 if (cp == CP_DOUBLE) { 272 return bands.cpDoubleValue(index); 273 } 274 if (cp == CP_STRING) { 275 return bands.cpStringValue(index); 276 } 277 if (cp == CP_CLASS) { 278 return bands.cpClassValue(index); 279 } 280 if (cp == SIGNATURE) { 281 throw new Error("I don't know what to do with signatures yet"); 282 // return null /* new CPSignature(bands.getCpSignature()[index]) */; 283 } 284 if (cp == CP_DESCR) { 285 throw new Error("I don't know what to do with descriptors yet"); 286 // return null /* new CPDescriptor(bands.getCpDescriptor()[index]) 287 // */; 288 } 289 if (cp == CP_FIELD) { 290 return bands.cpFieldValue(index); 291 } 292 if (cp == CP_METHOD) { 293 return bands.cpMethodValue(index); 294 } 295 if (cp == CP_IMETHOD) { 296 return bands.cpIMethodValue(index); 297 } 298 // etc 299 throw new Error("Get value incomplete"); 300 } 301}