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<Attribute> attributes = new ArrayList<>(); 029 // instances 030 public List<Integer> byteCodeOffsets = new ArrayList<>(); 031 public List<ByteCode> byteCodes = new ArrayList<>(); 032 public int codeLength; 033 public List<ExceptionTableEntry> exceptionTable; 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<ExceptionTableEntry> 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 = 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 (ByteCode byteCode : byteCodes) { 082 byteCode.applyByteCodeTargetFixup(this); 083 } 084 } 085 086 @Override 087 protected int getLength() { 088 int attributesSize = 0; 089 for (Attribute attribute : attributes) { 090 attributesSize += attribute.getLengthIncludingHeader(); 091 } 092 return 2 + 2 + 4 + codeLength + 2 + exceptionTable.size() * (2 + 2 + 2 + 2) + 2 + attributesSize; 093 } 094 095 @Override 096 protected ClassFileEntry[] getNestedClassFileEntries() { 097 final List<ClassFileEntry> nestedEntries = new ArrayList<>(attributes.size() + byteCodes.size() + 10); 098 nestedEntries.add(getAttributeName()); 099 nestedEntries.addAll(byteCodes); 100 nestedEntries.addAll(attributes); 101 // Don't forget to add the ExceptionTable catch_types 102 for (ExceptionTableEntry entry : exceptionTable) { 103 final CPClass catchType = entry.getCatchType(); 104 // If the catch type is null, this is a finally 105 // block. If it's not null, we need to add the 106 // CPClass to the list of nested class file entries. 107 if (catchType != null) { 108 nestedEntries.add(catchType); 109 } 110 } 111 return nestedEntries.toArray(ClassFileEntry.NONE); 112 } 113 114 @Override 115 protected void resolve(final ClassConstantPool pool) { 116 super.resolve(pool); 117 attributes.forEach(attribute -> attribute.resolve(pool)); 118 byteCodes.forEach(byteCode -> byteCode.resolve(pool)); 119 exceptionTable.forEach(byteCode -> byteCode.resolve(pool)); 120 } 121 122 @Override 123 public String toString() { 124 return "Code: " + getLength() + " bytes"; 125 } 126 127 @Override 128 protected void writeBody(final DataOutputStream dos) throws IOException { 129 dos.writeShort(maxStack); 130 dos.writeShort(maxLocals); 131 132 dos.writeInt(codeLength); 133 for (ByteCode byteCode : byteCodes) { 134 byteCode.write(dos); 135 } 136 137 dos.writeShort(exceptionTable.size()); 138 for (ExceptionTableEntry entry : exceptionTable) { 139 entry.write(dos); 140 } 141 142 dos.writeShort(attributes.size()); 143 for (Attribute attribute : attributes) { 144 attribute.write(dos); 145 } 146 } 147 148 public void addAttribute(final Attribute attribute) { 149 attributes.add(attribute); 150 if (attribute instanceof LocalVariableTableAttribute) { 151 ((LocalVariableTableAttribute) attribute).setCodeLength(codeLength); 152 } 153 if (attribute instanceof LocalVariableTypeTableAttribute) { 154 ((LocalVariableTypeTableAttribute) attribute).setCodeLength(codeLength); 155 } 156 } 157 158 @Override 159 protected int[] getStartPCs() { 160 // Do nothing here as we've overriden renumber 161 return null; 162 } 163 164 @Override 165 public void renumber(final List<Integer> byteCodeOffsets) { 166 exceptionTable.forEach(entry -> entry.renumber(byteCodeOffsets)); 167 } 168 169 public static void setAttributeName(final CPUTF8 attributeName) { 170 CodeAttribute.attributeName = attributeName; 171 } 172}