CodeAttribute.java

  1. /*
  2.  *  Licensed to the Apache Software Foundation (ASF) under one or more
  3.  *  contributor license agreements.  See the NOTICE file distributed with
  4.  *  this work for additional information regarding copyright ownership.
  5.  *  The ASF licenses this file to You under the Apache License, Version 2.0
  6.  *  (the "License"); you may not use this file except in compliance with
  7.  *  the License.  You may obtain a copy of the License at
  8.  *
  9.  *     http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  *  Unless required by applicable law or agreed to in writing, software
  12.  *  distributed under the License is distributed on an "AS IS" BASIS,
  13.  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  *  See the License for the specific language governing permissions and
  15.  *  limitations under the License.
  16.  */
  17. package org.apache.commons.compress.harmony.unpack200.bytecode;

  18. import java.io.DataOutputStream;
  19. import java.io.IOException;
  20. import java.util.ArrayList;
  21. import java.util.List;

  22. import org.apache.commons.compress.harmony.unpack200.Segment;

  23. public class CodeAttribute extends BCIRenumberedAttribute {

  24.     private static CPUTF8 attributeName;

  25.     public static void setAttributeName(final CPUTF8 attributeName) {
  26.         CodeAttribute.attributeName = attributeName;
  27.     }

  28.     public List<Attribute> attributes = new ArrayList<>();
  29.     // instances
  30.     public List<Integer> byteCodeOffsets = new ArrayList<>();
  31.     public List<ByteCode> byteCodes = new ArrayList<>();
  32.     public int codeLength;
  33.     public List<ExceptionTableEntry> exceptionTable;
  34.     public int maxLocals;

  35.     public int maxStack;

  36.     public CodeAttribute(final int maxStack, final int maxLocals, final byte[] codePacked, final Segment segment, final OperandManager operandManager,
  37.             final List<ExceptionTableEntry> exceptionTable) {
  38.         super(attributeName);
  39.         this.maxLocals = maxLocals;
  40.         this.maxStack = maxStack;
  41.         this.codeLength = 0;
  42.         this.exceptionTable = exceptionTable;
  43.         byteCodeOffsets.add(Integer.valueOf(0));
  44.         int byteCodeIndex = 0;
  45.         for (int i = 0; i < codePacked.length; i++) {
  46.             final ByteCode byteCode = ByteCode.getByteCode(codePacked[i] & 0xff);
  47.             // Setting the offset must happen before extracting operands
  48.             // because label bytecodes need to know their offsets.
  49.             byteCode.setByteCodeIndex(byteCodeIndex);
  50.             byteCodeIndex++;
  51.             byteCode.extractOperands(operandManager, segment, codeLength);
  52.             byteCodes.add(byteCode);
  53.             codeLength += byteCode.getLength();
  54.             final int lastBytecodePosition = byteCodeOffsets.get(byteCodeOffsets.size() - 1).intValue();
  55.             // This code assumes all multiple byte bytecodes are
  56.             // replaced by a single-byte bytecode followed by
  57.             // another bytecode.
  58.             if (byteCode.hasMultipleByteCodes()) {
  59.                 byteCodeOffsets.add(Integer.valueOf(lastBytecodePosition + 1));
  60.                 byteCodeIndex++;
  61.             }
  62.             // I've already added the first element (at 0) before
  63.             // entering this loop, so make sure I don't add one
  64.             // after the last element.
  65.             if (i < codePacked.length - 1) {
  66.                 byteCodeOffsets.add(Integer.valueOf(lastBytecodePosition + byteCode.getLength()));
  67.             }
  68.             if (byteCode.getOpcode() == 0xC4) {
  69.                 // Special processing for wide bytecode - it knows what its
  70.                 // instruction is from the opcode manager, so ignore the
  71.                 // next instruction
  72.                 i++;
  73.             }
  74.         }
  75.         // Now that all the bytecodes know their positions and
  76.         // sizes, fix up the byte code targets
  77.         // At this point, byteCodes may be a different size than
  78.         // codePacked because of wide bytecodes.
  79.         for (final ByteCode byteCode : byteCodes) {
  80.             byteCode.applyByteCodeTargetFixup(this);
  81.         }
  82.     }

  83.     public void addAttribute(final Attribute attribute) {
  84.         attributes.add(attribute);
  85.         if (attribute instanceof LocalVariableTableAttribute) {
  86.             ((LocalVariableTableAttribute) attribute).setCodeLength(codeLength);
  87.         }
  88.         if (attribute instanceof LocalVariableTypeTableAttribute) {
  89.             ((LocalVariableTypeTableAttribute) attribute).setCodeLength(codeLength);
  90.         }
  91.     }

  92.     @Override
  93.     protected int getLength() {
  94.         int attributesSize = 0;
  95.         for (final Attribute attribute : attributes) {
  96.             attributesSize += attribute.getLengthIncludingHeader();
  97.         }
  98.         return 2 + 2 + 4 + codeLength + 2 + exceptionTable.size() * (2 + 2 + 2 + 2) + 2 + attributesSize;
  99.     }

  100.     @Override
  101.     protected ClassFileEntry[] getNestedClassFileEntries() {
  102.         final List<ClassFileEntry> nestedEntries = new ArrayList<>(attributes.size() + byteCodes.size() + 10);
  103.         nestedEntries.add(getAttributeName());
  104.         nestedEntries.addAll(byteCodes);
  105.         nestedEntries.addAll(attributes);
  106.         // Don't forget to add the ExceptionTable catch_types
  107.         for (final ExceptionTableEntry entry : exceptionTable) {
  108.             final CPClass catchType = entry.getCatchType();
  109.             // If the catch type is null, this is a finally
  110.             // block. If it's not null, we need to add the
  111.             // CPClass to the list of nested class file entries.
  112.             if (catchType != null) {
  113.                 nestedEntries.add(catchType);
  114.             }
  115.         }
  116.         return nestedEntries.toArray(NONE);
  117.     }

  118.     @Override
  119.     protected int[] getStartPCs() {
  120.         // Do nothing here as we've overriden renumber
  121.         return null;
  122.     }

  123.     @Override
  124.     public void renumber(final List<Integer> byteCodeOffsets) {
  125.         exceptionTable.forEach(entry -> entry.renumber(byteCodeOffsets));
  126.     }

  127.     @Override
  128.     protected void resolve(final ClassConstantPool pool) {
  129.         super.resolve(pool);
  130.         attributes.forEach(attribute -> attribute.resolve(pool));
  131.         byteCodes.forEach(byteCode -> byteCode.resolve(pool));
  132.         exceptionTable.forEach(byteCode -> byteCode.resolve(pool));
  133.     }

  134.     @Override
  135.     public String toString() {
  136.         return "Code: " + getLength() + " bytes";
  137.     }

  138.     @Override
  139.     protected void writeBody(final DataOutputStream dos) throws IOException {
  140.         dos.writeShort(maxStack);
  141.         dos.writeShort(maxLocals);

  142.         dos.writeInt(codeLength);
  143.         for (final ByteCode byteCode : byteCodes) {
  144.             byteCode.write(dos);
  145.         }

  146.         dos.writeShort(exceptionTable.size());
  147.         for (final ExceptionTableEntry entry : exceptionTable) {
  148.             entry.write(dos);
  149.         }

  150.         dos.writeShort(attributes.size());
  151.         for (final Attribute attribute : attributes) {
  152.             attribute.write(dos);
  153.         }
  154.     }
  155. }