    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<>(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}