1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
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
67 final List<ClassFileEntry> parents = new ArrayList<>(512);
68 final List<ClassFileEntry> children = new ArrayList<>(512);
69
70
71 parents.addAll(entries);
72 parents.addAll(others);
73
74
75
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
84
85 for (int indexParents = 0; indexParents < parents.size(); indexParents++) {
86 final ClassFileEntry entry = parents.get(indexParents);
87
88
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
99 add(entry);
100 }
101
102 added = !(entries.size() == entriesOriginalSize && others.size() == othersOriginalSize);
103
104
105
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
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
186
187
188
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
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);
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);
226 entries.add(entry);
227 index += 2;
228 } else {
229 entries.add(entry);
230 index += 1;
231 }
232 }
233
234 }
235 }