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.commons.compress.harmony.unpack200.bytecode; 018 019import java.io.DataOutputStream; 020import java.io.IOException; 021import java.util.ArrayList; 022import java.util.List; 023 024import org.apache.commons.compress.harmony.unpack200.Segment; 025 026public class CodeAttribute extends BCIRenumberedAttribute { 027 028 public List attributes = new ArrayList(); 029 // instances 030 public List byteCodeOffsets = new ArrayList(); 031 public List byteCodes = new ArrayList(); 032 public int codeLength; 033 public List exceptionTable; // of ExceptionTableEntry 034 public int maxLocals; 035 public int maxStack; 036 private static CPUTF8 attributeName; 037 038 public CodeAttribute(final int maxStack, final int maxLocals, final byte codePacked[], final Segment segment, 039 final OperandManager operandManager, final List exceptionTable) { 040 super(attributeName); 041 this.maxLocals = maxLocals; 042 this.maxStack = maxStack; 043 this.codeLength = 0; 044 this.exceptionTable = exceptionTable; 045 byteCodeOffsets.add(Integer.valueOf(0)); 046 int byteCodeIndex = 0; 047 for (int i = 0; i < codePacked.length; i++) { 048 final ByteCode byteCode = ByteCode.getByteCode(codePacked[i] & 0xff); 049 // Setting the offset must happen before extracting operands 050 // because label bytecodes need to know their offsets. 051 byteCode.setByteCodeIndex(byteCodeIndex); 052 byteCodeIndex++; 053 byteCode.extractOperands(operandManager, segment, codeLength); 054 byteCodes.add(byteCode); 055 codeLength += byteCode.getLength(); 056 final int lastBytecodePosition = ((Integer) byteCodeOffsets.get(byteCodeOffsets.size() - 1)).intValue(); 057 // This code assumes all multiple byte bytecodes are 058 // replaced by a single-byte bytecode followed by 059 // another bytecode. 060 if (byteCode.hasMultipleByteCodes()) { 061 byteCodeOffsets.add(Integer.valueOf(lastBytecodePosition + 1)); 062 byteCodeIndex++; 063 } 064 // I've already added the first element (at 0) before 065 // entering this loop, so make sure I don't add one 066 // after the last element. 067 if (i < (codePacked.length - 1)) { 068 byteCodeOffsets.add(Integer.valueOf(lastBytecodePosition + byteCode.getLength())); 069 } 070 if (byteCode.getOpcode() == 0xC4) { 071 // Special processing for wide bytecode - it knows what its 072 // instruction is from the opcode manager, so ignore the 073 // next instruction 074 i++; 075 } 076 } 077 // Now that all the bytecodes know their positions and 078 // sizes, fix up the byte code targets 079 // At this point, byteCodes may be a different size than 080 // codePacked because of wide bytecodes. 081 for (int i = 0; i < byteCodes.size(); i++) { 082 final ByteCode byteCode = (ByteCode) byteCodes.get(i); 083 byteCode.applyByteCodeTargetFixup(this); 084 } 085 } 086 087 @Override 088 protected int getLength() { 089 int attributesSize = 0; 090 for (int it = 0; it < attributes.size(); it++) { 091 final Attribute attribute = (Attribute) attributes.get(it); 092 attributesSize += attribute.getLengthIncludingHeader(); 093 } 094 return 2 + 2 + 4 + codeLength + 2 + exceptionTable.size() * (2 + 2 + 2 + 2) + 2 + attributesSize; 095 } 096 097 @Override 098 protected ClassFileEntry[] getNestedClassFileEntries() { 099 final ArrayList nestedEntries = new ArrayList(attributes.size() + byteCodes.size() + 10); 100 nestedEntries.add(getAttributeName()); 101 nestedEntries.addAll(byteCodes); 102 nestedEntries.addAll(attributes); 103 // Don't forget to add the ExceptionTable catch_types 104 for (int iter = 0; iter < exceptionTable.size(); iter++) { 105 final ExceptionTableEntry entry = (ExceptionTableEntry) exceptionTable.get(iter); 106 final CPClass catchType = entry.getCatchType(); 107 // If the catch type is null, this is a finally 108 // block. If it's not null, we need to add the 109 // CPClass to the list of nested class file entries. 110 if (catchType != null) { 111 nestedEntries.add(catchType); 112 } 113 } 114 final ClassFileEntry[] nestedEntryArray = new ClassFileEntry[nestedEntries.size()]; 115 nestedEntries.toArray(nestedEntryArray); 116 return nestedEntryArray; 117 } 118 119 @Override 120 protected void resolve(final ClassConstantPool pool) { 121 super.resolve(pool); 122 for (int it = 0; it < attributes.size(); it++) { 123 final Attribute attribute = (Attribute) attributes.get(it); 124 attribute.resolve(pool); 125 } 126 127 for (int it = 0; it < byteCodes.size(); it++) { 128 final ByteCode byteCode = (ByteCode) byteCodes.get(it); 129 byteCode.resolve(pool); 130 } 131 132 for (int it = 0; it < exceptionTable.size(); it++) { 133 final ExceptionTableEntry entry = (ExceptionTableEntry) exceptionTable.get(it); 134 entry.resolve(pool); 135 } 136 } 137 138 @Override 139 public String toString() { 140 return "Code: " + getLength() + " bytes"; 141 } 142 143 @Override 144 protected void writeBody(final DataOutputStream dos) throws IOException { 145 dos.writeShort(maxStack); 146 dos.writeShort(maxLocals); 147 148 dos.writeInt(codeLength); 149 for (int it = 0; it < byteCodes.size(); it++) { 150 final ByteCode byteCode = (ByteCode) byteCodes.get(it); 151 byteCode.write(dos); 152 } 153 154 dos.writeShort(exceptionTable.size()); 155 for (int it = 0; it < exceptionTable.size(); it++) { 156 final ExceptionTableEntry entry = (ExceptionTableEntry) exceptionTable.get(it); 157 entry.write(dos); 158 } 159 160 dos.writeShort(attributes.size()); 161 for (int it = 0; it < attributes.size(); it++) { 162 final Attribute attribute = (Attribute) attributes.get(it); 163 attribute.write(dos); 164 } 165 } 166 167 public void addAttribute(final Attribute attribute) { 168 attributes.add(attribute); 169 if (attribute instanceof LocalVariableTableAttribute) { 170 ((LocalVariableTableAttribute) attribute).setCodeLength(codeLength); 171 } 172 if (attribute instanceof LocalVariableTypeTableAttribute) { 173 ((LocalVariableTypeTableAttribute) attribute).setCodeLength(codeLength); 174 } 175 } 176 177 @Override 178 protected int[] getStartPCs() { 179 // Do nothing here as we've overriden renumber 180 return null; 181 } 182 183 @Override 184 public void renumber(final List byteCodeOffsets) { 185 for (int iter = 0; iter < exceptionTable.size(); iter++) { 186 final ExceptionTableEntry entry = (ExceptionTableEntry) exceptionTable.get(iter); 187 entry.renumber(byteCodeOffsets); 188 } 189 } 190 191 public static void setAttributeName(final CPUTF8 attributeName) { 192 CodeAttribute.attributeName = attributeName; 193 } 194}