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