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 Thrown 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 * Constructs a StackMap.
72 *
73 * @param nameIndex Index of name.
74 * @param length Content length in bytes.
75 * @param table Table of stack map entries.
76 * @param constantPool Array of constants.
77 */
78 public StackMap(final int nameIndex, final int length, final StackMapEntry[] table, final ConstantPool constantPool) {
79 super(Const.ATTR_STACK_MAP, nameIndex, length, constantPool);
80 this.table = table != null ? table : StackMapEntry.EMPTY_ARRAY;
81 Args.requireU2(this.table.length, "table.length");
82 }
83
84 /**
85 * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
86 * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
87 *
88 * @param v Visitor object.
89 */
90 @Override
91 public void accept(final Visitor v) {
92 v.visitStackMap(this);
93 }
94
95 /**
96 * @return deep copy of this attribute.
97 */
98 @Override
99 public Attribute copy(final ConstantPool constantPool) {
100 final StackMap c = (StackMap) clone();
101 c.table = new StackMapEntry[table.length];
102 Arrays.setAll(c.table, i -> table[i].copy());
103 c.setConstantPool(constantPool);
104 return c;
105 }
106
107 /**
108 * Dumps stack map table attribute to file stream in binary format.
109 *
110 * @param file Output file stream.
111 * @throws IOException Thrown if an I/O error occurs.
112 */
113 @Override
114 public void dump(final DataOutputStream file) throws IOException {
115 super.dump(file);
116 file.writeShort(Args.requireU2(table.length, "table.length"));
117 for (final StackMapEntry entry : table) {
118 entry.dump(file);
119 }
120 }
121
122 /**
123 * Gets the map length.
124 *
125 * @return The map length.
126 */
127 public int getMapLength() {
128 return table.length;
129 }
130
131 /**
132 * Gets the stack map.
133 *
134 * @return Array of stack map entries.
135 */
136 public StackMapEntry[] getStackMap() {
137 return table;
138 }
139
140 /**
141 * Sets the stack map.
142 *
143 * @param table Array of stack map entries.
144 */
145 public void setStackMap(final StackMapEntry[] table) {
146 this.table = table != null ? table : StackMapEntry.EMPTY_ARRAY;
147 int len = 2; // Length of 'number_of_entries' field prior to the array of stack maps
148 for (final StackMapEntry element : this.table) {
149 len += element.getMapEntrySize();
150 }
151 setLength(len);
152 }
153
154 /**
155 * @return String representation.
156 */
157 @Override
158 public String toString() {
159 final StringBuilder buf = new StringBuilder("StackMap(");
160 int runningOffset = -1; // no +1 on first entry
161 for (int i = 0; i < table.length; i++) {
162 runningOffset = table[i].getByteCodeOffset() + runningOffset + 1;
163 buf.append(String.format("%n@%03d %s", runningOffset, table[i]));
164 if (i < table.length - 1) {
165 buf.append(", ");
166 }
167 }
168 buf.append(')');
169 return buf.toString();
170 }
171 }