001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * https://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.apache.commons.compress.harmony.unpack200.bytecode; 020 021import java.io.DataOutputStream; 022import java.io.IOException; 023import java.util.ArrayList; 024import java.util.List; 025 026import org.apache.commons.compress.harmony.pack200.Pack200Exception; 027import org.apache.commons.compress.harmony.unpack200.Segment; 028 029public class CodeAttribute extends BCIRenumberedAttribute { 030 031 private static CPUTF8 attributeName; 032 033 public static void setAttributeName(final CPUTF8 attributeName) { 034 CodeAttribute.attributeName = attributeName; 035 } 036 037 public List<Attribute> attributes = new ArrayList<>(); 038 // instances 039 public List<Integer> byteCodeOffsets = new ArrayList<>(); 040 public List<ByteCode> byteCodes = new ArrayList<>(); 041 public int codeLength; 042 public List<ExceptionTableEntry> exceptionTable; 043 public int maxLocals; 044 045 public int maxStack; 046 047 public CodeAttribute(final int maxStack, final int maxLocals, final byte[] codePacked, final Segment segment, final OperandManager operandManager, 048 final List<ExceptionTableEntry> exceptionTable) throws Pack200Exception { 049 super(attributeName); 050 this.maxLocals = maxLocals; 051 this.maxStack = maxStack; 052 this.codeLength = 0; 053 this.exceptionTable = exceptionTable; 054 byteCodeOffsets.add(Integer.valueOf(0)); 055 int byteCodeIndex = 0; 056 for (int i = 0; i < codePacked.length; i++) { 057 final ByteCode byteCode = ByteCode.getByteCode(codePacked[i] & 0xff); 058 // Setting the offset must happen before extracting operands 059 // because label bytecodes need to know their offsets. 060 byteCode.setByteCodeIndex(byteCodeIndex); 061 byteCodeIndex++; 062 byteCode.extractOperands(operandManager, segment, codeLength); 063 byteCodes.add(byteCode); 064 codeLength += byteCode.getLength(); 065 final int lastBytecodePosition = byteCodeOffsets.get(byteCodeOffsets.size() - 1).intValue(); 066 // This code assumes all multiple byte bytecodes are 067 // replaced by a single-byte bytecode followed by 068 // another bytecode. 069 if (byteCode.hasMultipleByteCodes()) { 070 byteCodeOffsets.add(Integer.valueOf(lastBytecodePosition + 1)); 071 byteCodeIndex++; 072 } 073 // I've already added the first element (at 0) before 074 // entering this loop, so make sure I don't add one 075 // after the last element. 076 if (i < codePacked.length - 1) { 077 byteCodeOffsets.add(Integer.valueOf(lastBytecodePosition + byteCode.getLength())); 078 } 079 if (byteCode.getOpcode() == 0xC4) { 080 // Special processing for wide bytecode - it knows what its 081 // instruction is from the opcode manager, so ignore the 082 // next instruction 083 i++; 084 } 085 } 086 // Now that all the bytecodes know their positions and 087 // sizes, fix up the byte code targets 088 // At this point, byteCodes may be a different size than 089 // codePacked because of wide bytecodes. 090 for (final ByteCode byteCode : byteCodes) { 091 byteCode.applyByteCodeTargetFixup(this); 092 } 093 } 094 095 public void addAttribute(final Attribute attribute) { 096 attributes.add(attribute); 097 if (attribute instanceof LocalVariableTableAttribute) { 098 ((LocalVariableTableAttribute) attribute).setCodeLength(codeLength); 099 } 100 if (attribute instanceof LocalVariableTypeTableAttribute) { 101 ((LocalVariableTypeTableAttribute) attribute).setCodeLength(codeLength); 102 } 103 } 104 105 @Override 106 protected int getLength() { 107 int attributesSize = 0; 108 for (final Attribute attribute : attributes) { 109 attributesSize += attribute.getLengthIncludingHeader(); 110 } 111 return 2 + 2 + 4 + codeLength + 2 + exceptionTable.size() * (2 + 2 + 2 + 2) + 2 + attributesSize; 112 } 113 114 @Override 115 protected ClassFileEntry[] getNestedClassFileEntries() { 116 final List<ClassFileEntry> nestedEntries = new ArrayList<>(attributes.size() + byteCodes.size() + 10); 117 nestedEntries.add(getAttributeName()); 118 nestedEntries.addAll(byteCodes); 119 nestedEntries.addAll(attributes); 120 // Don't forget to add the ExceptionTable catch_types 121 for (final ExceptionTableEntry entry : exceptionTable) { 122 final CPClass catchType = entry.getCatchType(); 123 // If the catch type is null, this is a finally 124 // block. If it's not null, we need to add the 125 // CPClass to the list of nested class file entries. 126 if (catchType != null) { 127 nestedEntries.add(catchType); 128 } 129 } 130 return nestedEntries.toArray(NONE); 131 } 132 133 @Override 134 protected int[] getStartPCs() { 135 // Do nothing here as we've overriden renumber 136 return null; 137 } 138 139 @Override 140 public void renumber(final List<Integer> byteCodeOffsets) { 141 exceptionTable.forEach(entry -> entry.renumber(byteCodeOffsets)); 142 } 143 144 @Override 145 protected void resolve(final ClassConstantPool pool) { 146 super.resolve(pool); 147 attributes.forEach(attribute -> attribute.resolve(pool)); 148 byteCodes.forEach(byteCode -> byteCode.resolve(pool)); 149 exceptionTable.forEach(byteCode -> byteCode.resolve(pool)); 150 } 151 152 @Override 153 public String toString() { 154 return "Code: " + getLength() + " bytes"; 155 } 156 157 @Override 158 protected void writeBody(final DataOutputStream dos) throws IOException { 159 dos.writeShort(maxStack); 160 dos.writeShort(maxLocals); 161 162 dos.writeInt(codeLength); 163 for (final ByteCode byteCode : byteCodes) { 164 byteCode.write(dos); 165 } 166 167 dos.writeShort(exceptionTable.size()); 168 for (final ExceptionTableEntry entry : exceptionTable) { 169 entry.write(dos); 170 } 171 172 dos.writeShort(attributes.size()); 173 for (final Attribute attribute : attributes) { 174 attribute.write(dos); 175 } 176 } 177}