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