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