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.bytecode;
18  
19  import java.util.ArrayList;
20  import java.util.Arrays;
21  import java.util.Collections;
22  import java.util.Comparator;
23  import java.util.HashMap;
24  import java.util.HashSet;
25  import java.util.List;
26  import java.util.Map;
27  import java.util.TreeSet;
28  
29  import org.apache.commons.compress.harmony.unpack200.Segment;
30  
31  /**
32   * The Class constant pool
33   */
34  public class ClassConstantPool {
35  
36      protected HashSet<ClassFileEntry> entriesContainsSet = new HashSet<>();
37      protected HashSet<ClassFileEntry> othersContainsSet = new HashSet<>();
38  
39      private final HashSet<ClassFileEntry> mustStartClassPool = new HashSet<>();
40  
41      protected Map<ClassFileEntry, Integer> indexCache;
42  
43      private final List<ClassFileEntry> others = new ArrayList<>(500);
44      private final List<ClassFileEntry> entries = new ArrayList<>(500);
45  
46      private boolean resolved;
47  
48      public ClassFileEntry add(final ClassFileEntry entry) {
49          if (entry instanceof ByteCode) {
50              return null;
51          }
52          if (entry instanceof ConstantPoolEntry) {
53              if (entriesContainsSet.add(entry)) {
54                  entries.add(entry);
55              }
56          } else if (othersContainsSet.add(entry)) {
57              others.add(entry);
58          }
59  
60          return entry;
61      }
62  
63      public void addNestedEntries() {
64          boolean added = true;
65  
66          // initial assignment
67          final List<ClassFileEntry> parents = new ArrayList<>(512);
68          final List<ClassFileEntry> children = new ArrayList<>(512);
69  
70          // adding old entries
71          parents.addAll(entries);
72          parents.addAll(others);
73  
74          // while there any parents to traverse and at least one change in target
75          // storage was made
76          while (added || parents.size() > 0) {
77  
78              children.clear();
79  
80              final int entriesOriginalSize = entries.size();
81              final int othersOriginalSize = others.size();
82  
83              // get the parents' children and add them to buffer
84              // concurrently add parents to target storage
85              for (int indexParents = 0; indexParents < parents.size(); indexParents++) {
86                  final ClassFileEntry entry = parents.get(indexParents);
87  
88                  // traverse children
89                  final ClassFileEntry[] entryChildren = entry.getNestedClassFileEntries();
90                  children.addAll(Arrays.asList(entryChildren));
91  
92                  final boolean isAtStart = entry instanceof ByteCode && ((ByteCode) entry).nestedMustStartClassPool();
93  
94                  if (isAtStart) {
95                      mustStartClassPool.addAll(Arrays.asList(entryChildren));
96                  }
97  
98                  // add parent
99                  add(entry);
100             }
101 
102             added = !(entries.size() == entriesOriginalSize && others.size() == othersOriginalSize);
103 
104             // parents are not needed anymore
105             // children now become parents
106             parents.clear();
107             parents.addAll(children);
108         }
109     }
110 
111     public ClassFileEntry addWithNestedEntries(final ClassFileEntry entry) {
112         add(entry);
113         for (final ClassFileEntry nestedEntry : entry.getNestedClassFileEntries()) {
114             addWithNestedEntries(nestedEntry);
115         }
116         return entry;
117     }
118 
119     public List<ClassFileEntry> entries() {
120         return Collections.unmodifiableList(entries);
121     }
122 
123     public ClassFileEntry get(int i) {
124         if (!resolved) {
125             throw new IllegalStateException("Constant pool is not yet resolved; this does not make any sense");
126         }
127         return entries.get(--i);
128     }
129 
130     public int indexOf(final ClassFileEntry entry) {
131         if (!resolved) {
132             throw new IllegalStateException("Constant pool is not yet resolved; this does not make any sense");
133         }
134         if (null == indexCache) {
135             throw new IllegalStateException("Index cache is not initialized!");
136         }
137         final Integer entryIndex = indexCache.get(entry);
138         // If the entry isn't found, answer -1, otherwise answer the entry.
139         if (entryIndex != null) {
140             return entryIndex.intValue() + 1;
141         }
142         return -1;
143     }
144 
145     private void initialSort() {
146         final TreeSet<ClassFileEntry> inCpAll = new TreeSet<>(Comparator.comparingInt(arg0 -> ((ConstantPoolEntry) arg0).getGlobalIndex()));
147         final TreeSet<ClassFileEntry> cpUtf8sNotInCpAll = new TreeSet<>(Comparator.comparing(arg0 -> ((CPUTF8) arg0).underlyingString()));
148         final TreeSet<ClassFileEntry> cpClassesNotInCpAll = new TreeSet<>(Comparator.comparing(arg0 -> ((CPClass) arg0).getName()));
149 
150         for (final ClassFileEntry entry2 : entries) {
151             final ConstantPoolEntry entry = (ConstantPoolEntry) entry2;
152             if (entry.getGlobalIndex() == -1) {
153                 if (entry instanceof CPUTF8) {
154                     cpUtf8sNotInCpAll.add(entry);
155                 } else if (entry instanceof CPClass) {
156                     cpClassesNotInCpAll.add(entry);
157                 } else {
158                     throw new Error("error");
159                 }
160             } else {
161                 inCpAll.add(entry);
162             }
163         }
164         entries.clear();
165         entries.addAll(inCpAll);
166         entries.addAll(cpUtf8sNotInCpAll);
167         entries.addAll(cpClassesNotInCpAll);
168     }
169 
170     public void resolve(final Segment segment) {
171         initialSort();
172         sortClassPool();
173 
174         resolved = true;
175 
176         entries.forEach(entry -> entry.resolve(this));
177         others.forEach(entry -> entry.resolve(this));
178     }
179 
180     public int size() {
181         return entries.size();
182     }
183 
184     protected void sortClassPool() {
185         // Now that everything has been resolved, do one
186         // final sort of the class pool. This fixes up
187         // references to objects which need to be at the
188         // start of the class pool
189 
190         final List<ClassFileEntry> startOfPool = new ArrayList<>(entries.size());
191         final List<ClassFileEntry> finalSort = new ArrayList<>(entries.size());
192 
193         for (final ClassFileEntry entry : entries) {
194             if (mustStartClassPool.contains(entry)) {
195                 startOfPool.add(entry);
196             } else {
197                 finalSort.add(entry);
198             }
199         }
200 
201         // copy over and rebuild the cache
202         //
203         indexCache = new HashMap<>(entries.size());
204         int index = 0;
205 
206         entries.clear();
207 
208         for (final ClassFileEntry entry : startOfPool) {
209             indexCache.put(entry, Integer.valueOf(index));
210 
211             if (entry instanceof CPLong || entry instanceof CPDouble) {
212                 entries.add(entry); // these get 2 slots because of their size
213                 entries.add(entry);
214                 index += 2;
215             } else {
216                 entries.add(entry);
217                 index += 1;
218             }
219         }
220 
221         for (final ClassFileEntry entry : finalSort) {
222             indexCache.put(entry, Integer.valueOf(index));
223 
224             if (entry instanceof CPLong || entry instanceof CPDouble) {
225                 entries.add(entry); // these get 2 slots because of their size
226                 entries.add(entry);
227                 index += 2;
228             } else {
229                 entries.add(entry);
230                 index += 1;
231             }
232         }
233 
234     }
235 }