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}