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