    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}