SegmentConstantPool.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;

  18. import java.util.List;

  19. import org.apache.commons.compress.harmony.pack200.Pack200Exception;
  20. import org.apache.commons.compress.harmony.unpack200.bytecode.ClassFileEntry;
  21. import org.apache.commons.compress.harmony.unpack200.bytecode.ConstantPoolEntry;

  22. /**
  23.  * SegmentConstantPool manages the constant pool used for re-creating class files.
  24.  */
  25. public class SegmentConstantPool {

  26.     public static final int ALL = 0;
  27.     public static final int UTF_8 = 1;

  28.     public static final int CP_INT = 2;

  29.     // define in archive order

  30.     public static final int CP_FLOAT = 3;
  31.     public static final int CP_LONG = 4;
  32.     public static final int CP_DOUBLE = 5;
  33.     public static final int CP_STRING = 6;
  34.     public static final int CP_CLASS = 7;
  35.     public static final int SIGNATURE = 8; // TODO and more to come --
  36.     public static final int CP_DESCR = 9;
  37.     public static final int CP_FIELD = 10;
  38.     public static final int CP_METHOD = 11;
  39.     public static final int CP_IMETHOD = 12;
  40.     protected static final String REGEX_MATCH_ALL = ".*";
  41.     protected static final String INITSTRING = "<init>";
  42.     protected static final String REGEX_MATCH_INIT = "^" + INITSTRING + ".*";

  43.     /**
  44.      * We don't want a dependency on regex in Pack200. The only place one exists is in matchSpecificPoolEntryIndex(). To eliminate this dependency, we've
  45.      * implemented the world's stupidest regexMatch. It knows about the two forms we care about: .* (aka REGEX_MATCH_ALL) {@code ^<init>;.*} (aka
  46.      * REGEX_MATCH_INIT) and will answer correctly if those are passed as the regexString.
  47.      *
  48.      * @param regexString   String against which the compareString will be matched
  49.      * @param compareString String to match against the regexString
  50.      * @return boolean true if the compareString matches the regexString; otherwise false.
  51.      */
  52.     protected static boolean regexMatches(final String regexString, final String compareString) {
  53.         if (REGEX_MATCH_ALL.equals(regexString)) {
  54.             return true;
  55.         }
  56.         if (REGEX_MATCH_INIT.equals(regexString)) {
  57.             if (compareString.length() < INITSTRING.length()) {
  58.                 return false;
  59.             }
  60.             return INITSTRING.equals(compareString.substring(0, INITSTRING.length()));
  61.         }
  62.         throw new Error("regex trying to match a pattern I don't know: " + regexString);
  63.     }

  64.     private final CpBands bands;
  65.     private final SegmentConstantPoolArrayCache arrayCache = new SegmentConstantPoolArrayCache();

  66.     /**
  67.      * @param bands TODO
  68.      */
  69.     public SegmentConstantPool(final CpBands bands) {
  70.         this.bands = bands;
  71.     }

  72.     /**
  73.      * Given the name of a class, answer the CPClass associated with that class. Answer null if the class doesn't exist.
  74.      *
  75.      * @param name Class name to look for (form: java/lang/Object)
  76.      * @return CPClass for that class name, or null if not found.
  77.      */
  78.     public ConstantPoolEntry getClassPoolEntry(final String name) {
  79.         final String[] classes = bands.getCpClass();
  80.         final int index = matchSpecificPoolEntryIndex(classes, name, 0);
  81.         if (index == -1) {
  82.             return null;
  83.         }
  84.         try {
  85.             return getConstantPoolEntry(CP_CLASS, index);
  86.         } catch (final Pack200Exception ex) {
  87.             throw new Error("Error getting class pool entry");
  88.         }
  89.     }

  90.     /**
  91.      * Subset the constant pool of the specified type to be just that which has the specified class name. Answer the ConstantPoolEntry at the desiredIndex of
  92.      * the subsetted pool.
  93.      *
  94.      * @param cp               type of constant pool array to search
  95.      * @param desiredIndex     index of the constant pool
  96.      * @param desiredClassName class to use to generate a subset of the pool
  97.      * @return ConstantPoolEntry
  98.      * @throws Pack200Exception TODO
  99.      */
  100.     public ConstantPoolEntry getClassSpecificPoolEntry(final int cp, final long desiredIndex, final String desiredClassName) throws Pack200Exception {
  101.         final int index = (int) desiredIndex;
  102.         int realIndex = -1;
  103.         String[] array;
  104.         switch (cp) {
  105.         case CP_FIELD:
  106.             array = bands.getCpFieldClass();
  107.             break;
  108.         case CP_METHOD:
  109.             array = bands.getCpMethodClass();
  110.             break;
  111.         case CP_IMETHOD:
  112.             array = bands.getCpIMethodClass();
  113.             break;
  114.         default:
  115.             throw new Error("Don't know how to handle " + cp);
  116.         }
  117.         realIndex = matchSpecificPoolEntryIndex(array, desiredClassName, index);
  118.         return getConstantPoolEntry(cp, realIndex);
  119.     }

  120.     public ConstantPoolEntry getConstantPoolEntry(final int cp, final long value) throws Pack200Exception {
  121.         final int index = (int) value;
  122.         if (index == -1) {
  123.             return null;
  124.         }
  125.         if (index < 0) {
  126.             throw new Pack200Exception("Cannot have a negative range");
  127.         }
  128.         switch (cp) {
  129.         case UTF_8:
  130.             return bands.cpUTF8Value(index);
  131.         case CP_INT:
  132.             return bands.cpIntegerValue(index);
  133.         case CP_FLOAT:
  134.             return bands.cpFloatValue(index);
  135.         case CP_LONG:
  136.             return bands.cpLongValue(index);
  137.         case CP_DOUBLE:
  138.             return bands.cpDoubleValue(index);
  139.         case CP_STRING:
  140.             return bands.cpStringValue(index);
  141.         case CP_CLASS:
  142.             return bands.cpClassValue(index);
  143.         case SIGNATURE:
  144.             throw new Error("I don't know what to do with signatures yet");
  145.         // return null /* new CPSignature(bands.getCpSignature()[index]) */;
  146.         case CP_DESCR:
  147.             throw new Error("I don't know what to do with descriptors yet");
  148.         // return null /* new CPDescriptor(bands.getCpDescriptor()[index])
  149.         // */;
  150.         case CP_FIELD:
  151.             return bands.cpFieldValue(index);
  152.         case CP_METHOD:
  153.             return bands.cpMethodValue(index);
  154.         case CP_IMETHOD:
  155.             return bands.cpIMethodValue(index);
  156.         default:
  157.             break;
  158.         }
  159.         // etc
  160.         throw new Error("Get value incomplete");
  161.     }

  162.     /**
  163.      * Answer the init method for the specified class.
  164.      *
  165.      * @param cp               constant pool to search (must be CP_METHOD)
  166.      * @param value            index of init method
  167.      * @param desiredClassName String class name of the init method
  168.      * @return CPMethod init method
  169.      * @throws Pack200Exception TODO
  170.      */
  171.     public ConstantPoolEntry getInitMethodPoolEntry(final int cp, final long value, final String desiredClassName) throws Pack200Exception {
  172.         int realIndex = -1;
  173.         if (cp != CP_METHOD) {
  174.             // TODO really an error?
  175.             throw new Error("Nothing but CP_METHOD can be an <init>");
  176.         }
  177.         realIndex = matchSpecificPoolEntryIndex(bands.getCpMethodClass(), bands.getCpMethodDescriptor(), desiredClassName, REGEX_MATCH_INIT, (int) value);
  178.         return getConstantPoolEntry(cp, realIndex);
  179.     }

  180.     public ClassFileEntry getValue(final int cp, final long value) throws Pack200Exception {
  181.         final int index = (int) value;
  182.         if (index == -1) {
  183.             return null;
  184.         }
  185.         if (index < 0) {
  186.             throw new Pack200Exception("Cannot have a negative range");
  187.         }
  188.         switch (cp) {
  189.         case UTF_8:
  190.             return bands.cpUTF8Value(index);
  191.         case CP_INT:
  192.             return bands.cpIntegerValue(index);
  193.         case CP_FLOAT:
  194.             return bands.cpFloatValue(index);
  195.         case CP_LONG:
  196.             return bands.cpLongValue(index);
  197.         case CP_DOUBLE:
  198.             return bands.cpDoubleValue(index);
  199.         case CP_STRING:
  200.             return bands.cpStringValue(index);
  201.         case CP_CLASS:
  202.             return bands.cpClassValue(index);
  203.         case SIGNATURE:
  204.             return bands.cpSignatureValue(index);
  205.         case CP_DESCR:
  206.             return bands.cpNameAndTypeValue(index);
  207.         default:
  208.             break;
  209.         }
  210.         throw new Error("Tried to get a value I don't know about: " + cp);
  211.     }

  212.     /**
  213.      * A number of things make use of subsets of structures. In one particular example, _super bytecodes will use a subset of method or field classes which have
  214.      * just those methods / fields defined in the superclass. Similarly, _this bytecodes use just those methods/fields defined in this class, and _init
  215.      * bytecodes use just those methods that start with {@code <init>}.
  216.      *
  217.      * This method takes an array of names, a String to match for, an index and a boolean as parameters, and answers the array position in the array of the
  218.      * indexth element which matches (or equals) the String (depending on the state of the boolean)
  219.      *
  220.      * In other words, if the class array consists of: Object [position 0, 0th instance of Object] String [position 1, 0th instance of String] String [position
  221.      * 2, 1st instance of String] Object [position 3, 1st instance of Object] Object [position 4, 2nd instance of Object] then matchSpecificPoolEntryIndex(...,
  222.      * "Object", 2, false) will answer 4. matchSpecificPoolEntryIndex(..., "String", 0, false) will answer 1.
  223.      *
  224.      * @param nameArray     Array of Strings against which the compareString is tested
  225.      * @param compareString String for which to search
  226.      * @param desiredIndex  nth element with that match (counting from 0)
  227.      * @return int index into nameArray, or -1 if not found.
  228.      */
  229.     protected int matchSpecificPoolEntryIndex(final String[] nameArray, final String compareString, final int desiredIndex) {
  230.         return matchSpecificPoolEntryIndex(nameArray, nameArray, compareString, REGEX_MATCH_ALL, desiredIndex);
  231.     }

  232.     /**
  233.      * This method's function is to look through pairs of arrays. It keeps track of the number of hits it finds using the following basis of comparison for a
  234.      * hit: - the primaryArray[index] must be .equals() to the primaryCompareString - the secondaryArray[index] .matches() the secondaryCompareString. When the
  235.      * desiredIndex number of hits has been reached, the index into the original two arrays of the element hit is returned.
  236.      *
  237.      * @param primaryArray          The first array to search
  238.      * @param secondaryArray        The second array (must be same .length as primaryArray)
  239.      * @param primaryCompareString  The String to compare against primaryArray using .equals()
  240.      * @param secondaryCompareRegex The String to compare against secondaryArray using .matches()
  241.      * @param desiredIndex          The nth hit whose position we're seeking
  242.      * @return int index that represents the position of the nth hit in primaryArray and secondaryArray
  243.      */
  244.     protected int matchSpecificPoolEntryIndex(final String[] primaryArray, final String[] secondaryArray, final String primaryCompareString,
  245.             final String secondaryCompareRegex, final int desiredIndex) {
  246.         int instanceCount = -1;
  247.         final List<Integer> indexList = arrayCache.indexesForArrayKey(primaryArray, primaryCompareString);
  248.         if (indexList.isEmpty()) {
  249.             // Primary key not found, no chance of finding secondary
  250.             return -1;
  251.         }

  252.         for (final Integer element : indexList) {
  253.             final int arrayIndex = element.intValue();
  254.             if (regexMatches(secondaryCompareRegex, secondaryArray[arrayIndex])) {
  255.                 instanceCount++;
  256.                 if (instanceCount == desiredIndex) {
  257.                     return arrayIndex;
  258.                 }
  259.             }
  260.         }
  261.         // We didn't return in the for loop, so the desiredMatch
  262.         // with desiredIndex must not exist in the arrays.
  263.         return -1;
  264.     }
  265. }