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 */ 017 018package org.apache.bcel.classfile; 019 020import java.io.DataInput; 021import java.io.DataOutputStream; 022import java.io.IOException; 023import org.apache.bcel.Const; 024 025/** 026 * This class represents a stack map entry recording the types of 027 * local variables and the the of stack items at a given byte code offset. 028 * See CLDC specification 5.3.1.2 029 * 030 * @see StackMap 031 * @see StackMapType 032 */ 033public final class StackMapEntry implements Node, Cloneable 034{ 035 036 private int frame_type; 037 private int byte_code_offset; 038 private StackMapType[] types_of_locals; 039 private StackMapType[] types_of_stack_items; 040 private ConstantPool constant_pool; 041 042 043 /** 044 * Construct object from input stream. 045 * 046 * @param input Input stream 047 * @throws IOException 048 */ 049 StackMapEntry(final DataInput input, final ConstantPool constantPool) throws IOException { 050 this(input.readByte() & 0xFF, -1, null, null, constantPool); 051 052 if (frame_type >= Const.SAME_FRAME && frame_type <= Const.SAME_FRAME_MAX) { 053 byte_code_offset = frame_type - Const.SAME_FRAME; 054 } else if (frame_type >= Const.SAME_LOCALS_1_STACK_ITEM_FRAME && 055 frame_type <= Const.SAME_LOCALS_1_STACK_ITEM_FRAME_MAX) { 056 byte_code_offset = frame_type - Const.SAME_LOCALS_1_STACK_ITEM_FRAME; 057 types_of_stack_items = new StackMapType[1]; 058 types_of_stack_items[0] = new StackMapType(input, constantPool); 059 } else if (frame_type == Const.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED) { 060 byte_code_offset = input.readShort(); 061 types_of_stack_items = new StackMapType[1]; 062 types_of_stack_items[0] = new StackMapType(input, constantPool); 063 } else if (frame_type >= Const.CHOP_FRAME && frame_type <= Const.CHOP_FRAME_MAX) { 064 byte_code_offset = input.readShort(); 065 } else if (frame_type == Const.SAME_FRAME_EXTENDED) { 066 byte_code_offset = input.readShort(); 067 } else if (frame_type >= Const.APPEND_FRAME && frame_type <= Const.APPEND_FRAME_MAX) { 068 byte_code_offset = input.readShort(); 069 final int number_of_locals = frame_type - 251; 070 types_of_locals = new StackMapType[number_of_locals]; 071 for (int i = 0; i < number_of_locals; i++) { 072 types_of_locals[i] = new StackMapType(input, constantPool); 073 } 074 } else if (frame_type == Const.FULL_FRAME) { 075 byte_code_offset = input.readShort(); 076 final int number_of_locals = input.readShort(); 077 types_of_locals = new StackMapType[number_of_locals]; 078 for (int i = 0; i < number_of_locals; i++) { 079 types_of_locals[i] = new StackMapType(input, constantPool); 080 } 081 final int number_of_stack_items = input.readShort(); 082 types_of_stack_items = new StackMapType[number_of_stack_items]; 083 for (int i = 0; i < number_of_stack_items; i++) { 084 types_of_stack_items[i] = new StackMapType(input, constantPool); 085 } 086 } else { 087 /* Can't happen */ 088 throw new ClassFormatException ("Invalid frame type found while parsing stack map table: " + frame_type); 089 } 090 } 091 092 /** 093 * DO NOT USE 094 * 095 * @param byteCodeOffset 096 * @param numberOfLocals NOT USED 097 * @param typesOfLocals array of {@link StackMapType}s of locals 098 * @param numberOfStackItems NOT USED 099 * @param typesOfStackItems array ot {@link StackMapType}s of stack items 100 * @param constantPool the constant pool 101 * @deprecated Since 6.0, use {@link #StackMapEntry(int, int, StackMapType[], StackMapType[], ConstantPool)} 102 * instead 103 */ 104 @java.lang.Deprecated 105 public StackMapEntry(final int byteCodeOffset, final int numberOfLocals, 106 final StackMapType[] typesOfLocals, final int numberOfStackItems, 107 final StackMapType[] typesOfStackItems, final ConstantPool constantPool) { 108 this.byte_code_offset = byteCodeOffset; 109 this.types_of_locals = typesOfLocals != null ? typesOfLocals : new StackMapType[0]; 110 this.types_of_stack_items = typesOfStackItems != null ? typesOfStackItems : new StackMapType[0]; 111 this.constant_pool = constantPool; 112 } 113 114 /** 115 * Create an instance 116 * 117 * @param tag the frame_type to use 118 * @param byteCodeOffset 119 * @param typesOfLocals array of {@link StackMapType}s of locals 120 * @param typesOfStackItems array ot {@link StackMapType}s of stack items 121 * @param constantPool the constant pool 122 */ 123 public StackMapEntry(final int tag, final int byteCodeOffset, 124 final StackMapType[] typesOfLocals, 125 final StackMapType[] typesOfStackItems, final ConstantPool constantPool) { 126 this.frame_type = tag; 127 this.byte_code_offset = byteCodeOffset; 128 this.types_of_locals = typesOfLocals != null ? typesOfLocals : new StackMapType[0]; 129 this.types_of_stack_items = typesOfStackItems != null ? typesOfStackItems : new StackMapType[0]; 130 this.constant_pool = constantPool; 131 } 132 133 134 /** 135 * Dump stack map entry 136 * 137 * @param file Output file stream 138 * @throws IOException 139 */ 140 public void dump( final DataOutputStream file ) throws IOException { 141 file.write(frame_type); 142 if (frame_type >= Const.SAME_FRAME && frame_type <= Const.SAME_FRAME_MAX) { 143 // nothing to be done 144 } else if (frame_type >= Const.SAME_LOCALS_1_STACK_ITEM_FRAME && 145 frame_type <= Const.SAME_LOCALS_1_STACK_ITEM_FRAME_MAX) { 146 types_of_stack_items[0].dump(file); 147 } else if (frame_type == Const.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED) { 148 file.writeShort(byte_code_offset); 149 types_of_stack_items[0].dump(file); 150 } else if (frame_type >= Const.CHOP_FRAME && frame_type <= Const.CHOP_FRAME_MAX) { 151 file.writeShort(byte_code_offset); 152 } else if (frame_type == Const.SAME_FRAME_EXTENDED) { 153 file.writeShort(byte_code_offset); 154 } else if (frame_type >= Const.APPEND_FRAME && frame_type <= Const.APPEND_FRAME_MAX) { 155 file.writeShort(byte_code_offset); 156 for (final StackMapType type : types_of_locals) { 157 type.dump(file); 158 } 159 } else if (frame_type == Const.FULL_FRAME) { 160 file.writeShort(byte_code_offset); 161 file.writeShort(types_of_locals.length); 162 for (final StackMapType type : types_of_locals) { 163 type.dump(file); 164 } 165 file.writeShort(types_of_stack_items.length); 166 for (final StackMapType type : types_of_stack_items) { 167 type.dump(file); 168 } 169 } else { 170 /* Can't happen */ 171 throw new ClassFormatException ("Invalid Stack map table tag: " + frame_type); 172 } 173 } 174 175 176 /** 177 * @return String representation. 178 */ 179 @Override 180 public String toString() { 181 final StringBuilder buf = new StringBuilder(64); 182 buf.append("("); 183 if (frame_type >= Const.SAME_FRAME && frame_type <= Const.SAME_FRAME_MAX) { 184 buf.append("SAME"); 185 } else if (frame_type >= Const.SAME_LOCALS_1_STACK_ITEM_FRAME && 186 frame_type <= Const.SAME_LOCALS_1_STACK_ITEM_FRAME_MAX) { 187 buf.append("SAME_LOCALS_1_STACK"); 188 } else if (frame_type == Const.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED) { 189 buf.append("SAME_LOCALS_1_STACK_EXTENDED"); 190 } else if (frame_type >= Const.CHOP_FRAME && frame_type <= Const.CHOP_FRAME_MAX) { 191 buf.append("CHOP ").append(String.valueOf(251-frame_type)); 192 } else if (frame_type == Const.SAME_FRAME_EXTENDED) { 193 buf.append("SAME_EXTENDED"); 194 } else if (frame_type >= Const.APPEND_FRAME && frame_type <= Const.APPEND_FRAME_MAX) { 195 buf.append("APPEND ").append(String.valueOf(frame_type-251)); 196 } else if (frame_type == Const.FULL_FRAME) { 197 buf.append("FULL"); 198 } else { 199 buf.append("UNKNOWN (").append(frame_type).append(")"); 200 } 201 buf.append(", offset delta=").append(byte_code_offset); 202 if (types_of_locals.length > 0) { 203 buf.append(", locals={"); 204 for (int i = 0; i < types_of_locals.length; i++) { 205 buf.append(types_of_locals[i]); 206 if (i < types_of_locals.length - 1) { 207 buf.append(", "); 208 } 209 } 210 buf.append("}"); 211 } 212 if (types_of_stack_items.length > 0) { 213 buf.append(", stack items={"); 214 for (int i = 0; i < types_of_stack_items.length; i++) { 215 buf.append(types_of_stack_items[i]); 216 if (i < types_of_stack_items.length - 1) { 217 buf.append(", "); 218 } 219 } 220 buf.append("}"); 221 } 222 buf.append(")"); 223 return buf.toString(); 224 } 225 226 227 /** 228 * Calculate stack map entry size 229 * 230 */ 231 int getMapEntrySize() { 232 if (frame_type >= Const.SAME_FRAME && frame_type <= Const.SAME_FRAME_MAX) { 233 return 1; 234 } else if (frame_type >= Const.SAME_LOCALS_1_STACK_ITEM_FRAME && 235 frame_type <= Const.SAME_LOCALS_1_STACK_ITEM_FRAME_MAX) { 236 return 1 + (types_of_stack_items[0].hasIndex() ? 3 : 1); 237 } else if (frame_type == Const.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED) { 238 return 3 + (types_of_stack_items[0].hasIndex() ? 3 : 1); 239 } else if (frame_type >= Const.CHOP_FRAME && frame_type <= Const.CHOP_FRAME_MAX) { 240 return 3; 241 } else if (frame_type == Const.SAME_FRAME_EXTENDED) { 242 return 3; 243 } else if (frame_type >= Const.APPEND_FRAME && frame_type <= Const.APPEND_FRAME_MAX) { 244 int len = 3; 245 for (final StackMapType types_of_local : types_of_locals) { 246 len += types_of_local.hasIndex() ? 3 : 1; 247 } 248 return len; 249 } else if (frame_type == Const.FULL_FRAME) { 250 int len = 7; 251 for (final StackMapType types_of_local : types_of_locals) { 252 len += types_of_local.hasIndex() ? 3 : 1; 253 } 254 for (final StackMapType types_of_stack_item : types_of_stack_items) { 255 len += types_of_stack_item.hasIndex() ? 3 : 1; 256 } 257 return len; 258 } else { 259 throw new RuntimeException("Invalid StackMap frame_type: " + frame_type); 260 } 261 } 262 263 264 public void setFrameType( final int f ) { 265 if (f >= Const.SAME_FRAME && f <= Const.SAME_FRAME_MAX) { 266 byte_code_offset = f - Const.SAME_FRAME; 267 } else if (f >= Const.SAME_LOCALS_1_STACK_ITEM_FRAME && 268 f <= Const.SAME_LOCALS_1_STACK_ITEM_FRAME_MAX) { 269 byte_code_offset = f - Const.SAME_LOCALS_1_STACK_ITEM_FRAME; 270 } else if (f == Const.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED) { // CHECKSTYLE IGNORE EmptyBlock 271 } else if (f >= Const.CHOP_FRAME && f <= Const.CHOP_FRAME_MAX) { // CHECKSTYLE IGNORE EmptyBlock 272 } else if (f == Const.SAME_FRAME_EXTENDED) { // CHECKSTYLE IGNORE EmptyBlock 273 } else if (f >= Const.APPEND_FRAME && f <= Const.APPEND_FRAME_MAX) { // CHECKSTYLE IGNORE EmptyBlock 274 } else if (f == Const.FULL_FRAME) { // CHECKSTYLE IGNORE EmptyBlock 275 } else { 276 throw new RuntimeException("Invalid StackMap frame_type"); 277 } 278 frame_type = f; 279 } 280 281 282 public int getFrameType() { 283 return frame_type; 284 } 285 286 287 public void setByteCodeOffset( final int new_offset ) { 288 if (new_offset < 0 || new_offset > 32767) { 289 throw new RuntimeException("Invalid StackMap offset: " + new_offset); 290 } 291 292 if (frame_type >= Const.SAME_FRAME && 293 frame_type <= Const.SAME_FRAME_MAX) { 294 if (new_offset > Const.SAME_FRAME_MAX) { 295 frame_type = Const.SAME_FRAME_EXTENDED; 296 } else { 297 frame_type = new_offset; 298 } 299 } else if (frame_type >= Const.SAME_LOCALS_1_STACK_ITEM_FRAME && 300 frame_type <= Const.SAME_LOCALS_1_STACK_ITEM_FRAME_MAX) { 301 if (new_offset > Const.SAME_FRAME_MAX) { 302 frame_type = Const.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED; 303 } else { 304 frame_type = Const.SAME_LOCALS_1_STACK_ITEM_FRAME + new_offset; 305 } 306 } else if (frame_type == Const.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED) { // CHECKSTYLE IGNORE EmptyBlock 307 } else if (frame_type >= Const.CHOP_FRAME && 308 frame_type <= Const.CHOP_FRAME_MAX) { // CHECKSTYLE IGNORE EmptyBlock 309 } else if (frame_type == Const.SAME_FRAME_EXTENDED) { // CHECKSTYLE IGNORE EmptyBlock 310 } else if (frame_type >= Const.APPEND_FRAME && 311 frame_type <= Const.APPEND_FRAME_MAX) { // CHECKSTYLE IGNORE EmptyBlock 312 } else if (frame_type == Const.FULL_FRAME) { // CHECKSTYLE IGNORE EmptyBlock 313 } else { 314 throw new RuntimeException("Invalid StackMap frame_type: " + frame_type); 315 } 316 byte_code_offset = new_offset; 317 } 318 319 320 /** 321 * Update the distance (as an offset delta) from this StackMap 322 * entry to the next. Note that this might cause the the 323 * frame type to change. Note also that delta may be negative. 324 * 325 * @param delta offset delta 326 */ 327 public void updateByteCodeOffset(final int delta) { 328 setByteCodeOffset(byte_code_offset + delta); 329 } 330 331 332 public int getByteCodeOffset() { 333 return byte_code_offset; 334 } 335 336 337 /** 338 * 339 * @deprecated since 6.0 340 */ 341 @java.lang.Deprecated 342 public void setNumberOfLocals( final int n ) { // TODO unused 343 } 344 345 346 public int getNumberOfLocals() { 347 return types_of_locals.length; 348 } 349 350 351 public void setTypesOfLocals( final StackMapType[] types ) { 352 types_of_locals = types != null ? types : new StackMapType[0]; 353 } 354 355 356 public StackMapType[] getTypesOfLocals() { 357 return types_of_locals; 358 } 359 360 361 /** 362 * 363 * @deprecated since 6.0 364 */ 365 @java.lang.Deprecated 366 public void setNumberOfStackItems( final int n ) { // TODO unused 367 } 368 369 370 public int getNumberOfStackItems() { 371 return types_of_stack_items.length; 372 } 373 374 375 public void setTypesOfStackItems( final StackMapType[] types ) { 376 types_of_stack_items = types != null ? types : new StackMapType[0]; 377 } 378 379 380 public StackMapType[] getTypesOfStackItems() { 381 return types_of_stack_items; 382 } 383 384 385 /** 386 * @return deep copy of this object 387 */ 388 public StackMapEntry copy() { 389 StackMapEntry e; 390 try { 391 e = (StackMapEntry) clone(); 392 } catch (final CloneNotSupportedException ex) { 393 throw new Error("Clone Not Supported"); 394 } 395 396 e.types_of_locals = new StackMapType[types_of_locals.length]; 397 for (int i = 0; i < types_of_locals.length; i++) { 398 e.types_of_locals[i] = types_of_locals[i].copy(); 399 } 400 e.types_of_stack_items = new StackMapType[types_of_stack_items.length]; 401 for (int i = 0; i < types_of_stack_items.length; i++) { 402 e.types_of_stack_items[i] = types_of_stack_items[i].copy(); 403 } 404 return e; 405 } 406 407 408 /** 409 * Called by objects that are traversing the nodes of the tree implicitely 410 * defined by the contents of a Java class. I.e., the hierarchy of methods, 411 * fields, attributes, etc. spawns a tree of objects. 412 * 413 * @param v Visitor object 414 */ 415 @Override 416 public void accept( final Visitor v ) { 417 v.visitStackMapEntry(this); 418 } 419 420 421 /** 422 * @return Constant pool used by this object. 423 */ 424 public ConstantPool getConstantPool() { 425 return constant_pool; 426 } 427 428 429 /** 430 * @param constant_pool Constant pool to be used for this object. 431 */ 432 public void setConstantPool( final ConstantPool constant_pool ) { 433 this.constant_pool = constant_pool; 434 } 435}