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 1808136 2017-09-12 16:51:32Z britter $
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 constantPool) throws IOException {
051        this(input.readByte() & 0xFF, -1, null, null, constantPool);
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, constantPool);
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, constantPool);
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, constantPool);
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, constantPool);
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, constantPool);
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 byteCodeOffset
097     * @param numberOfLocals NOT USED
098     * @param typesOfLocals array of {@link StackMapType}s of locals
099     * @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}