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 eliminate this dependency, we've 053 * implemented the world's stupidest regexMatch. It knows about the two forms we care about: .* (aka REGEX_MATCH_ALL) {@code ^<init>;.*} (aka 054 * REGEX_MATCH_INIT) and will answer correctly if those are passed as the regexString. 055 * 056 * @param regexString String against which the compareString will be matched 057 * @param compareString String to match against the regexString 058 * @return boolean true if the compareString matches the regexString; otherwise false. 059 */ 060 protected static boolean regexMatches(final String regexString, final String compareString) { 061 if (REGEX_MATCH_ALL.equals(regexString)) { 062 return true; 063 } 064 if (REGEX_MATCH_INIT.equals(regexString)) { 065 if (compareString.length() < INITSTRING.length()) { 066 return false; 067 } 068 return INITSTRING.equals(compareString.substring(0, INITSTRING.length())); 069 } 070 throw new Error("regex trying to match a pattern I don't know: " + regexString); 071 } 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 ConstantPoolEntry at the desiredIndex of 104 * 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, final String desiredClassName) throws Pack200Exception { 113 final int index = (int) desiredIndex; 114 int realIndex = -1; 115 String[] array; 116 switch (cp) { 117 case CP_FIELD: 118 array = bands.getCpFieldClass(); 119 break; 120 case CP_METHOD: 121 array = bands.getCpMethodClass(); 122 break; 123 case CP_IMETHOD: 124 array = bands.getCpIMethodClass(); 125 break; 126 default: 127 throw new Error("Don't know how to handle " + cp); 128 } 129 realIndex = matchSpecificPoolEntryIndex(array, desiredClassName, index); 130 return getConstantPoolEntry(cp, realIndex); 131 } 132 133 public ConstantPoolEntry getConstantPoolEntry(final int cp, final long value) throws Pack200Exception { 134 final int index = (int) value; 135 if (index == -1) { 136 return null; 137 } 138 if (index < 0) { 139 throw new Pack200Exception("Cannot have a negative range"); 140 } 141 switch (cp) { 142 case UTF_8: 143 return bands.cpUTF8Value(index); 144 case CP_INT: 145 return bands.cpIntegerValue(index); 146 case CP_FLOAT: 147 return bands.cpFloatValue(index); 148 case CP_LONG: 149 return bands.cpLongValue(index); 150 case CP_DOUBLE: 151 return bands.cpDoubleValue(index); 152 case CP_STRING: 153 return bands.cpStringValue(index); 154 case CP_CLASS: 155 return bands.cpClassValue(index); 156 case SIGNATURE: 157 throw new Error("I don't know what to do with signatures yet"); 158 // return null /* new CPSignature(bands.getCpSignature()[index]) */; 159 case CP_DESCR: 160 throw new Error("I don't know what to do with descriptors yet"); 161 // return null /* new CPDescriptor(bands.getCpDescriptor()[index]) 162 // */; 163 case CP_FIELD: 164 return bands.cpFieldValue(index); 165 case CP_METHOD: 166 return bands.cpMethodValue(index); 167 case CP_IMETHOD: 168 return bands.cpIMethodValue(index); 169 default: 170 break; 171 } 172 // etc 173 throw new Error("Get value incomplete"); 174 } 175 176 /** 177 * Answer the init method for the specified class. 178 * 179 * @param cp constant pool to search (must be CP_METHOD) 180 * @param value index of init method 181 * @param desiredClassName String class name of the init method 182 * @return CPMethod init method 183 * @throws Pack200Exception TODO 184 */ 185 public ConstantPoolEntry getInitMethodPoolEntry(final int cp, final long value, final String desiredClassName) throws Pack200Exception { 186 int realIndex = -1; 187 if (cp != CP_METHOD) { 188 // TODO really an error? 189 throw new Error("Nothing but CP_METHOD can be an <init>"); 190 } 191 realIndex = matchSpecificPoolEntryIndex(bands.getCpMethodClass(), bands.getCpMethodDescriptor(), desiredClassName, REGEX_MATCH_INIT, (int) value); 192 return getConstantPoolEntry(cp, realIndex); 193 } 194 195 public ClassFileEntry getValue(final int cp, final long value) throws Pack200Exception { 196 final int index = (int) value; 197 if (index == -1) { 198 return null; 199 } 200 if (index < 0) { 201 throw new Pack200Exception("Cannot have a negative range"); 202 } 203 switch (cp) { 204 case UTF_8: 205 return bands.cpUTF8Value(index); 206 case CP_INT: 207 return bands.cpIntegerValue(index); 208 case CP_FLOAT: 209 return bands.cpFloatValue(index); 210 case CP_LONG: 211 return bands.cpLongValue(index); 212 case CP_DOUBLE: 213 return bands.cpDoubleValue(index); 214 case CP_STRING: 215 return bands.cpStringValue(index); 216 case CP_CLASS: 217 return bands.cpClassValue(index); 218 case SIGNATURE: 219 return bands.cpSignatureValue(index); 220 case CP_DESCR: 221 return bands.cpNameAndTypeValue(index); 222 default: 223 break; 224 } 225 throw new Error("Tried to get a value I don't know about: " + cp); 226 } 227 228 /** 229 * 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 230 * just those methods / fields defined in the superclass. Similarly, _this bytecodes use just those methods/fields defined in this class, and _init 231 * bytecodes use just those methods that start with {@code <init>}. 232 * 233 * 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 234 * indexth element which matches (or equals) the String (depending on the state of the boolean) 235 * 236 * 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 237 * 2, 1st instance of String] Object [position 3, 1st instance of Object] Object [position 4, 2nd instance of Object] then matchSpecificPoolEntryIndex(..., 238 * "Object", 2, false) will answer 4. matchSpecificPoolEntryIndex(..., "String", 0, false) will answer 1. 239 * 240 * @param nameArray Array of Strings against which the compareString is tested 241 * @param compareString String for which to search 242 * @param desiredIndex nth element with that match (counting from 0) 243 * @return int index into nameArray, or -1 if not found. 244 */ 245 protected int matchSpecificPoolEntryIndex(final String[] nameArray, final String compareString, final int desiredIndex) { 246 return matchSpecificPoolEntryIndex(nameArray, nameArray, compareString, REGEX_MATCH_ALL, desiredIndex); 247 } 248 249 /** 250 * 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 251 * hit: - the primaryArray[index] must be .equals() to the primaryCompareString - the secondaryArray[index] .matches() the secondaryCompareString. When the 252 * desiredIndex number of hits has been reached, the index into the original two arrays of the element hit is returned. 253 * 254 * @param primaryArray The first array to search 255 * @param secondaryArray The second array (must be same .length as primaryArray) 256 * @param primaryCompareString The String to compare against primaryArray using .equals() 257 * @param secondaryCompareRegex The String to compare against secondaryArray using .matches() 258 * @param desiredIndex The nth hit whose position we're seeking 259 * @return int index that represents the position of the nth hit in primaryArray and secondaryArray 260 */ 261 protected int matchSpecificPoolEntryIndex(final String[] primaryArray, final String[] secondaryArray, final String primaryCompareString, 262 final String secondaryCompareRegex, final int desiredIndex) { 263 int instanceCount = -1; 264 final List<Integer> indexList = arrayCache.indexesForArrayKey(primaryArray, primaryCompareString); 265 if (indexList.isEmpty()) { 266 // Primary key not found, no chance of finding secondary 267 return -1; 268 } 269 270 for (final Integer element : indexList) { 271 final int arrayIndex = element.intValue(); 272 if (regexMatches(secondaryCompareRegex, secondaryArray[arrayIndex])) { 273 instanceCount++; 274 if (instanceCount == desiredIndex) { 275 return arrayIndex; 276 } 277 } 278 } 279 // We didn't return in the for loop, so the desiredMatch 280 // with desiredIndex must not exist in the arrays. 281 return -1; 282 } 283}