View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   *  Unless required by applicable law or agreed to in writing, software
12   *  distributed under the License is distributed on an "AS IS" BASIS,
13   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   *  See the License for the specific language governing permissions and
15   *  limitations under the License.
16   */
17  
18  package org.apache.bcel.classfile;
19  
20  import java.io.DataInput;
21  import java.io.DataOutputStream;
22  import java.io.IOException;
23  import org.apache.bcel.Const;
24  
25  /**
26   * This class represents a stack map entry recording the types of
27   * local variables and the the of stack items at a given byte code offset.
28   * See CLDC specification 5.3.1.2
29   *
30   * @version $Id: StackMapEntry.html 1018313 2017-09-18 09:03:04Z britter $
31   * @see     StackMap
32   * @see     StackMapType
33   */
34  public final class StackMapEntry implements Node, Cloneable
35  {
36  
37      private int frame_type;
38      private int byte_code_offset;
39      private StackMapType[] types_of_locals;
40      private StackMapType[] types_of_stack_items;
41      private ConstantPool constant_pool;
42  
43  
44      /**
45       * Construct object from input stream.
46       *
47       * @param input Input stream
48       * @throws IOException
49       */
50      StackMapEntry(final DataInput input, final ConstantPool constantPool) throws IOException {
51          this(input.readByte() & 0xFF, -1, null, null, constantPool);
52  
53          if (frame_type >= Const.SAME_FRAME && frame_type <= Const.SAME_FRAME_MAX) {
54              byte_code_offset = frame_type - Const.SAME_FRAME;
55          } else if (frame_type >= Const.SAME_LOCALS_1_STACK_ITEM_FRAME &&
56                     frame_type <= Const.SAME_LOCALS_1_STACK_ITEM_FRAME_MAX) {
57              byte_code_offset = frame_type - Const.SAME_LOCALS_1_STACK_ITEM_FRAME;
58              types_of_stack_items = new StackMapType[1];
59              types_of_stack_items[0] = new StackMapType(input, constantPool);
60          } else if (frame_type == Const.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED) {
61              byte_code_offset = input.readShort();
62              types_of_stack_items = new StackMapType[1];
63              types_of_stack_items[0] = new StackMapType(input, constantPool);
64          } else if (frame_type >= Const.CHOP_FRAME && frame_type <= Const.CHOP_FRAME_MAX) {
65              byte_code_offset = input.readShort();
66          } else if (frame_type == Const.SAME_FRAME_EXTENDED) {
67              byte_code_offset = input.readShort();
68          } else if (frame_type >= Const.APPEND_FRAME && frame_type <= Const.APPEND_FRAME_MAX) {
69              byte_code_offset = input.readShort();
70              final int number_of_locals = frame_type - 251;
71              types_of_locals = new StackMapType[number_of_locals];
72              for (int i = 0; i < number_of_locals; i++) {
73                  types_of_locals[i] = new StackMapType(input, constantPool);
74              }
75          } else if (frame_type == Const.FULL_FRAME) {
76              byte_code_offset = input.readShort();
77              final int number_of_locals = input.readShort();
78              types_of_locals = new StackMapType[number_of_locals];
79              for (int i = 0; i < number_of_locals; i++) {
80                  types_of_locals[i] = new StackMapType(input, constantPool);
81              }
82              final int number_of_stack_items = input.readShort();
83              types_of_stack_items = new StackMapType[number_of_stack_items];
84              for (int i = 0; i < number_of_stack_items; i++) {
85                  types_of_stack_items[i] = new StackMapType(input, constantPool);
86              }
87          } else {
88              /* Can't happen */
89              throw new ClassFormatException ("Invalid frame type found while parsing stack map table: " + frame_type);
90          }
91      }
92  
93      /**
94       * DO NOT USE
95       *
96       * @param byteCodeOffset
97       * @param numberOfLocals NOT USED
98       * @param typesOfLocals array of {@link StackMapType}s of locals
99       * @param numberOfStackItems NOT USED
100      * @param typesOfStackItems array ot {@link StackMapType}s of stack items
101      * @param constantPool 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 byteCodeOffset, final int numberOfLocals,
107             final StackMapType[] typesOfLocals, final int numberOfStackItems,
108             final StackMapType[] typesOfStackItems, final ConstantPool constantPool) {
109         this.byte_code_offset = byteCodeOffset;
110         this.types_of_locals = typesOfLocals != null ? typesOfLocals : new StackMapType[0];
111         this.types_of_stack_items = typesOfStackItems != null ? typesOfStackItems : new StackMapType[0];
112         this.constant_pool = constantPool;
113     }
114 
115     /**
116      * Create an instance
117      *
118      * @param tag the frame_type to use
119      * @param byteCodeOffset
120      * @param typesOfLocals array of {@link StackMapType}s of locals
121      * @param typesOfStackItems array ot {@link StackMapType}s of stack items
122      * @param constantPool the constant pool
123      */
124     public StackMapEntry(final int tag, final int byteCodeOffset,
125             final StackMapType[] typesOfLocals,
126             final StackMapType[] typesOfStackItems, final ConstantPool constantPool) {
127         this.frame_type = tag;
128         this.byte_code_offset = byteCodeOffset;
129         this.types_of_locals = typesOfLocals != null ? typesOfLocals : new StackMapType[0];
130         this.types_of_stack_items = typesOfStackItems != null ? typesOfStackItems : new StackMapType[0];
131         this.constant_pool = constantPool;
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 }