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