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}