IcBands.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.io.IOException;
  19. import java.io.InputStream;
  20. import java.util.ArrayList;
  21. import java.util.HashMap;
  22. import java.util.HashSet;
  23. import java.util.List;
  24. import java.util.Map;
  25. import java.util.Set;

  26. import org.apache.commons.compress.harmony.pack200.Codec;
  27. import org.apache.commons.compress.harmony.pack200.Pack200Exception;
  28. import org.apache.commons.compress.harmony.unpack200.bytecode.CPClass;
  29. import org.apache.commons.compress.harmony.unpack200.bytecode.ClassConstantPool;
  30. import org.apache.commons.compress.harmony.unpack200.bytecode.ClassFileEntry;
  31. import org.apache.commons.compress.harmony.unpack200.bytecode.ConstantPoolEntry;

  32. /**
  33.  * Inner Class Bands
  34.  */
  35. public class IcBands extends BandSet {

  36.     private IcTuple[] icAll;

  37.     private final String[] cpUTF8;

  38.     private final String[] cpClass;

  39.     private Map<String, IcTuple> thisClassToTuple;
  40.     private Map<String, List<IcTuple>> outerClassToTuples;

  41.     /**
  42.      * @param segment TODO
  43.      */
  44.     public IcBands(final Segment segment) {
  45.         super(segment);
  46.         this.cpClass = segment.getCpBands().getCpClass();
  47.         this.cpUTF8 = segment.getCpBands().getCpUTF8();
  48.     }

  49.     public IcTuple[] getIcTuples() {
  50.         return icAll;
  51.     }

  52.     /**
  53.      * Answer the relevant IcTuples for the specified className and class constant pool.
  54.      *
  55.      * @param className String name of the class X for ic_relevant(X)
  56.      * @param cp        ClassConstantPool used to generate ic_relevant(X)
  57.      * @return array of IcTuple
  58.      */
  59.     public IcTuple[] getRelevantIcTuples(final String className, final ClassConstantPool cp) {
  60.         final Set<IcTuple> relevantTuplesContains = new HashSet<>();
  61.         final List<IcTuple> relevantTuples = new ArrayList<>();

  62.         final List<IcTuple> relevantCandidates = outerClassToTuples.get(className);
  63.         if (relevantCandidates != null) {
  64.             for (int index = 0; index < relevantCandidates.size(); index++) {
  65.                 final IcTuple tuple = relevantCandidates.get(index);
  66.                 relevantTuplesContains.add(tuple);
  67.                 relevantTuples.add(tuple);
  68.             }
  69.         }

  70.         final List<ClassFileEntry> entries = cp.entries();

  71.         // For every class constant in both ic_this_class and cp,
  72.         // add it to ic_relevant. Repeat until no more
  73.         // changes to ic_relevant.

  74.         for (int eIndex = 0; eIndex < entries.size(); eIndex++) {
  75.             final ConstantPoolEntry entry = (ConstantPoolEntry) entries.get(eIndex);
  76.             if (entry instanceof CPClass) {
  77.                 final CPClass clazz = (CPClass) entry;
  78.                 final IcTuple relevant = thisClassToTuple.get(clazz.name);
  79.                 if (relevant != null && relevantTuplesContains.add(relevant)) {
  80.                     relevantTuples.add(relevant);
  81.                 }
  82.             }
  83.         }

  84.         // Not part of spec: fix up by adding to relevantTuples the parents
  85.         // of inner classes which are themselves inner classes.
  86.         // i.e., I think that if Foo$Bar$Baz gets added, Foo$Bar needs to be
  87.         // added
  88.         // as well.

  89.         final List<IcTuple> tuplesToScan = new ArrayList<>(relevantTuples);
  90.         final List<IcTuple> tuplesToAdd = new ArrayList<>();

  91.         while (tuplesToScan.size() > 0) {

  92.             tuplesToAdd.clear();
  93.             for (int index = 0; index < tuplesToScan.size(); index++) {
  94.                 final IcTuple aRelevantTuple = tuplesToScan.get(index);
  95.                 final IcTuple relevant = thisClassToTuple.get(aRelevantTuple.outerClassString());
  96.                 if (relevant != null && !aRelevantTuple.outerIsAnonymous()) {
  97.                     tuplesToAdd.add(relevant);
  98.                 }
  99.             }

  100.             tuplesToScan.clear();
  101.             for (int index = 0; index < tuplesToAdd.size(); index++) {
  102.                 final IcTuple tuple = tuplesToAdd.get(index);
  103.                 if (relevantTuplesContains.add(tuple)) {
  104.                     relevantTuples.add(tuple);
  105.                     tuplesToScan.add(tuple);
  106.                 }
  107.             }

  108.         }

  109.         // End not part of the spec. Ugh.

  110.         // Now order the result as a subsequence of ic_all
  111.         relevantTuples.sort((arg0, arg1) -> {
  112.             final int index1 = arg0.getTupleIndex();
  113.             final Integer index2 = Integer.valueOf(arg1.getTupleIndex());
  114.             return Integer.compare(index1, index2);
  115.         });

  116.         return relevantTuples.toArray(IcTuple.EMPTY_ARRAY);
  117.     }

  118.     /*
  119.      * (non-Javadoc)
  120.      *
  121.      * @see org.apache.commons.compress.harmony.unpack200.BandSet#unpack(java.io.InputStream)
  122.      */
  123.     @Override
  124.     public void read(final InputStream in) throws IOException, Pack200Exception {
  125.         // Read IC bands
  126.         final int innerClassCount = header.getInnerClassCount();
  127.         final int[] icThisClassInts = decodeBandInt("ic_this_class", in, Codec.UDELTA5, innerClassCount);
  128.         final String[] icThisClass = getReferences(icThisClassInts, cpClass);
  129.         final int[] icFlags = decodeBandInt("ic_flags", in, Codec.UNSIGNED5, innerClassCount);
  130.         final int outerClasses = SegmentUtils.countBit16(icFlags);
  131.         final int[] icOuterClassInts = decodeBandInt("ic_outer_class", in, Codec.DELTA5, outerClasses);
  132.         final String[] icOuterClass = new String[outerClasses];
  133.         for (int i = 0; i < icOuterClass.length; i++) {
  134.             if (icOuterClassInts[i] == 0) {
  135.                 icOuterClass[i] = null;
  136.             } else {
  137.                 icOuterClass[i] = cpClass[icOuterClassInts[i] - 1];
  138.             }
  139.         }
  140.         final int[] icNameInts = decodeBandInt("ic_name", in, Codec.DELTA5, outerClasses);
  141.         final String[] icName = new String[outerClasses];
  142.         for (int i = 0; i < icName.length; i++) {
  143.             if (icNameInts[i] == 0) {
  144.                 icName[i] = null;
  145.             } else {
  146.                 icName[i] = cpUTF8[icNameInts[i] - 1];
  147.             }
  148.         }

  149.         // Construct IC tuples
  150.         icAll = new IcTuple[icThisClass.length];
  151.         int index = 0;
  152.         for (int i = 0; i < icThisClass.length; i++) {
  153.             final String icTupleC = icThisClass[i];
  154.             final int icTupleF = icFlags[i];
  155.             String icTupleC2 = null;
  156.             String icTupleN = null;
  157.             final int cIndex = icThisClassInts[i];
  158.             int c2Index = -1;
  159.             int nIndex = -1;
  160.             if ((icFlags[i] & 1 << 16) != 0) {
  161.                 icTupleC2 = icOuterClass[index];
  162.                 icTupleN = icName[index];
  163.                 c2Index = icOuterClassInts[index] - 1;
  164.                 nIndex = icNameInts[index] - 1;
  165.                 index++;
  166.             }
  167.             icAll[i] = new IcTuple(icTupleC, icTupleF, icTupleC2, icTupleN, cIndex, c2Index, nIndex, i);
  168.         }
  169.     }

  170.     @Override
  171.     public void unpack() throws IOException, Pack200Exception {
  172.         final IcTuple[] allTuples = getIcTuples();
  173.         thisClassToTuple = new HashMap<>(allTuples.length);
  174.         outerClassToTuples = new HashMap<>(allTuples.length);
  175.         for (final IcTuple tuple : allTuples) {

  176.             // generate mapping thisClassString -> IcTuple
  177.             // presumably this relation is 1:1
  178.             //
  179.             final Object result = thisClassToTuple.put(tuple.thisClassString(), tuple);
  180.             if (result != null) {
  181.                 throw new Error("Collision detected in <thisClassString, IcTuple> mapping. " + "There are at least two inner clases with the same name.");
  182.             }

  183.             // generate mapping outerClassString -> IcTuple
  184.             // this relation is 1:M

  185.             // If it's not anon and the outer is not anon, it could be relevant
  186.             if (!tuple.isAnonymous() && !tuple.outerIsAnonymous() || tuple.nestedExplicitFlagSet()) {

  187.                 // add tuple to corresponding bucket
  188.                 final String key = tuple.outerClassString();
  189.                 outerClassToTuples.computeIfAbsent(key, k -> new ArrayList<>()).add(tuple);
  190.             }
  191.         }
  192.     }

  193. }