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