ByteCode.java

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

  20. import java.io.DataOutputStream;
  21. import java.io.IOException;

  22. import org.apache.commons.compress.harmony.pack200.Pack200Exception;
  23. import org.apache.commons.compress.harmony.unpack200.Segment;
  24. import org.apache.commons.compress.harmony.unpack200.bytecode.forms.ByteCodeForm;

  25. /**
  26.  * A bytecode class file entry.
  27.  */
  28. public class ByteCode extends ClassFileEntry {

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

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

  40.     private final ByteCodeForm byteCodeForm;

  41.     private ClassFileEntry[] nested;
  42.     private int[][] nestedPositions;
  43.     private int[] rewrite;

  44.     private int byteCodeOffset = -1;
  45.     private int[] byteCodeTargets;

  46.     protected ByteCode(final int opcode) {
  47.         this(opcode, NONE);
  48.     }

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

  54.     /**
  55.      * 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
  56.      * beforehand because the CodeAttribute needs to be complete before targets can be assigned.)
  57.      *
  58.      * @param codeAttribute the code attribute
  59.      */
  60.     public void applyByteCodeTargetFixup(final CodeAttribute codeAttribute) {
  61.         getByteCodeForm().fixUpByteCodeTargets(this, codeAttribute);
  62.     }

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

  69.     @Override
  70.     public boolean equals(final Object obj) {
  71.         return this == obj;
  72.     }

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

  80.     protected ByteCodeForm getByteCodeForm() {
  81.         return byteCodeForm;
  82.     }

  83.     public int getByteCodeIndex() {
  84.         return byteCodeOffset;
  85.     }

  86.     public int[] getByteCodeTargets() {
  87.         return byteCodeTargets;
  88.     }

  89.     public int getLength() {
  90.         return rewrite.length;
  91.     }

  92.     public String getName() {
  93.         return getByteCodeForm().getName();
  94.     }

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

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

  102.     public int[][] getNestedPositions() {
  103.         return nestedPositions;
  104.     }

  105.     public int getOpcode() {
  106.         return getByteCodeForm().getOpcode();
  107.     }

  108.     /**
  109.      * 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
  110.      * associated bytecode formst to query their rewrite array.
  111.      *
  112.      * Note that this should not be called from bytecodes which have a static rewrite; use the table in ByteCodeForm instead to specify those rewrites.
  113.      *
  114.      * @return Some bytecodes.
  115.      */
  116.     public int[] getRewrite() {
  117.         return rewrite;
  118.     }

  119.     @Override
  120.     public int hashCode() {
  121.         return objectHashCode();
  122.     }

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

  131.     public boolean nestedMustStartClassPool() {
  132.         return byteCodeForm.nestedMustStartClassPool();
  133.     }

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

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

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

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

  161.     /**
  162.      * 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
  163.      * targets). This method lets the CodeAttribute specify where the byte code is.
  164.      *
  165.      * Since there are no aload0+label instructions, this method doesn't worry about multioperation bytecodes.
  166.      *
  167.      * @param byteCodeOffset int position in code array.
  168.      */
  169.     public void setByteCodeIndex(final int byteCodeOffset) {
  170.         this.byteCodeOffset = byteCodeOffset;
  171.     }

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

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

  184.     /**
  185.      * 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
  186.      * element.
  187.      *
  188.      * 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
  189.      * 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
  190.      * CPFloat will be resolved to an int position and inserted at positions 2 and 3 of the rewrite arguments.
  191.      *
  192.      * @param nestedPositions Each subarray specifies a position of a nested element (from the nested[] array) and the length of that element.
  193.      */
  194.     public void setNestedPositions(final int[][] nestedPositions) {
  195.         this.nestedPositions = nestedPositions;
  196.     }

  197.     /**
  198.      * 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.
  199.      *
  200.      * @param operand  int to set the rewrite bytes to
  201.      * @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
  202.      *                 second -1, etc.
  203.      */
  204.     public void setOperand2Bytes(final int operand, final int position) {
  205.         final int firstOperandIndex = getByteCodeForm().firstOperandIndex();
  206.         final int byteCodeFormLength = getByteCodeForm().getRewrite().length;
  207.         if (firstOperandIndex < 1) {
  208.             // No operand rewriting permitted for this bytecode
  209.             throw new Error("Trying to rewrite " + this + " that has no rewrite");
  210.         }

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

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

  217.     /**
  218.      * 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.
  219.      *
  220.      * @param operand  int to set the rewrite byte to (unsigned)
  221.      * @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
  222.      *                 second -1, etc.
  223.      */
  224.     public void setOperandByte(final int operand, final int position) {
  225.         final int firstOperandIndex = getByteCodeForm().firstOperandIndex();
  226.         final int byteCodeFormLength = getByteCodeForm().operandLength();
  227.         if (firstOperandIndex < 1) {
  228.             // No operand rewriting permitted for this bytecode
  229.             throw new Error("Trying to rewrite " + this + " that has no rewrite");
  230.         }

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

  234.         rewrite[firstOperandIndex + position] = operand & 0xFF;
  235.     }

  236.     /**
  237.      * 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
  238.      * values in operands[] will be masked with 0xFF so they fit into a byte.
  239.      *
  240.      * @param operands int[] rewrite operand bytes
  241.      */
  242.     public void setOperandBytes(final int[] operands) {
  243.         final int firstOperandIndex = getByteCodeForm().firstOperandIndex();
  244.         final int byteCodeFormLength = getByteCodeForm().operandLength();
  245.         if (firstOperandIndex < 1) {
  246.             // No operand rewriting permitted for this bytecode
  247.             throw new Error("Trying to rewrite " + this + " that has no rewrite");
  248.         }

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

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

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

  270.     /**
  271.      * 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.
  272.      *
  273.      * Note that this should not be called from bytecodes which have a static rewrite; use the table in ByteCodeForm instead to specify those rewrites.
  274.      *
  275.      * @param rewrite Some bytecodes.
  276.      */
  277.     public void setRewrite(final int[] rewrite) {
  278.         this.rewrite = rewrite;
  279.     }

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