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     * Dumps 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     *
113     * @return the fully qualified name of the class for this StackMapType.
114     * @since 6.8.0
115     */
116    public String getClassName() {
117        return constantPool.constantToString(index, Const.CONSTANT_Class);
118    }
119
120    /**
121     * @return Constant pool used by this object.
122     */
123    public ConstantPool getConstantPool() {
124        return constantPool;
125    }
126
127    /**
128     * @return index to constant pool if type == ITEM_Object, or offset in byte code, if type == ITEM_NewObject, and -1
129     *         otherwise
130     */
131    public int getIndex() {
132        return index;
133    }
134
135    public byte getType() {
136        return type;
137    }
138
139    /**
140     * @return true, if type is either ITEM_Object or ITEM_NewObject.
141     */
142    public boolean hasIndex() {
143        return type == Const.ITEM_Object || type == Const.ITEM_NewObject;
144    }
145
146    private String printIndex() {
147        if (type == Const.ITEM_Object) {
148            if (index < 0) {
149                return ", class=<unknown>";
150            }
151            return ", class=" + getClassName();
152        }
153        if (type == Const.ITEM_NewObject) {
154            return ", offset=" + index;
155        }
156        return "";
157    }
158
159    /**
160     * @param constantPool Constant pool to be used for this object.
161     */
162    public void setConstantPool(final ConstantPool constantPool) {
163        this.constantPool = constantPool;
164    }
165
166    public void setIndex(final int index) {
167        this.index = index;
168    }
169
170    public void setType(final byte type) {
171        this.type = checkType(type);
172    }
173
174    /**
175     * @return String representation.
176     */
177    @Override
178    public String toString() {
179        return "(type=" + Const.getItemName(type) + printIndex() + ")";
180    }
181}