    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}