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