001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *   https://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.apache.commons.compress.harmony.unpack200.bytecode;
020
021import java.util.ArrayList;
022import java.util.Arrays;
023import java.util.Collections;
024import java.util.Comparator;
025import java.util.HashMap;
026import java.util.HashSet;
027import java.util.List;
028import java.util.Map;
029import java.util.TreeSet;
030
031import org.apache.commons.compress.harmony.unpack200.Segment;
032
033/**
034 * The Class constant pool
035 */
036public class ClassConstantPool {
037
038    protected HashSet<ClassFileEntry> entriesContainsSet = new HashSet<>();
039    protected HashSet<ClassFileEntry> othersContainsSet = new HashSet<>();
040
041    private final HashSet<ClassFileEntry> mustStartClassPool = new HashSet<>();
042
043    protected Map<ClassFileEntry, Integer> indexCache;
044
045    private final List<ClassFileEntry> others = new ArrayList<>(500);
046    private final List<ClassFileEntry> entries = new ArrayList<>(500);
047
048    private boolean resolved;
049
050    public ClassFileEntry add(final ClassFileEntry entry) {
051        if (entry instanceof ByteCode) {
052            return null;
053        }
054        if (entry instanceof ConstantPoolEntry) {
055            if (entriesContainsSet.add(entry)) {
056                entries.add(entry);
057            }
058        } else if (othersContainsSet.add(entry)) {
059            others.add(entry);
060        }
061
062        return entry;
063    }
064
065    public void addNestedEntries() {
066        boolean added = true;
067
068        // initial assignment
069        final List<ClassFileEntry> parents = new ArrayList<>(512);
070        final List<ClassFileEntry> children = new ArrayList<>(512);
071
072        // adding old entries
073        parents.addAll(entries);
074        parents.addAll(others);
075
076        // while there any parents to traverse and at least one change in target
077        // storage was made
078        while (added || parents.size() > 0) {
079
080            children.clear();
081
082            final int entriesOriginalSize = entries.size();
083            final int othersOriginalSize = others.size();
084
085            // get the parents' children and add them to buffer
086            // concurrently add parents to target storage
087            for (int indexParents = 0; indexParents < parents.size(); indexParents++) {
088                final ClassFileEntry entry = parents.get(indexParents);
089
090                // traverse children
091                final ClassFileEntry[] entryChildren = entry.getNestedClassFileEntries();
092                children.addAll(Arrays.asList(entryChildren));
093
094                final boolean isAtStart = entry instanceof ByteCode && ((ByteCode) entry).nestedMustStartClassPool();
095
096                if (isAtStart) {
097                    mustStartClassPool.addAll(Arrays.asList(entryChildren));
098                }
099
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}