1 /* 2 * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.invoke.anon; 27 28 import java.io.IOException; 29 import java.io.OutputStream; 30 import java.util.Arrays; 31 import java.util.HashSet; 32 import java.util.IdentityHashMap; 33 import java.util.Map; 34 35 import static sun.invoke.anon.ConstantPoolVisitor.*; 36 37 /** A class and its patched constant pool. 38 * 39 * This class allow to modify (patch) a constant pool 40 * by changing the value of its entry. 41 * Entry are referenced using index that can be get 42 * by parsing the constant pool using 43 * {@link ConstantPoolParser#parse(ConstantPoolVisitor)}. 44 * 45 * @see ConstantPoolVisitor 46 * @see ConstantPoolParser#createPatch() 47 */ 48 public class ConstantPoolPatch { 49 final ConstantPoolParser outer; 50 final Object[] patchArray; 51 52 ConstantPoolPatch(ConstantPoolParser outer) { 53 this.outer = outer; 54 this.patchArray = new Object[outer.getLength()]; 55 } 56 57 /** Create a {@link ConstantPoolParser} and 58 * a {@link ConstantPoolPatch} in one step. 59 * Equivalent to {@code new ConstantPoolParser(classFile).createPatch()}. 60 * 61 * @param classFile an array of bytes containing a class. 62 * @see #ConstantPoolParser(Class) 63 */ 64 public ConstantPoolPatch(byte[] classFile) throws InvalidConstantPoolFormatException { 65 this(new ConstantPoolParser(classFile)); 66 } 67 68 /** Create a {@link ConstantPoolParser} and 69 * a {@link ConstantPoolPatch} in one step. 70 * Equivalent to {@code new ConstantPoolParser(templateClass).createPatch()}. 71 * 72 * @param templateClass the class to parse. 73 * @see #ConstantPoolParser(Class) 74 */ 75 public ConstantPoolPatch(Class<?> templateClass) throws IOException, InvalidConstantPoolFormatException { 76 this(new ConstantPoolParser(templateClass)); 77 } 78 79 80 /** Creates a patch from an existing patch. 81 * All changes are copied from that patch. 82 * @param patch a patch 83 * 84 * @see ConstantPoolParser#createPatch() 85 */ 86 public ConstantPoolPatch(ConstantPoolPatch patch) { 87 outer = patch.outer; 88 patchArray = patch.patchArray.clone(); 89 } 90 91 /** Which parser built this patch? */ 92 public ConstantPoolParser getParser() { 93 return outer; 94 } 95 96 /** Report the tag at the given index in the constant pool. */ 97 public byte getTag(int index) { 98 return outer.getTag(index); 99 } 100 101 /** Report the current patch at the given index of the constant pool. 102 * Null means no patch will be made. 103 * To observe the unpatched entry at the given index, use 104 * {@link #getParser()}{@code .}@link ConstantPoolParser#parse(ConstantPoolVisitor)} 105 */ 106 public Object getPatch(int index) { 107 Object value = patchArray[index]; 108 if (value == null) return null; 109 switch (getTag(index)) { 110 case CONSTANT_Fieldref: 111 case CONSTANT_Methodref: 112 case CONSTANT_InterfaceMethodref: 113 if (value instanceof String) 114 value = stripSemis(2, (String) value); 115 break; 116 case CONSTANT_NameAndType: 117 if (value instanceof String) 118 value = stripSemis(1, (String) value); 119 break; 120 } 121 return value; 122 } 123 124 /** Clear all patches. */ 125 public void clear() { 126 Arrays.fill(patchArray, null); 127 } 128 129 /** Clear one patch. */ 130 public void clear(int index) { 131 patchArray[index] = null; 132 } 133 134 /** Produce the patches as an array. */ 135 public Object[] getPatches() { 136 return patchArray.clone(); 137 } 138 139 /** Produce the original constant pool as an array. */ 140 public Object[] getOriginalCP() throws InvalidConstantPoolFormatException { 141 return getOriginalCP(0, patchArray.length, -1); 142 } 143 144 /** Walk the constant pool, applying patches using the given map. 145 * 146 * @param utf8Map Utf8 strings to modify, if encountered 147 * @param classMap Classes (or their names) to modify, if encountered 148 * @param valueMap Constant values to modify, if encountered 149 * @param deleteUsedEntries if true, delete map entries that are used 150 */ 151 public void putPatches(final Map<String,String> utf8Map, 152 final Map<String,Object> classMap, 153 final Map<Object,Object> valueMap, 154 boolean deleteUsedEntries) throws InvalidConstantPoolFormatException { 155 final HashSet<String> usedUtf8Keys; 156 final HashSet<String> usedClassKeys; 157 final HashSet<Object> usedValueKeys; 158 if (deleteUsedEntries) { 159 usedUtf8Keys = (utf8Map == null) ? null : new HashSet<String>(); 160 usedClassKeys = (classMap == null) ? null : new HashSet<String>(); 161 usedValueKeys = (valueMap == null) ? null : new HashSet<Object>(); 162 } else { 163 usedUtf8Keys = null; 164 usedClassKeys = null; 165 usedValueKeys = null; 166 } 167 168 outer.parse(new ConstantPoolVisitor() { 169 170 @Override 171 public void visitUTF8(int index, byte tag, String utf8) { 172 putUTF8(index, utf8Map.get(utf8)); 173 if (usedUtf8Keys != null) usedUtf8Keys.add(utf8); 174 } 175 176 @Override 177 public void visitConstantValue(int index, byte tag, Object value) { 178 putConstantValue(index, tag, valueMap.get(value)); 179 if (usedValueKeys != null) usedValueKeys.add(value); 180 } 181 182 @Override 183 public void visitConstantString(int index, byte tag, String name, int nameIndex) { 184 if (tag == CONSTANT_Class) { 185 putConstantValue(index, tag, classMap.get(name)); 186 if (usedClassKeys != null) usedClassKeys.add(name); 187 } else { 188 assert(tag == CONSTANT_String); 189 visitConstantValue(index, tag, name); 190 } 191 } 192 }); 193 if (usedUtf8Keys != null) utf8Map.keySet().removeAll(usedUtf8Keys); 194 if (usedClassKeys != null) classMap.keySet().removeAll(usedClassKeys); 195 if (usedValueKeys != null) valueMap.keySet().removeAll(usedValueKeys); 196 } 197 198 Object[] getOriginalCP(final int startIndex, 199 final int endIndex, 200 final int tagMask) throws InvalidConstantPoolFormatException { 201 final Object[] cpArray = new Object[endIndex - startIndex]; 202 outer.parse(new ConstantPoolVisitor() { 203 204 void show(int index, byte tag, Object value) { 205 if (index < startIndex || index >= endIndex) return; 206 if (((1 << tag) & tagMask) == 0) return; 207 cpArray[index - startIndex] = value; 208 } 209 210 @Override 211 public void visitUTF8(int index, byte tag, String utf8) { 212 show(index, tag, utf8); 213 } 214 215 @Override 216 public void visitConstantValue(int index, byte tag, Object value) { 217 assert(tag != CONSTANT_String); 218 show(index, tag, value); 219 } 220 221 @Override 222 public void visitConstantString(int index, byte tag, 223 String value, int j) { 224 show(index, tag, value); 225 } 226 227 @Override 228 public void visitMemberRef(int index, byte tag, 229 String className, String memberName, 230 String signature, 231 int j, int k) { 232 show(index, tag, new String[]{ className, memberName, signature }); 233 } 234 235 @Override 236 public void visitDescriptor(int index, byte tag, 237 String memberName, String signature, 238 int j, int k) { 239 show(index, tag, new String[]{ memberName, signature }); 240 } 241 }); 242 return cpArray; 243 } 244 245 /** Write the head (header plus constant pool) 246 * of the patched class file to the indicated stream. 247 */ 248 void writeHead(OutputStream out) throws IOException { 249 outer.writePatchedHead(out, patchArray); 250 } 251 252 /** Write the tail (everything after the constant pool) 253 * of the patched class file to the indicated stream. 254 */ 255 void writeTail(OutputStream out) throws IOException { 256 outer.writeTail(out); 257 } 258 259 private void checkConstantTag(byte tag, Object value) { 260 if (value == null) 261 throw new IllegalArgumentException( 262 "invalid null constant value"); 263 if (classForTag(tag) != value.getClass()) 264 throw new IllegalArgumentException( 265 "invalid constant value" 266 + (tag == CONSTANT_None ? "" 267 : " for tag "+tagName(tag)) 268 + " of class "+value.getClass()); 269 } 270 271 private void checkTag(int index, byte putTag) { 272 byte tag = outer.tags[index]; 273 if (tag != putTag) 274 throw new IllegalArgumentException( 275 "invalid put operation" 276 + " for " + tagName(putTag) 277 + " at index " + index + " found " + tagName(tag)); 278 } 279 280 private void checkTagMask(int index, int tagBitMask) { 281 byte tag = outer.tags[index]; 282 int tagBit = ((tag & 0x1F) == tag) ? (1 << tag) : 0; 283 if ((tagBit & tagBitMask) == 0) 284 throw new IllegalArgumentException( 285 "invalid put operation" 286 + " at index " + index + " found " + tagName(tag)); 287 } 288 289 private static void checkMemberName(String memberName) { 290 if (memberName.indexOf(';') >= 0) 291 throw new IllegalArgumentException("memberName " + memberName + " contains a ';'"); 292 } 293 294 /** Set the entry of the constant pool indexed by index to 295 * a new string. 296 * 297 * @param index an index to a constant pool entry containing a 298 * {@link ConstantPoolVisitor#CONSTANT_Utf8} value. 299 * @param utf8 a string 300 * 301 * @see ConstantPoolVisitor#visitUTF8(int, byte, String) 302 */ 303 public void putUTF8(int index, String utf8) { 304 if (utf8 == null) { clear(index); return; } 305 checkTag(index, CONSTANT_Utf8); 306 patchArray[index] = utf8; 307 } 308 309 /** Set the entry of the constant pool indexed by index to 310 * a new value, depending on its dynamic type. 311 * 312 * @param index an index to a constant pool entry containing a 313 * one of the following structures: 314 * {@link ConstantPoolVisitor#CONSTANT_Integer}, 315 * {@link ConstantPoolVisitor#CONSTANT_Float}, 316 * {@link ConstantPoolVisitor#CONSTANT_Long}, 317 * {@link ConstantPoolVisitor#CONSTANT_Double}, 318 * {@link ConstantPoolVisitor#CONSTANT_String}, or 319 * {@link ConstantPoolVisitor#CONSTANT_Class} 320 * @param value a boxed int, float, long or double; or a string or class object 321 * @throws IllegalArgumentException if the type of the constant does not 322 * match the constant pool entry type, 323 * as reported by {@link #getTag(int)} 324 * 325 * @see #putConstantValue(int, byte, Object) 326 * @see ConstantPoolVisitor#visitConstantValue(int, byte, Object) 327 * @see ConstantPoolVisitor#visitConstantString(int, byte, String, int) 328 */ 329 public void putConstantValue(int index, Object value) { 330 if (value == null) { clear(index); return; } 331 byte tag = tagForConstant(value.getClass()); 332 checkConstantTag(tag, value); 333 checkTag(index, tag); 334 patchArray[index] = value; 335 } 336 337 /** Set the entry of the constant pool indexed by index to 338 * a new value. 339 * 340 * @param index an index to a constant pool entry matching the given tag 341 * @param tag one of the following values: 342 * {@link ConstantPoolVisitor#CONSTANT_Integer}, 343 * {@link ConstantPoolVisitor#CONSTANT_Float}, 344 * {@link ConstantPoolVisitor#CONSTANT_Long}, 345 * {@link ConstantPoolVisitor#CONSTANT_Double}, 346 * {@link ConstantPoolVisitor#CONSTANT_String}, or 347 * {@link ConstantPoolVisitor#CONSTANT_Class} 348 * @param value a boxed number, string, or class object 349 * @throws IllegalArgumentException if the type of the constant does not 350 * match the constant pool entry type, or if a class name contains 351 * '/' or ';' 352 * 353 * @see #putConstantValue(int, Object) 354 * @see ConstantPoolVisitor#visitConstantValue(int, byte, Object) 355 * @see ConstantPoolVisitor#visitConstantString(int, byte, String, int) 356 */ 357 public void putConstantValue(int index, byte tag, Object value) { 358 if (value == null) { clear(index); return; } 359 checkTag(index, tag); 360 if (tag == CONSTANT_Class && value instanceof String) { 361 checkClassName((String) value); 362 } else if (tag == CONSTANT_String) { 363 // the JVM accepts any object as a patch for a string 364 } else { 365 // make sure the incoming value is the right type 366 checkConstantTag(tag, value); 367 } 368 checkTag(index, tag); 369 patchArray[index] = value; 370 } 371 372 /** Set the entry of the constant pool indexed by index to 373 * a new {@link ConstantPoolVisitor#CONSTANT_NameAndType} value. 374 * 375 * @param index an index to a constant pool entry containing a 376 * {@link ConstantPoolVisitor#CONSTANT_NameAndType} value. 377 * @param memberName a memberName 378 * @param signature a signature 379 * @throws IllegalArgumentException if memberName contains the character ';' 380 * 381 * @see ConstantPoolVisitor#visitDescriptor(int, byte, String, String, int, int) 382 */ 383 public void putDescriptor(int index, String memberName, String signature) { 384 checkTag(index, CONSTANT_NameAndType); 385 checkMemberName(memberName); 386 patchArray[index] = addSemis(memberName, signature); 387 } 388 389 /** Set the entry of the constant pool indexed by index to 390 * a new {@link ConstantPoolVisitor#CONSTANT_Fieldref}, 391 * {@link ConstantPoolVisitor#CONSTANT_Methodref}, or 392 * {@link ConstantPoolVisitor#CONSTANT_InterfaceMethodref} value. 393 * 394 * @param index an index to a constant pool entry containing a member reference 395 * @param className a class name 396 * @param memberName a field or method name 397 * @param signature a field or method signature 398 * @throws IllegalArgumentException if memberName contains the character ';' 399 * or signature is not a correct signature 400 * 401 * @see ConstantPoolVisitor#visitMemberRef(int, byte, String, String, String, int, int) 402 */ 403 public void putMemberRef(int index, byte tag, 404 String className, String memberName, String signature) { 405 checkTagMask(tag, CONSTANT_MemberRef_MASK); 406 checkTag(index, tag); 407 checkClassName(className); 408 checkMemberName(memberName); 409 if (signature.startsWith("(") == (tag == CONSTANT_Fieldref)) 410 throw new IllegalArgumentException("bad signature: "+signature); 411 patchArray[index] = addSemis(className, memberName, signature); 412 } 413 414 private static final int CONSTANT_MemberRef_MASK = 415 CONSTANT_Fieldref 416 | CONSTANT_Methodref 417 | CONSTANT_InterfaceMethodref; 418 419 private static final Map<Class<?>, Byte> CONSTANT_VALUE_CLASS_TAG 420 = new IdentityHashMap<Class<?>, Byte>(6); 421 private static final Class<?>[] CONSTANT_VALUE_CLASS = new Class<?>[16]; 422 static { 423 Object[][] values = { 424 {Integer.class, CONSTANT_Integer}, 425 {Long.class, CONSTANT_Long}, 426 {Float.class, CONSTANT_Float}, 427 {Double.class, CONSTANT_Double}, 428 {String.class, CONSTANT_String}, 429 {Class.class, CONSTANT_Class} 430 }; 431 for (Object[] value : values) { 432 Class<?> cls = (Class<?>)value[0]; 433 Byte tag = (Byte) value[1]; 434 CONSTANT_VALUE_CLASS_TAG.put(cls, tag); 435 CONSTANT_VALUE_CLASS[(byte)tag] = cls; 436 } 437 } 438 439 static Class<?> classForTag(byte tag) { 440 if ((tag & 0xFF) >= CONSTANT_VALUE_CLASS.length) 441 return null; 442 return CONSTANT_VALUE_CLASS[tag]; 443 } 444 445 static byte tagForConstant(Class<?> cls) { 446 Byte tag = CONSTANT_VALUE_CLASS_TAG.get(cls); 447 return (tag == null) ? CONSTANT_None : (byte)tag; 448 } 449 450 private static void checkClassName(String className) { 451 if (className.indexOf('/') >= 0 || className.indexOf(';') >= 0) 452 throw new IllegalArgumentException("invalid class name " + className); 453 } 454 455 static String addSemis(String name, String... names) { 456 StringBuilder buf = new StringBuilder(name.length() * 5); 457 buf.append(name); 458 for (String name2 : names) { 459 buf.append(';').append(name2); 460 } 461 String res = buf.toString(); 462 assert(stripSemis(names.length, res)[0].equals(name)); 463 assert(stripSemis(names.length, res)[1].equals(names[0])); 464 assert(names.length == 1 || 465 stripSemis(names.length, res)[2].equals(names[1])); 466 return res; 467 } 468 469 static String[] stripSemis(int count, String string) { 470 String[] res = new String[count+1]; 471 int pos = 0; 472 for (int i = 0; i < count; i++) { 473 int pos2 = string.indexOf(';', pos); 474 if (pos2 < 0) pos2 = string.length(); // yuck 475 res[i] = string.substring(pos, pos2); 476 pos = pos2; 477 } 478 res[count] = string.substring(pos); 479 return res; 480 } 481 482 public String toString() { 483 StringBuilder buf = new StringBuilder(this.getClass().getName()); 484 buf.append("{"); 485 Object[] origCP = null; 486 for (int i = 0; i < patchArray.length; i++) { 487 if (patchArray[i] == null) continue; 488 if (origCP != null) { 489 buf.append(", "); 490 } else { 491 try { 492 origCP = getOriginalCP(); 493 } catch (InvalidConstantPoolFormatException ee) { 494 origCP = new Object[0]; 495 } 496 } 497 Object orig = (i < origCP.length) ? origCP[i] : "?"; 498 buf.append(orig).append("=").append(patchArray[i]); 499 } 500 buf.append("}"); 501 return buf.toString(); 502 } 503 }