1 /* 2 * Javassist, a Java-bytecode translator toolkit. 3 * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. 4 * 5 * The contents of this file are subject to the Mozilla Public License Version 6 * 1.1 (the "License"); you may not use this file except in compliance with 7 * the License. Alternatively, the contents of this file may be used under 8 * the terms of the GNU Lesser General Public License Version 2.1 or later, 9 * or the Apache License Version 2.0. 10 * 11 * Software distributed under the License is distributed on an "AS IS" basis, 12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 13 * for the specific language governing rights and limitations under the 14 * License. 15 */ 16 17 package javassist.bytecode; 18 19 import java.io.ByteArrayOutputStream; 20 import java.io.DataInputStream; 21 import java.io.IOException; 22 import java.util.Map; 23 24 import javassist.CannotCompileException; 25 26 /** 27 * Another <code>stack_map</code> attribute defined in CLDC 1.1 for J2ME. 28 * 29 * <p>This is an entry in the attributes table of a Code attribute. 30 * It was introduced by J2ME CLDC 1.1 (JSR 139) for pre-verification. 31 * 32 * <p>According to the CLDC specification, the sizes of some fields are not 16bit 33 * but 32bit if the code size is more than 64K or the number of the local variables 34 * is more than 64K. However, for the J2ME CLDC technology, they are always 16bit. 35 * The implementation of the StackMap class assumes they are 16bit. 36 * 37 * @see MethodInfo#doPreverify 38 * @see StackMapTable 39 * @since 3.12 40 */ 41 public class StackMap extends AttributeInfo { 42 /** 43 * The name of this attribute <code>"StackMap"</code>. 44 */ 45 public static final String tag = "StackMap"; 46 47 48 /** 49 * Constructs a <code>stack_map</code> attribute. 50 */ StackMap(ConstPool cp, byte[] newInfo)51 StackMap(ConstPool cp, byte[] newInfo) { 52 super(cp, tag, newInfo); 53 } 54 StackMap(ConstPool cp, int name_id, DataInputStream in)55 StackMap(ConstPool cp, int name_id, DataInputStream in) 56 throws IOException 57 { 58 super(cp, name_id, in); 59 } 60 61 /** 62 * Returns <code>number_of_entries</code>. 63 */ numOfEntries()64 public int numOfEntries() { 65 return ByteArray.readU16bit(info, 0); 66 } 67 68 /** 69 * <code>Top_variable_info.tag</code>. 70 */ 71 public static final int TOP = 0; 72 73 /** 74 * <code>Integer_variable_info.tag</code>. 75 */ 76 public static final int INTEGER = 1; 77 78 /** 79 * <code>Float_variable_info.tag</code>. 80 */ 81 public static final int FLOAT = 2; 82 83 /** 84 * <code>Double_variable_info.tag</code>. 85 */ 86 public static final int DOUBLE = 3; 87 88 /** 89 * <code>Long_variable_info.tag</code>. 90 */ 91 public static final int LONG = 4; 92 93 /** 94 * <code>Null_variable_info.tag</code>. 95 */ 96 public static final int NULL = 5; 97 98 /** 99 * <code>UninitializedThis_variable_info.tag</code>. 100 */ 101 public static final int THIS = 6; 102 103 /** 104 * <code>Object_variable_info.tag</code>. 105 */ 106 public static final int OBJECT = 7; 107 108 /** 109 * <code>Uninitialized_variable_info.tag</code>. 110 */ 111 public static final int UNINIT = 8; 112 113 /** 114 * Makes a copy. 115 */ 116 @Override copy(ConstPool newCp, Map<String,String> classnames)117 public AttributeInfo copy(ConstPool newCp, Map<String,String> classnames) { 118 Copier copier = new Copier(this, newCp, classnames); 119 copier.visit(); 120 return copier.getStackMap(); 121 } 122 123 /** 124 * A code walker for a StackMap attribute. 125 */ 126 public static class Walker { 127 byte[] info; 128 129 /** 130 * Constructs a walker. 131 */ Walker(StackMap sm)132 public Walker(StackMap sm) { 133 info = sm.get(); 134 } 135 136 /** 137 * Visits each entry of the stack map frames. 138 */ visit()139 public void visit() { 140 int num = ByteArray.readU16bit(info, 0); 141 int pos = 2; 142 for (int i = 0; i < num; i++) { 143 int offset = ByteArray.readU16bit(info, pos); 144 int numLoc = ByteArray.readU16bit(info, pos + 2); 145 pos = locals(pos + 4, offset, numLoc); 146 int numStack = ByteArray.readU16bit(info, pos); 147 pos = stack(pos + 2, offset, numStack); 148 } 149 } 150 151 /** 152 * Invoked when <code>locals</code> of <code>stack_map_frame</code> 153 * is visited. 154 */ locals(int pos, int offset, int num)155 public int locals(int pos, int offset, int num) { 156 return typeInfoArray(pos, offset, num, true); 157 } 158 159 /** 160 * Invoked when <code>stack</code> of <code>stack_map_frame</code> 161 * is visited. 162 */ stack(int pos, int offset, int num)163 public int stack(int pos, int offset, int num) { 164 return typeInfoArray(pos, offset, num, false); 165 } 166 167 /** 168 * Invoked when an array of <code>verification_type_info</code> is 169 * visited. 170 * 171 * @param num the number of elements. 172 * @param isLocals true if this array is for <code>locals</code>. 173 * false if it is for <code>stack</code>. 174 */ typeInfoArray(int pos, int offset, int num, boolean isLocals)175 public int typeInfoArray(int pos, int offset, int num, boolean isLocals) { 176 for (int k = 0; k < num; k++) 177 pos = typeInfoArray2(k, pos); 178 179 return pos; 180 } 181 typeInfoArray2(int k, int pos)182 int typeInfoArray2(int k, int pos) { 183 byte tag = info[pos]; 184 if (tag == OBJECT) { 185 int clazz = ByteArray.readU16bit(info, pos + 1); 186 objectVariable(pos, clazz); 187 pos += 3; 188 } 189 else if (tag == UNINIT) { 190 int offsetOfNew = ByteArray.readU16bit(info, pos + 1); 191 uninitialized(pos, offsetOfNew); 192 pos += 3; 193 } 194 else { 195 typeInfo(pos, tag); 196 pos++; 197 } 198 199 return pos; 200 } 201 202 /** 203 * Invoked when an element of <code>verification_type_info</code> 204 * (except <code>Object_variable_info</code> and 205 * <code>Uninitialized_variable_info</code>) is visited. 206 */ typeInfo(int pos, byte tag)207 public void typeInfo(int pos, byte tag) {} 208 209 /** 210 * Invoked when an element of type <code>Object_variable_info</code> 211 * is visited. 212 */ objectVariable(int pos, int clazz)213 public void objectVariable(int pos, int clazz) {} 214 215 /** 216 * Invoked when an element of type <code>Uninitialized_variable_info</code> 217 * is visited. 218 */ uninitialized(int pos, int offset)219 public void uninitialized(int pos, int offset) {} 220 } 221 222 static class Copier extends Walker { 223 byte[] dest; 224 ConstPool srcCp, destCp; 225 Map<String,String> classnames; 226 Copier(StackMap map, ConstPool newCp, Map<String,String> classnames)227 Copier(StackMap map, ConstPool newCp, Map<String,String> classnames) { 228 super(map); 229 srcCp = map.getConstPool(); 230 dest = new byte[info.length]; 231 destCp = newCp; 232 this.classnames = classnames; 233 } 234 @Override visit()235 public void visit() { 236 int num = ByteArray.readU16bit(info, 0); 237 ByteArray.write16bit(num, dest, 0); 238 super.visit(); 239 } 240 241 @Override locals(int pos, int offset, int num)242 public int locals(int pos, int offset, int num) { 243 ByteArray.write16bit(offset, dest, pos - 4); 244 return super.locals(pos, offset, num); 245 } 246 247 @Override typeInfoArray(int pos, int offset, int num, boolean isLocals)248 public int typeInfoArray(int pos, int offset, int num, boolean isLocals) { 249 ByteArray.write16bit(num, dest, pos - 2); 250 return super.typeInfoArray(pos, offset, num, isLocals); 251 } 252 253 @Override typeInfo(int pos, byte tag)254 public void typeInfo(int pos, byte tag) { 255 dest[pos] = tag; 256 } 257 258 @Override objectVariable(int pos, int clazz)259 public void objectVariable(int pos, int clazz) { 260 dest[pos] = OBJECT; 261 int newClazz = srcCp.copy(clazz, destCp, classnames); 262 ByteArray.write16bit(newClazz, dest, pos + 1); 263 } 264 265 @Override uninitialized(int pos, int offset)266 public void uninitialized(int pos, int offset) { 267 dest[pos] = UNINIT; 268 ByteArray.write16bit(offset, dest, pos + 1); 269 } 270 getStackMap()271 public StackMap getStackMap() { 272 return new StackMap(destCp, dest); 273 } 274 } 275 276 /** 277 * Updates this stack map table when a new local variable is inserted 278 * for a new parameter. 279 * 280 * @param index the index of the added local variable. 281 * @param tag the type tag of that local variable. 282 * It is available by <code>StackMapTable.typeTagOf(char)</code>. 283 * @param classInfo the index of the <code>CONSTANT_Class_info</code> structure 284 * in a constant pool table. This should be zero unless the tag 285 * is <code>ITEM_Object</code>. 286 * 287 * @see javassist.CtBehavior#addParameter(javassist.CtClass) 288 * @see StackMapTable#typeTagOf(char) 289 * @see ConstPool 290 */ insertLocal(int index, int tag, int classInfo)291 public void insertLocal(int index, int tag, int classInfo) 292 throws BadBytecode 293 { 294 byte[] data = new InsertLocal(this, index, tag, classInfo).doit(); 295 this.set(data); 296 } 297 298 static class SimpleCopy extends Walker { 299 Writer writer; 300 SimpleCopy(StackMap map)301 SimpleCopy(StackMap map) { 302 super(map); 303 writer = new Writer(); 304 } 305 doit()306 byte[] doit() { 307 visit(); 308 return writer.toByteArray(); 309 } 310 311 @Override visit()312 public void visit() { 313 int num = ByteArray.readU16bit(info, 0); 314 writer.write16bit(num); 315 super.visit(); 316 } 317 318 @Override locals(int pos, int offset, int num)319 public int locals(int pos, int offset, int num) { 320 writer.write16bit(offset); 321 return super.locals(pos, offset, num); 322 } 323 324 @Override typeInfoArray(int pos, int offset, int num, boolean isLocals)325 public int typeInfoArray(int pos, int offset, int num, boolean isLocals) { 326 writer.write16bit(num); 327 return super.typeInfoArray(pos, offset, num, isLocals); 328 } 329 330 @Override typeInfo(int pos, byte tag)331 public void typeInfo(int pos, byte tag) { 332 writer.writeVerifyTypeInfo(tag, 0); 333 } 334 335 @Override objectVariable(int pos, int clazz)336 public void objectVariable(int pos, int clazz) { 337 writer.writeVerifyTypeInfo(OBJECT, clazz); 338 } 339 340 @Override uninitialized(int pos, int offset)341 public void uninitialized(int pos, int offset) { 342 writer.writeVerifyTypeInfo(UNINIT, offset); 343 } 344 } 345 346 static class InsertLocal extends SimpleCopy { 347 private int varIndex; 348 private int varTag, varData; 349 InsertLocal(StackMap map, int varIndex, int varTag, int varData)350 InsertLocal(StackMap map, int varIndex, int varTag, int varData) { 351 super(map); 352 this.varIndex = varIndex; 353 this.varTag = varTag; 354 this.varData = varData; 355 } 356 357 @Override typeInfoArray(int pos, int offset, int num, boolean isLocals)358 public int typeInfoArray(int pos, int offset, int num, boolean isLocals) { 359 if (!isLocals || num < varIndex) 360 return super.typeInfoArray(pos, offset, num, isLocals); 361 362 writer.write16bit(num + 1); 363 for (int k = 0; k < num; k++) { 364 if (k == varIndex) 365 writeVarTypeInfo(); 366 367 pos = typeInfoArray2(k, pos); 368 } 369 370 if (num == varIndex) 371 writeVarTypeInfo(); 372 373 return pos; 374 } 375 writeVarTypeInfo()376 private void writeVarTypeInfo() { 377 if (varTag == OBJECT) 378 writer.writeVerifyTypeInfo(OBJECT, varData); 379 else if (varTag == UNINIT) 380 writer.writeVerifyTypeInfo(UNINIT, varData); 381 else 382 writer.writeVerifyTypeInfo(varTag, 0); 383 } 384 } 385 shiftPc(int where, int gapSize, boolean exclusive)386 void shiftPc(int where, int gapSize, boolean exclusive) 387 throws BadBytecode 388 { 389 new Shifter(this, where, gapSize, exclusive).visit(); 390 } 391 392 static class Shifter extends Walker { 393 private int where, gap; 394 private boolean exclusive; 395 Shifter(StackMap smt, int where, int gap, boolean exclusive)396 public Shifter(StackMap smt, int where, int gap, boolean exclusive) { 397 super(smt); 398 this.where = where; 399 this.gap = gap; 400 this.exclusive = exclusive; 401 } 402 403 @Override locals(int pos, int offset, int num)404 public int locals(int pos, int offset, int num) { 405 if (exclusive ? where <= offset : where < offset) 406 ByteArray.write16bit(offset + gap, info, pos - 4); 407 408 return super.locals(pos, offset, num); 409 } 410 411 @Override uninitialized(int pos, int offset)412 public void uninitialized(int pos, int offset) { 413 if (where <= offset) 414 ByteArray.write16bit(offset + gap, info, pos + 1); 415 } 416 } 417 418 /** 419 * @see CodeIterator.Switcher#adjustOffsets(int, int) 420 */ shiftForSwitch(int where, int gapSize)421 void shiftForSwitch(int where, int gapSize) throws BadBytecode { 422 new SwitchShifter(this, where, gapSize).visit(); 423 } 424 425 static class SwitchShifter extends Walker { 426 private int where, gap; 427 SwitchShifter(StackMap smt, int where, int gap)428 public SwitchShifter(StackMap smt, int where, int gap) { 429 super(smt); 430 this.where = where; 431 this.gap = gap; 432 } 433 434 @Override locals(int pos, int offset, int num)435 public int locals(int pos, int offset, int num) { 436 if (where == pos + offset) 437 ByteArray.write16bit(offset - gap, info, pos - 4); 438 else if (where == pos) 439 ByteArray.write16bit(offset + gap, info, pos - 4); 440 441 return super.locals(pos, offset, num); 442 } 443 } 444 445 /** 446 * Undocumented method. Do not use; internal-use only. 447 * 448 * <p>This method is for javassist.convert.TransformNew. 449 * It is called to update the stack map when 450 * the NEW opcode (and the following DUP) is removed. 451 * 452 * @param where the position of the removed NEW opcode. 453 */ removeNew(int where)454 public void removeNew(int where) throws CannotCompileException { 455 byte[] data = new NewRemover(this, where).doit(); 456 this.set(data); 457 } 458 459 static class NewRemover extends SimpleCopy { 460 int posOfNew; 461 NewRemover(StackMap map, int where)462 NewRemover(StackMap map, int where) { 463 super(map); 464 posOfNew = where; 465 } 466 467 @Override stack(int pos, int offset, int num)468 public int stack(int pos, int offset, int num) { 469 return stackTypeInfoArray(pos, offset, num); 470 } 471 stackTypeInfoArray(int pos, int offset, int num)472 private int stackTypeInfoArray(int pos, int offset, int num) { 473 int p = pos; 474 int count = 0; 475 for (int k = 0; k < num; k++) { 476 byte tag = info[p]; 477 if (tag == OBJECT) 478 p += 3; 479 else if (tag == UNINIT) { 480 int offsetOfNew = ByteArray.readU16bit(info, p + 1); 481 if (offsetOfNew == posOfNew) 482 count++; 483 484 p += 3; 485 } 486 else 487 p++; 488 } 489 490 writer.write16bit(num - count); 491 for (int k = 0; k < num; k++) { 492 byte tag = info[pos]; 493 if (tag == OBJECT) { 494 int clazz = ByteArray.readU16bit(info, pos + 1); 495 objectVariable(pos, clazz); 496 pos += 3; 497 } 498 else if (tag == UNINIT) { 499 int offsetOfNew = ByteArray.readU16bit(info, pos + 1); 500 if (offsetOfNew != posOfNew) 501 uninitialized(pos, offsetOfNew); 502 503 pos += 3; 504 } 505 else { 506 typeInfo(pos, tag); 507 pos++; 508 } 509 } 510 511 return pos; 512 } 513 } 514 515 /** 516 * Prints this stack map. 517 */ print(java.io.PrintWriter out)518 public void print(java.io.PrintWriter out) { 519 new Printer(this, out).print(); 520 } 521 522 static class Printer extends Walker { 523 private java.io.PrintWriter writer; 524 Printer(StackMap map, java.io.PrintWriter out)525 public Printer(StackMap map, java.io.PrintWriter out) { 526 super(map); 527 writer = out; 528 } 529 print()530 public void print() { 531 int num = ByteArray.readU16bit(info, 0); 532 writer.println(num + " entries"); 533 visit(); 534 } 535 536 @Override locals(int pos, int offset, int num)537 public int locals(int pos, int offset, int num) { 538 writer.println(" * offset " + offset); 539 return super.locals(pos, offset, num); 540 } 541 } 542 543 /** 544 * Internal use only. 545 */ 546 public static class Writer { 547 // see javassist.bytecode.stackmap.MapMaker 548 549 private ByteArrayOutputStream output; 550 551 /** 552 * Constructs a writer. 553 */ Writer()554 public Writer() { 555 output = new ByteArrayOutputStream(); 556 } 557 558 /** 559 * Converts the written data into a byte array. 560 */ toByteArray()561 public byte[] toByteArray() { 562 return output.toByteArray(); 563 } 564 565 /** 566 * Converts to a <code>StackMap</code> attribute. 567 */ toStackMap(ConstPool cp)568 public StackMap toStackMap(ConstPool cp) { 569 return new StackMap(cp, output.toByteArray()); 570 } 571 572 /** 573 * Writes a <code>union verification_type_info</code> value. 574 * 575 * @param data <code>cpool_index</code> or <code>offset</code>. 576 */ writeVerifyTypeInfo(int tag, int data)577 public void writeVerifyTypeInfo(int tag, int data) { 578 output.write(tag); 579 if (tag == StackMap.OBJECT || tag == StackMap.UNINIT) 580 write16bit(data); 581 } 582 583 /** 584 * Writes a 16bit value. 585 */ write16bit(int value)586 public void write16bit(int value) { 587 output.write((value >>> 8) & 0xff); 588 output.write(value & 0xff); 589 } 590 } 591 } 592