View Javadoc
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;
20  
21  import java.io.IOException;
22  import java.io.InputStream;
23  import java.util.ArrayList;
24  import java.util.HashMap;
25  import java.util.HashSet;
26  import java.util.List;
27  import java.util.Map;
28  import java.util.Set;
29  
30  import org.apache.commons.compress.harmony.pack200.Codec;
31  import org.apache.commons.compress.harmony.pack200.Pack200Exception;
32  import org.apache.commons.compress.harmony.unpack200.bytecode.CPClass;
33  import org.apache.commons.compress.harmony.unpack200.bytecode.ClassConstantPool;
34  import org.apache.commons.compress.harmony.unpack200.bytecode.ClassFileEntry;
35  import org.apache.commons.compress.harmony.unpack200.bytecode.ConstantPoolEntry;
36  
37  /**
38   * Inner Class Bands
39   */
40  public class IcBands extends BandSet {
41  
42      private IcTuple[] icAll;
43  
44      private final String[] cpUTF8;
45  
46      private final String[] cpClass;
47  
48      private Map<String, IcTuple> thisClassToTuple;
49      private Map<String, List<IcTuple>> outerClassToTuples;
50  
51      /**
52       * @param segment TODO
53       */
54      public IcBands(final Segment segment) {
55          super(segment);
56          this.cpClass = segment.getCpBands().getCpClass();
57          this.cpUTF8 = segment.getCpBands().getCpUTF8();
58      }
59  
60      public IcTuple[] getIcTuples() {
61          return icAll;
62      }
63  
64      /**
65       * Answer the relevant IcTuples for the specified className and class constant pool.
66       *
67       * @param className String name of the class X for ic_relevant(X)
68       * @param cp        ClassConstantPool used to generate ic_relevant(X)
69       * @return array of IcTuple
70       */
71      public IcTuple[] getRelevantIcTuples(final String className, final ClassConstantPool cp) {
72          final Set<IcTuple> relevantTuplesContains = new HashSet<>();
73          final List<IcTuple> relevantTuples = new ArrayList<>();
74  
75          final List<IcTuple> relevantCandidates = outerClassToTuples.get(className);
76          if (relevantCandidates != null) {
77              for (int index = 0; index < relevantCandidates.size(); index++) {
78                  final IcTuple tuple = relevantCandidates.get(index);
79                  relevantTuplesContains.add(tuple);
80                  relevantTuples.add(tuple);
81              }
82          }
83  
84          final List<ClassFileEntry> entries = cp.entries();
85  
86          // For every class constant in both ic_this_class and cp,
87          // add it to ic_relevant. Repeat until no more
88          // changes to ic_relevant.
89  
90          for (int eIndex = 0; eIndex < entries.size(); eIndex++) {
91              final ConstantPoolEntry entry = (ConstantPoolEntry) entries.get(eIndex);
92              if (entry instanceof CPClass) {
93                  final CPClass clazz = (CPClass) entry;
94                  final IcTuple relevant = thisClassToTuple.get(clazz.name);
95                  if (relevant != null && relevantTuplesContains.add(relevant)) {
96                      relevantTuples.add(relevant);
97                  }
98              }
99          }
100 
101         // Not part of spec: fix up by adding to relevantTuples the parents
102         // of inner classes which are themselves inner classes.
103         // i.e., I think that if Foo$Bar$Baz gets added, Foo$Bar needs to be
104         // added
105         // as well.
106 
107         final List<IcTuple> tuplesToScan = new ArrayList<>(relevantTuples);
108         final List<IcTuple> tuplesToAdd = new ArrayList<>();
109 
110         while (tuplesToScan.size() > 0) {
111 
112             tuplesToAdd.clear();
113             for (int index = 0; index < tuplesToScan.size(); index++) {
114                 final IcTuple aRelevantTuple = tuplesToScan.get(index);
115                 final IcTuple relevant = thisClassToTuple.get(aRelevantTuple.outerClassString());
116                 if (relevant != null && !aRelevantTuple.outerIsAnonymous()) {
117                     tuplesToAdd.add(relevant);
118                 }
119             }
120 
121             tuplesToScan.clear();
122             for (int index = 0; index < tuplesToAdd.size(); index++) {
123                 final IcTuple tuple = tuplesToAdd.get(index);
124                 if (relevantTuplesContains.add(tuple)) {
125                     relevantTuples.add(tuple);
126                     tuplesToScan.add(tuple);
127                 }
128             }
129 
130         }
131 
132         // End not part of the spec. Ugh.
133 
134         // Now order the result as a subsequence of ic_all
135         relevantTuples.sort((arg0, arg1) -> {
136             final int index1 = arg0.getTupleIndex();
137             final Integer index2 = Integer.valueOf(arg1.getTupleIndex());
138             return Integer.compare(index1, index2);
139         });
140 
141         return relevantTuples.toArray(IcTuple.EMPTY_ARRAY);
142     }
143 
144     /*
145      * (non-Javadoc)
146      *
147      * @see org.apache.commons.compress.harmony.unpack200.BandSet#unpack(java.io.InputStream)
148      */
149     @Override
150     public void read(final InputStream in) throws IOException, Pack200Exception {
151         // Read IC bands
152         final int innerClassCount = header.getInnerClassCount();
153         final int[] icThisClassInts = decodeBandInt("ic_this_class", in, Codec.UDELTA5, innerClassCount);
154         final String[] icThisClass = getReferences(icThisClassInts, cpClass);
155         final int[] icFlags = decodeBandInt("ic_flags", in, Codec.UNSIGNED5, innerClassCount);
156         final int outerClasses = SegmentUtils.countBit16(icFlags);
157         final int[] icOuterClassInts = decodeBandInt("ic_outer_class", in, Codec.DELTA5, outerClasses);
158         final String[] icOuterClass = new String[outerClasses];
159         for (int i = 0; i < icOuterClass.length; i++) {
160             if (icOuterClassInts[i] == 0) {
161                 icOuterClass[i] = null;
162             } else {
163                 icOuterClass[i] = cpClass[icOuterClassInts[i] - 1];
164             }
165         }
166         final int[] icNameInts = decodeBandInt("ic_name", in, Codec.DELTA5, outerClasses);
167         final String[] icName = new String[outerClasses];
168         for (int i = 0; i < icName.length; i++) {
169             if (icNameInts[i] == 0) {
170                 icName[i] = null;
171             } else {
172                 icName[i] = cpUTF8[icNameInts[i] - 1];
173             }
174         }
175 
176         // Construct IC tuples
177         icAll = new IcTuple[icThisClass.length];
178         int index = 0;
179         for (int i = 0; i < icThisClass.length; i++) {
180             final String icTupleC = icThisClass[i];
181             final int icTupleF = icFlags[i];
182             String icTupleC2 = null;
183             String icTupleN = null;
184             final int cIndex = icThisClassInts[i];
185             int c2Index = -1;
186             int nIndex = -1;
187             if ((icFlags[i] & 1 << 16) != 0) {
188                 icTupleC2 = icOuterClass[index];
189                 icTupleN = icName[index];
190                 c2Index = icOuterClassInts[index] - 1;
191                 nIndex = icNameInts[index] - 1;
192                 index++;
193             }
194             icAll[i] = new IcTuple(icTupleC, icTupleF, icTupleC2, icTupleN, cIndex, c2Index, nIndex, i);
195         }
196     }
197 
198     @Override
199     public void unpack() throws IOException, Pack200Exception {
200         final IcTuple[] allTuples = getIcTuples();
201         thisClassToTuple = new HashMap<>(allTuples.length);
202         outerClassToTuples = new HashMap<>(allTuples.length);
203         for (final IcTuple tuple : allTuples) {
204 
205             // generate mapping thisClassString -> IcTuple
206             // presumably this relation is 1:1
207             //
208             final Object result = thisClassToTuple.put(tuple.thisClassString(), tuple);
209             if (result != null) {
210                 throw new Error("Collision detected in <thisClassString, IcTuple> mapping. There are at least two inner clases with the same name.");
211             }
212 
213             // generate mapping outerClassString -> IcTuple
214             // this relation is 1:M
215 
216             // If it's not anon and the outer is not anon, it could be relevant
217             if (!tuple.isAnonymous() && !tuple.outerIsAnonymous() || tuple.nestedExplicitFlagSet()) {
218 
219                 // add tuple to corresponding bucket
220                 final String key = tuple.outerClassString();
221                 outerClassToTuples.computeIfAbsent(key, k -> new ArrayList<>()).add(tuple);
222             }
223         }
224     }
225 
226 }