    001/*
    002 *  Licensed to the Apache Software Foundation (ASF) under one or more
    003 *  contributor license agreements.  See the NOTICE file distributed with
    004 *  this work for additional information regarding copyright ownership.
    005 *  The ASF licenses this file to You under the Apache License, Version 2.0
    006 *  (the "License"); you may not use this file except in compliance with
    007 *  the License.  You may obtain a copy of the License at
    008 *
    009 *     http://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 *  Unless required by applicable law or agreed to in writing, software
    012 *  distributed under the License is distributed on an "AS IS" BASIS,
    013 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 *  See the License for the specific language governing permissions and
    015 *  limitations under the License.
    016 */
    017package org.apache.commons.compress.harmony.unpack200.bytecode;
    018
    019import java.util.ArrayList;
    020import java.util.Arrays;
    021import java.util.Collections;
    022import java.util.Comparator;
    023import java.util.HashMap;
    024import java.util.HashSet;
    025import java.util.List;
    026import java.util.Map;
    027import java.util.TreeSet;
    028
    029import org.apache.commons.compress.harmony.unpack200.Segment;
    030
    031/**
    032 * The Class constant pool
    033 */
    034public class ClassConstantPool {
    035
    036    protected HashSet<ClassFileEntry> entriesContainsSet = new HashSet<>();
    037    protected HashSet<ClassFileEntry> othersContainsSet = new HashSet<>();
    038
    039    private final HashSet<ClassFileEntry> mustStartClassPool = new HashSet<>();
    040
    041    protected Map<ClassFileEntry, Integer> indexCache;
    042
    043    private final List<ClassFileEntry> others = new ArrayList<>(500);
    044    private final List<ClassFileEntry> entries = new ArrayList<>(500);
    045
    046    private boolean resolved;
    047
    048    public ClassFileEntry add(final ClassFileEntry entry) {
    049        if (entry instanceof ByteCode) {
    050            return null;
    051        }
    052        if (entry instanceof ConstantPoolEntry) {
    053            if (entriesContainsSet.add(entry)) {
    054                entries.add(entry);
    055            }
    056        } else if (othersContainsSet.add(entry)) {
    057            others.add(entry);
    058        }
    059
    060        return entry;
    061    }
    062
    063    public void addNestedEntries() {
    064        boolean added = true;
    065
    066        // initial assignment
    067        final List<ClassFileEntry> parents = new ArrayList<>(512);
    068        final List<ClassFileEntry> children = new ArrayList<>(512);
    069
    070        // adding old entries
    071        parents.addAll(entries);
    072        parents.addAll(others);
    073
    074        // while there any parents to traverse and at least one change in target
    075        // storage was made
    076        while (added || parents.size() > 0) {
    077
    078            children.clear();
    079
    080            final int entriesOriginalSize = entries.size();
    081            final int othersOriginalSize = others.size();
    082
    083            // get the parents' children and add them to buffer
    084            // concurrently add parents to target storage
    085            for (int indexParents = 0; indexParents < parents.size(); indexParents++) {
    086                final ClassFileEntry entry = parents.get(indexParents);
    087
    088                // traverse children
    089                final ClassFileEntry[] entryChildren = entry.getNestedClassFileEntries();
    090                children.addAll(Arrays.asList(entryChildren));
    091
    092                final boolean isAtStart = entry instanceof ByteCode && ((ByteCode) entry).nestedMustStartClassPool();
    093
    094                if (isAtStart) {
    095                    mustStartClassPool.addAll(Arrays.asList(entryChildren));
    096                }
    097
    098                // add parent
    099                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<>(
    147                Comparator.comparingInt(arg0 -> ((ConstantPoolEntry) arg0).getGlobalIndex()));
    148        final TreeSet<ClassFileEntry> cpUtf8sNotInCpAll = new TreeSet<>(
    149                Comparator.comparing(arg0 -> ((CPUTF8) arg0).underlyingString()));
    150        final TreeSet<ClassFileEntry> cpClassesNotInCpAll = new TreeSet<>(
    151                Comparator.comparing(arg0 -> ((CPClass) arg0).getName()));
    152
    153        for (final ClassFileEntry entry2 : entries) {
    154            final ConstantPoolEntry entry = (ConstantPoolEntry) entry2;
    155            if (entry.getGlobalIndex() == -1) {
    156                if (entry instanceof CPUTF8) {
    157                    cpUtf8sNotInCpAll.add(entry);
    158                } else if (entry instanceof CPClass) {
    159                    cpClassesNotInCpAll.add(entry);
    160                } else {
    161                    throw new Error("error");
    162                }
    163            } else {
    164                inCpAll.add(entry);
    165            }
    166        }
    167        entries.clear();
    168        entries.addAll(inCpAll);
    169        entries.addAll(cpUtf8sNotInCpAll);
    170        entries.addAll(cpClassesNotInCpAll);
    171    }
    172
    173    public void resolve(final Segment segment) {
    174        initialSort();
    175        sortClassPool();
    176
    177        resolved = true;
    178
    179        entries.forEach(entry -> entry.resolve(this));
    180        others.forEach(entry -> entry.resolve(this));
    181    }
    182
    183    public int size() {
    184        return entries.size();
    185    }
    186
    187    protected void sortClassPool() {
    188        // Now that everything has been resolved, do one
    189        // final sort of the class pool. This fixes up
    190        // references to objects which need to be at the
    191        // start of the class pool
    192
    193        final List<ClassFileEntry> startOfPool = new ArrayList<>(entries.size());
    194        final List<ClassFileEntry> finalSort = new ArrayList<>(entries.size());
    195
    196        for (final ClassFileEntry entry : entries) {
    197            if (mustStartClassPool.contains(entry)) {
    198                startOfPool.add(entry);
    199            } else {
    200                finalSort.add(entry);
    201            }
    202        }
    203
    204        // copy over and rebuild the cache
    205        //
    206        indexCache = new HashMap<>(entries.size());
    207        int index = 0;
    208
    209        entries.clear();
    210
    211        for (final ClassFileEntry entry : startOfPool) {
    212            indexCache.put(entry, Integer.valueOf(index));
    213
    214            if (entry instanceof CPLong || entry instanceof CPDouble) {
    215                entries.add(entry); // these get 2 slots because of their size
    216                entries.add(entry);
    217                index += 2;
    218            } else {
    219                entries.add(entry);
    220                index += 1;
    221            }
    222        }
    223
    224        for (final ClassFileEntry entry : finalSort) {
    225            indexCache.put(entry, Integer.valueOf(index));
    226
    227            if (entry instanceof CPLong || entry instanceof CPDouble) {
    228                entries.add(entry); // these get 2 slots because of their size
    229                entries.add(entry);
    230                index += 2;
    231            } else {
    232                entries.add(entry);
    233                index += 1;
    234            }
    235        }
    236
    237    }
    238}