001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *   https://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.apache.bcel.classfile;
020
021import java.io.DataInput;
022import java.io.DataOutputStream;
023import java.io.IOException;
024
025import org.apache.bcel.Const;
026
027/**
028 * This class represents the type of a local variable or item on stack used in the StackMap entries.
029 *
030 * @see StackMapEntry
031 * @see StackMap
032 * @see Const
033 */
034public final class StackMapType implements Node, Cloneable {
035
036    public static final StackMapType[] EMPTY_ARRAY = {}; // BCELifier code generator writes calls to constructor translating null to EMPTY_ARRAY
037
038    private byte type;
039    private int index = -1; // Index to CONSTANT_Class or offset
040    private ConstantPool constantPool;
041
042    /**
043     * @param type type tag as defined in the Constants interface
044     * @param index index to constant pool, or byte code offset
045     */
046    public StackMapType(final byte type, final int index, final ConstantPool constantPool) {
047        this.type = checkType(type);
048        this.index = index;
049        this.constantPool = constantPool;
050    }
051
052    /**
053     * Constructs object from file stream.
054     *
055     * @param file Input stream
056     * @throws IOException if an I/O error occurs.
057     */
058    StackMapType(final DataInput file, final ConstantPool constantPool) throws IOException {
059        this(file.readByte(), -1, constantPool);
060        if (hasIndex()) {
061            this.index = file.readUnsignedShort();
062        }
063        this.constantPool = constantPool;
064    }
065
066    /**
067     * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
068     * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
069     *
070     * @param v Visitor object
071     * @since 6.8.0
072     */
073    @Override
074    public void accept(final Visitor v) {
075        v.visitStackMapType(this);
076    }
077
078    private byte checkType(final byte type) {
079        if (type < Const.ITEM_Bogus || type > Const.ITEM_NewObject) {
080            throw new ClassFormatException("Illegal type for StackMapType: " + type);
081        }
082        return type;
083    }
084
085    /**
086     * @return deep copy of this object
087     */
088    public StackMapType copy() {
089        try {
090            return (StackMapType) clone();
091        } catch (final CloneNotSupportedException e) {
092            // TODO should this throw?
093        }
094        return null;
095    }
096
097    /**
098     * Dump type entries to file.
099     *
100     * @param file Output file stream
101     * @throws IOException if an I/O error occurs.
102     */
103    public void dump(final DataOutputStream file) throws IOException {
104        file.writeByte(type);
105        if (hasIndex()) {
106            file.writeShort(getIndex());
107        }
108    }
109
110    /**
111     * Gets the class name of this StackMapType from the constant pool at index position.
112     * @return the fully qualified name of the class for this StackMapType.
113     * @since 6.8.0
114     */
115    public String getClassName() {
116        return constantPool.constantToString(index, Const.CONSTANT_Class);
117    }
118
119    /**
120     * @return Constant pool used by this object.
121     */
122    public ConstantPool getConstantPool() {
123        return constantPool;
124    }
125
126    /**
127     * @return index to constant pool if type == ITEM_Object, or offset in byte code, if type == ITEM_NewObject, and -1
128     *         otherwise
129     */
130    public int getIndex() {
131        return index;
132    }
133
134    public byte getType() {
135        return type;
136    }
137
138    /**
139     * @return true, if type is either ITEM_Object or ITEM_NewObject
140     */
141    public boolean hasIndex() {
142        return type == Const.ITEM_Object || type == Const.ITEM_NewObject;
143    }
144
145    private String printIndex() {
146        if (type == Const.ITEM_Object) {
147            if (index < 0) {
148                return ", class=<unknown>";
149            }
150            return ", class=" + getClassName();
151        }
152        if (type == Const.ITEM_NewObject) {
153            return ", offset=" + index;
154        }
155        return "";
156    }
157
158    /**
159     * @param constantPool Constant pool to be used for this object.
160     */
161    public void setConstantPool(final ConstantPool constantPool) {
162        this.constantPool = constantPool;
163    }
164
165    public void setIndex(final int index) {
166        this.index = index;
167    }
168
169    public void setType(final byte type) {
170        this.type = checkType(type);
171    }
172
173    /**
174     * @return String representation
175     */
176    @Override
177    public String toString() {
178        return "(type=" + Const.getItemName(type) + printIndex() + ")";
179    }
180}