    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}