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 java.util.Arrays;
024
025import org.apache.bcel.Const;
026
027/**
028 * This class represents a stack map attribute used for preverification of Java classes for the
029 * <a href="http://java.sun.com/j2me/"> Java 2 Micro Edition</a> (J2ME). This attribute is used by the
030 * <a href="http://java.sun.com/products/cldc/">KVM</a> and contained within the Code attribute of a method. See CLDC
031 * specification �5.3.1.2
032 *
033 * @see Code
034 * @see StackMapEntry
035 * @see StackMapType
036 */
037public final class StackMap extends Attribute {
038
039    private StackMapEntry[] table; // Table of stack map entries
040
041    /**
042     * Construct object from input stream.
043     *
044     * @param name_index Index of name
045     * @param length Content length in bytes
046     * @param input Input stream
047     * @param constant_pool Array of constants
048     * @throws IOException if an I/O error occurs.
049     */
050    StackMap(final int name_index, final int length, final DataInput input, final ConstantPool constant_pool) throws IOException {
051        this(name_index, length, (StackMapEntry[]) null, constant_pool);
052        final int map_length = input.readUnsignedShort();
053        table = new StackMapEntry[map_length];
054        for (int i = 0; i < map_length; i++) {
055            table[i] = new StackMapEntry(input, constant_pool);
056        }
057    }
058
059    /*
060     * @param name_index Index of name
061     *
062     * @param length Content length in bytes
063     *
064     * @param map Table of stack map entries
065     *
066     * @param constant_pool Array of constants
067     */
068    public StackMap(final int name_index, final int length, final StackMapEntry[] map, final ConstantPool constant_pool) {
069        super(Const.ATTR_STACK_MAP, name_index, length, constant_pool);
070        this.table = map;
071    }
072
073    /**
074     * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
075     * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
076     *
077     * @param v Visitor object
078     */
079    @Override
080    public void accept(final Visitor v) {
081        v.visitStackMap(this);
082    }
083
084    /**
085     * @return deep copy of this attribute
086     */
087    @Override
088    public Attribute copy(final ConstantPool constantPool) {
089        final StackMap c = (StackMap) clone();
090        c.table = new StackMapEntry[table.length];
091        Arrays.setAll(c.table, i -> table[i].copy());
092        c.setConstantPool(constantPool);
093        return c;
094    }
095
096    /**
097     * Dump stack map table attribute to file stream in binary format.
098     *
099     * @param file Output file stream
100     * @throws IOException if an I/O error occurs.
101     */
102    @Override
103    public void dump(final DataOutputStream file) throws IOException {
104        super.dump(file);
105        file.writeShort(table.length);
106        for (final StackMapEntry entry : table) {
107            entry.dump(file);
108        }
109    }
110
111    public int getMapLength() {
112        return table == null ? 0 : table.length;
113    }
114
115    /**
116     * @return Array of stack map entries
117     */
118    public StackMapEntry[] getStackMap() {
119        return table;
120    }
121
122    /**
123     * @param table Array of stack map entries
124     */
125    public void setStackMap(final StackMapEntry[] table) {
126        this.table = table;
127        int len = 2; // Length of 'number_of_entries' field prior to the array of stack maps
128        for (final StackMapEntry element : table) {
129            len += element.getMapEntrySize();
130        }
131        setLength(len);
132    }
133
134    /**
135     * @return String representation.
136     */
137    @Override
138    public String toString() {
139        final StringBuilder buf = new StringBuilder("StackMap(");
140        int running_offset = -1; // no +1 on first entry
141        for (int i = 0; i < table.length; i++) {
142            running_offset = table[i].getByteCodeOffset() + running_offset + 1;
143            buf.append(String.format("%n@%03d %s", running_offset, table[i]));
144            if (i < table.length - 1) {
145                buf.append(", ");
146            }
147        }
148        buf.append(')');
149        return buf.toString();
150    }
151}