1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * https://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19 package org.apache.bcel.classfile;
20
21 import java.io.DataInput;
22 import java.io.DataOutputStream;
23 import java.io.IOException;
24 import java.util.Arrays;
25
26 import org.apache.bcel.Const;
27 import org.apache.bcel.util.Args;
28
29 /**
30 * This class represents a stack map attribute used for preverification of Java classes for the
31 * <a href="https://java.sun.com/j2me/"> Java 2 Micro Edition</a> (J2ME). This attribute is used by the
32 * <a href="https://java.sun.com/products/cldc/">KVM</a> and contained within the Code attribute of a method. See CLDC
33 * specification �5.3.1.2
34 *
35 * <pre>
36 * StackMapTable_attribute {
37 * u2 attribute_name_index;
38 * u4 attribute_length;
39 * u2 number_of_entries;
40 * stack_map_frame entries[number_of_entries];
41 * }
42 * </pre>
43 *
44 * @see Code
45 * @see StackMapEntry
46 * @see StackMapType
47 */
48 public final class StackMap extends Attribute {
49
50 private StackMapEntry[] table; // Table of stack map entries
51
52 /**
53 * Constructs object from input stream.
54 *
55 * @param nameIndex Index of name
56 * @param length Content length in bytes
57 * @param dataInput Input stream
58 * @param constantPool Array of constants
59 * @throws IOException if an I/O error occurs.
60 */
61 StackMap(final int nameIndex, final int length, final DataInput dataInput, final ConstantPool constantPool) throws IOException {
62 this(nameIndex, length, (StackMapEntry[]) null, constantPool);
63 final int mapLength = dataInput.readUnsignedShort();
64 table = new StackMapEntry[mapLength];
65 for (int i = 0; i < mapLength; i++) {
66 table[i] = new StackMapEntry(dataInput, constantPool);
67 }
68 }
69
70 /*
71 * @param nameIndex Index of name
72 * @param length Content length in bytes
73 * @param map Table of stack map entries
74 * @param constantPool Array of constants
75 */
76 public StackMap(final int nameIndex, final int length, final StackMapEntry[] table, final ConstantPool constantPool) {
77 super(Const.ATTR_STACK_MAP, nameIndex, length, constantPool);
78 this.table = table != null ? table : StackMapEntry.EMPTY_ARRAY;
79 Args.requireU2(this.table.length, "table.length");
80 }
81
82 /**
83 * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
84 * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
85 *
86 * @param v Visitor object
87 */
88 @Override
89 public void accept(final Visitor v) {
90 v.visitStackMap(this);
91 }
92
93 /**
94 * @return deep copy of this attribute
95 */
96 @Override
97 public Attribute copy(final ConstantPool constantPool) {
98 final StackMap c = (StackMap) clone();
99 c.table = new StackMapEntry[table.length];
100 Arrays.setAll(c.table, i -> table[i].copy());
101 c.setConstantPool(constantPool);
102 return c;
103 }
104
105 /**
106 * Dump stack map table attribute to file stream in binary format.
107 *
108 * @param file Output file stream
109 * @throws IOException if an I/O error occurs.
110 */
111 @Override
112 public void dump(final DataOutputStream file) throws IOException {
113 super.dump(file);
114 file.writeShort(table.length);
115 for (final StackMapEntry entry : table) {
116 entry.dump(file);
117 }
118 }
119
120 public int getMapLength() {
121 return table.length;
122 }
123
124 /**
125 * @return Array of stack map entries
126 */
127 public StackMapEntry[] getStackMap() {
128 return table;
129 }
130
131 /**
132 * @param table Array of stack map entries
133 */
134 public void setStackMap(final StackMapEntry[] table) {
135 this.table = table != null ? table : StackMapEntry.EMPTY_ARRAY;
136 int len = 2; // Length of 'number_of_entries' field prior to the array of stack maps
137 for (final StackMapEntry element : this.table) {
138 len += element.getMapEntrySize();
139 }
140 setLength(len);
141 }
142
143 /**
144 * @return String representation.
145 */
146 @Override
147 public String toString() {
148 final StringBuilder buf = new StringBuilder("StackMap(");
149 int runningOffset = -1; // no +1 on first entry
150 for (int i = 0; i < table.length; i++) {
151 runningOffset = table[i].getByteCodeOffset() + runningOffset + 1;
152 buf.append(String.format("%n@%03d %s", runningOffset, table[i]));
153 if (i < table.length - 1) {
154 buf.append(", ");
155 }
156 }
157 buf.append(')');
158 return buf.toString();
159 }
160 }