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