ByteCode.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 org.apache.commons.compress.harmony.unpack200.Segment;
  21. import org.apache.commons.compress.harmony.unpack200.bytecode.forms.ByteCodeForm;

  22. /**
  23.  * A bytecode class file entry.
  24.  */
  25. public class ByteCode extends ClassFileEntry {

  26.     private static ByteCode[] noArgByteCodes = new ByteCode[255];

  27.     public static ByteCode getByteCode(final int opcode) {
  28.         final int byteOpcode = 0xFF & opcode;
  29.         if (ByteCodeForm.get(byteOpcode).hasNoOperand()) {
  30.             if (null == noArgByteCodes[byteOpcode]) {
  31.                 noArgByteCodes[byteOpcode] = new ByteCode(byteOpcode);
  32.             }
  33.             return noArgByteCodes[byteOpcode];
  34.         }
  35.         return new ByteCode(byteOpcode);
  36.     }

  37.     private final ByteCodeForm byteCodeForm;

  38.     private ClassFileEntry[] nested;
  39.     private int[][] nestedPositions;
  40.     private int[] rewrite;

  41.     private int byteCodeOffset = -1;
  42.     private int[] byteCodeTargets;

  43.     protected ByteCode(final int opcode) {
  44.         this(opcode, NONE);
  45.     }

  46.     protected ByteCode(final int opcode, final ClassFileEntry[] nested) {
  47.         this.byteCodeForm = ByteCodeForm.get(opcode);
  48.         this.rewrite = byteCodeForm.getRewriteCopy();
  49.         this.nested = nested;
  50.     }

  51.     /**
  52.      * Some ByteCodes (in particular, those with labels need to be fixed up after all the bytecodes in the CodeAttribute have been added. (This can't be done
  53.      * beforehand because the CodeAttribute needs to be complete before targets can be assigned.)
  54.      *
  55.      * @param codeAttribute the code attribute
  56.      */
  57.     public void applyByteCodeTargetFixup(final CodeAttribute codeAttribute) {
  58.         getByteCodeForm().fixUpByteCodeTargets(this, codeAttribute);
  59.     }

  60.     @Override
  61.     protected void doWrite(final DataOutputStream dos) throws IOException {
  62.         for (final int element : rewrite) {
  63.             dos.writeByte(element);
  64.         }
  65.     }

  66.     @Override
  67.     public boolean equals(final Object obj) {
  68.         return this == obj;
  69.     }

  70.     public void extractOperands(final OperandManager operandManager, final Segment segment, final int codeLength) {
  71.         // Given an OperandTable, figure out which operands
  72.         // the receiver needs and stuff them in operands.
  73.         // Later on the operands can be rewritten (But that's
  74.         // later, not now).
  75.         final ByteCodeForm currentByteCodeForm = getByteCodeForm();
  76.         currentByteCodeForm.setByteCodeOperands(this, operandManager, codeLength);
  77.     }

  78.     protected ByteCodeForm getByteCodeForm() {
  79.         return byteCodeForm;
  80.     }

  81.     public int getByteCodeIndex() {
  82.         return byteCodeOffset;
  83.     }

  84.     public int[] getByteCodeTargets() {
  85.         return byteCodeTargets;
  86.     }

  87.     public int getLength() {
  88.         return rewrite.length;
  89.     }

  90.     public String getName() {
  91.         return getByteCodeForm().getName();
  92.     }

  93.     @Override
  94.     public ClassFileEntry[] getNestedClassFileEntries() {
  95.         return nested;
  96.     }

  97.     public int[] getNestedPosition(final int index) {
  98.         return getNestedPositions()[index];
  99.     }

  100.     public int[][] getNestedPositions() {
  101.         return nestedPositions;
  102.     }

  103.     public int getOpcode() {
  104.         return getByteCodeForm().getOpcode();
  105.     }

  106.     /**
  107.      * Some bytecodes (the ones with variable lengths) can't have a static rewrite array - they need the ability to update the array. This method permits their
  108.      * associated bytecode formst to query their rewrite array.
  109.      *
  110.      * Note that this should not be called from bytecodes which have a static rewrite; use the table in ByteCodeForm instead to specify those rewrites.
  111.      *
  112.      * @return Some bytecodes.
  113.      */
  114.     public int[] getRewrite() {
  115.         return rewrite;
  116.     }

  117.     @Override
  118.     public int hashCode() {
  119.         return objectHashCode();
  120.     }

  121.     /**
  122.      * This method will answer true if the receiver is a multi-bytecode instruction (such as aload0_putfield_super); otherwise, it will answer false.
  123.      *
  124.      * @return boolean true if multibytecode, false otherwise
  125.      */
  126.     public boolean hasMultipleByteCodes() {
  127.         return getByteCodeForm().hasMultipleByteCodes();
  128.     }

  129.     public boolean nestedMustStartClassPool() {
  130.         return byteCodeForm.nestedMustStartClassPool();
  131.     }

  132.     /*
  133.      * (non-Javadoc)
  134.      *
  135.      * @see org.apache.commons.compress.harmony.unpack200.bytecode.ClassFileEntry#resolve(org.apache.commons.compress.harmony
  136.      * .unpack200.bytecode.ClassConstantPool)
  137.      */
  138.     @Override
  139.     protected void resolve(final ClassConstantPool pool) {
  140.         super.resolve(pool);
  141.         if (nested.length > 0) {
  142.             // Update the bytecode rewrite array so that it points
  143.             // to the elements of the nested array.
  144.             for (int index = 0; index < nested.length; index++) {
  145.                 final int argLength = getNestedPosition(index)[1];
  146.                 switch (argLength) {

  147.                 case 1:
  148.                     setOperandByte(pool.indexOf(nested[index]), getNestedPosition(index)[0]);
  149.                     break;

  150.                 case 2:
  151.                     setOperand2Bytes(pool.indexOf(nested[index]), getNestedPosition(index)[0]);
  152.                     break;

  153.                 default:
  154.                     throw new Error("Unhandled resolve " + this);
  155.                 }
  156.             }
  157.         }
  158.     }

  159.     /**
  160.      * ByteCodes may need to know their position in the code array (in particular, label byte codes need to know where they are in order to calculate their
  161.      * targets). This method lets the CodeAttribute specify where the byte code is.
  162.      *
  163.      * Since there are no aload0+label instructions, this method doesn't worry about multioperation bytecodes.
  164.      *
  165.      * @param byteCodeOffset int position in code array.
  166.      */
  167.     public void setByteCodeIndex(final int byteCodeOffset) {
  168.         this.byteCodeOffset = byteCodeOffset;
  169.     }

  170.     /**
  171.      * Some ByteCodes (in particular, those with labels) have to keep track of byteCodeTargets. These are initially offsets in the CodeAttribute array relative
  172.      * to the byteCodeOffset, but later get fixed up to point to the absolute position in the CodeAttribute array. This method sets the targets.
  173.      *
  174.      * @param byteCodeTargets int index in array
  175.      */
  176.     public void setByteCodeTargets(final int[] byteCodeTargets) {
  177.         this.byteCodeTargets = byteCodeTargets;
  178.     }

  179.     public void setNested(final ClassFileEntry[] nested) {
  180.         this.nested = nested;
  181.     }

  182.     /**
  183.      * nestedPositions is an array of arrays of ints. Each subarray specifies a position of a nested element (from the nested[] array) and the length of that
  184.      * element.
  185.      *
  186.      * For instance, one might have a nested of: {CPClass java/lang/Foo, CPFloat 3.14} The nestedPositions would then be: {{0,2},{2,2}} In other words, when the
  187.      * bytecode is resolved, the CPClass will be resolved to an int and inserted at position 0 and 1 of the rewrite arguments (the first occurrences of -1). The
  188.      * CPFloat will be resolved to an int position and inserted at positions 2 and 3 of the rewrite arguments.
  189.      *
  190.      * @param nestedPositions Each subarray specifies a position of a nested element (from the nested[] array) and the length of that element.
  191.      */
  192.     public void setNestedPositions(final int[][] nestedPositions) {
  193.         this.nestedPositions = nestedPositions;
  194.     }

  195.     /**
  196.      * Given an int operand, set the rewrite bytes for that position and the one immediately following it to a high-byte, low-byte encoding of the operand.
  197.      *
  198.      * @param operand  int to set the rewrite bytes to
  199.      * @param position int position in the operands of the rewrite bytes. For a rewrite array of {100, -1, -1, -1} position 0 is the first -1, position 1 is the
  200.      *                 second -1, etc.
  201.      */
  202.     public void setOperand2Bytes(final int operand, final int position) {
  203.         final int firstOperandIndex = getByteCodeForm().firstOperandIndex();
  204.         final int byteCodeFormLength = getByteCodeForm().getRewrite().length;
  205.         if (firstOperandIndex < 1) {
  206.             // No operand rewriting permitted for this bytecode
  207.             throw new Error("Trying to rewrite " + this + " that has no rewrite");
  208.         }

  209.         if (firstOperandIndex + position + 1 > byteCodeFormLength) {
  210.             throw new Error("Trying to rewrite " + this + " with an int at position " + position + " but this won't fit in the rewrite array");
  211.         }

  212.         rewrite[firstOperandIndex + position] = (operand & 0xFF00) >> 8;
  213.         rewrite[firstOperandIndex + position + 1] = operand & 0xFF;
  214.     }

  215.     /**
  216.      * Given an int operand, treat it as a byte and set the rewrite byte for that position to that value. Mask of anything beyond 0xFF.
  217.      *
  218.      * @param operand  int to set the rewrite byte to (unsigned)
  219.      * @param position int position in the operands of the rewrite bytes. For a rewrite array of {100, -1, -1, -1} position 0 is the first -1, position 1 is the
  220.      *                 second -1, etc.
  221.      */
  222.     public void setOperandByte(final int operand, final int position) {
  223.         final int firstOperandIndex = getByteCodeForm().firstOperandIndex();
  224.         final int byteCodeFormLength = getByteCodeForm().operandLength();
  225.         if (firstOperandIndex < 1) {
  226.             // No operand rewriting permitted for this bytecode
  227.             throw new Error("Trying to rewrite " + this + " that has no rewrite");
  228.         }

  229.         if (firstOperandIndex + position > byteCodeFormLength) {
  230.             throw new Error("Trying to rewrite " + this + " with an byte at position " + position + " but this won't fit in the rewrite array");
  231.         }

  232.         rewrite[firstOperandIndex + position] = operand & 0xFF;
  233.     }

  234.     /**
  235.      * Given an array of ints which correspond to bytes in the operand of the bytecode, set the rewrite bytes of the operand to be the appropriate values. All
  236.      * values in operands[] will be masked with 0xFF so they fit into a byte.
  237.      *
  238.      * @param operands int[] rewrite operand bytes
  239.      */
  240.     public void setOperandBytes(final int[] operands) {
  241.         final int firstOperandIndex = getByteCodeForm().firstOperandIndex();
  242.         final int byteCodeFormLength = getByteCodeForm().operandLength();
  243.         if (firstOperandIndex < 1) {
  244.             // No operand rewriting permitted for this bytecode
  245.             throw new Error("Trying to rewrite " + this + " that has no rewrite");
  246.         }

  247.         if (byteCodeFormLength != operands.length) {
  248.             throw new Error("Trying to rewrite " + this + " with " + operands.length + " but bytecode has length " + byteCodeForm.operandLength());
  249.         }

  250.         for (int index = 0; index < byteCodeFormLength; index++) {
  251.             rewrite[index + firstOperandIndex] = operands[index] & 0xFF;
  252.         }
  253.     }

  254.     /**
  255.      * This is just like setOperandInt, but takes special care when the operand is less than 0 to make sure it's written correctly.
  256.      *
  257.      * @param operand  int to set the rewrite bytes to
  258.      * @param position int position of the operands in the rewrite bytes
  259.      */
  260.     public void setOperandSigned2Bytes(final int operand, final int position) {
  261.         if (operand >= 0) {
  262.             setOperand2Bytes(operand, position);
  263.         } else {
  264.             final int twosComplementOperand = 0x10000 + operand;
  265.             setOperand2Bytes(twosComplementOperand, position);
  266.         }
  267.     }

  268.     /**
  269.      * Some bytecodes (the ones with variable lengths) can't have a static rewrite array - they need the ability to update the array. This method permits that.
  270.      *
  271.      * Note that this should not be called from bytecodes which have a static rewrite; use the table in ByteCodeForm instead to specify those rewrites.
  272.      *
  273.      * @param rewrite Some bytecodes.
  274.      */
  275.     public void setRewrite(final int[] rewrite) {
  276.         this.rewrite = rewrite;
  277.     }

  278.     @Override
  279.     public String toString() {
  280.         return getByteCodeForm().getName();
  281.     }
  282. }