ClassConstantPool.java

  1. /*
  2.  *  Licensed to the Apache Software Foundation (ASF) under one or more
  3.  *  contributor license agreements.  See the NOTICE file distributed with
  4.  *  this work for additional information regarding copyright ownership.
  5.  *  The ASF licenses this file to You under the Apache License, Version 2.0
  6.  *  (the "License"); you may not use this file except in compliance with
  7.  *  the License.  You may obtain a copy of the License at
  8.  *
  9.  *     http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  *  Unless required by applicable law or agreed to in writing, software
  12.  *  distributed under the License is distributed on an "AS IS" BASIS,
  13.  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  *  See the License for the specific language governing permissions and
  15.  *  limitations under the License.
  16.  */
  17. package org.apache.commons.compress.harmony.unpack200.bytecode;

  18. import java.util.ArrayList;
  19. import java.util.Arrays;
  20. import java.util.Collections;
  21. import java.util.Comparator;
  22. import java.util.HashMap;
  23. import java.util.HashSet;
  24. import java.util.List;
  25. import java.util.Map;
  26. import java.util.TreeSet;

  27. import org.apache.commons.compress.harmony.unpack200.Segment;

  28. /**
  29.  * The Class constant pool
  30.  */
  31. public class ClassConstantPool {

  32.     protected HashSet<ClassFileEntry> entriesContainsSet = new HashSet<>();
  33.     protected HashSet<ClassFileEntry> othersContainsSet = new HashSet<>();

  34.     private final HashSet<ClassFileEntry> mustStartClassPool = new HashSet<>();

  35.     protected Map<ClassFileEntry, Integer> indexCache;

  36.     private final List<ClassFileEntry> others = new ArrayList<>(500);
  37.     private final List<ClassFileEntry> entries = new ArrayList<>(500);

  38.     private boolean resolved;

  39.     public ClassFileEntry add(final ClassFileEntry entry) {
  40.         if (entry instanceof ByteCode) {
  41.             return null;
  42.         }
  43.         if (entry instanceof ConstantPoolEntry) {
  44.             if (entriesContainsSet.add(entry)) {
  45.                 entries.add(entry);
  46.             }
  47.         } else if (othersContainsSet.add(entry)) {
  48.             others.add(entry);
  49.         }

  50.         return entry;
  51.     }

  52.     public void addNestedEntries() {
  53.         boolean added = true;

  54.         // initial assignment
  55.         final List<ClassFileEntry> parents = new ArrayList<>(512);
  56.         final List<ClassFileEntry> children = new ArrayList<>(512);

  57.         // adding old entries
  58.         parents.addAll(entries);
  59.         parents.addAll(others);

  60.         // while there any parents to traverse and at least one change in target
  61.         // storage was made
  62.         while (added || parents.size() > 0) {

  63.             children.clear();

  64.             final int entriesOriginalSize = entries.size();
  65.             final int othersOriginalSize = others.size();

  66.             // get the parents' children and add them to buffer
  67.             // concurrently add parents to target storage
  68.             for (int indexParents = 0; indexParents < parents.size(); indexParents++) {
  69.                 final ClassFileEntry entry = parents.get(indexParents);

  70.                 // traverse children
  71.                 final ClassFileEntry[] entryChildren = entry.getNestedClassFileEntries();
  72.                 children.addAll(Arrays.asList(entryChildren));

  73.                 final boolean isAtStart = entry instanceof ByteCode && ((ByteCode) entry).nestedMustStartClassPool();

  74.                 if (isAtStart) {
  75.                     mustStartClassPool.addAll(Arrays.asList(entryChildren));
  76.                 }

  77.                 // add parent
  78.                 add(entry);
  79.             }

  80.             added = !(entries.size() == entriesOriginalSize && others.size() == othersOriginalSize);

  81.             // parents are not needed anymore
  82.             // children now become parents
  83.             parents.clear();
  84.             parents.addAll(children);
  85.         }
  86.     }

  87.     public ClassFileEntry addWithNestedEntries(final ClassFileEntry entry) {
  88.         add(entry);
  89.         for (final ClassFileEntry nestedEntry : entry.getNestedClassFileEntries()) {
  90.             addWithNestedEntries(nestedEntry);
  91.         }
  92.         return entry;
  93.     }

  94.     public List<ClassFileEntry> entries() {
  95.         return Collections.unmodifiableList(entries);
  96.     }

  97.     public ClassFileEntry get(int i) {
  98.         if (!resolved) {
  99.             throw new IllegalStateException("Constant pool is not yet resolved; this does not make any sense");
  100.         }
  101.         return entries.get(--i);
  102.     }

  103.     public int indexOf(final ClassFileEntry entry) {
  104.         if (!resolved) {
  105.             throw new IllegalStateException("Constant pool is not yet resolved; this does not make any sense");
  106.         }
  107.         if (null == indexCache) {
  108.             throw new IllegalStateException("Index cache is not initialized!");
  109.         }
  110.         final Integer entryIndex = indexCache.get(entry);
  111.         // If the entry isn't found, answer -1, otherwise answer the entry.
  112.         if (entryIndex != null) {
  113.             return entryIndex.intValue() + 1;
  114.         }
  115.         return -1;
  116.     }

  117.     private void initialSort() {
  118.         final TreeSet<ClassFileEntry> inCpAll = new TreeSet<>(Comparator.comparingInt(arg0 -> ((ConstantPoolEntry) arg0).getGlobalIndex()));
  119.         final TreeSet<ClassFileEntry> cpUtf8sNotInCpAll = new TreeSet<>(Comparator.comparing(arg0 -> ((CPUTF8) arg0).underlyingString()));
  120.         final TreeSet<ClassFileEntry> cpClassesNotInCpAll = new TreeSet<>(Comparator.comparing(arg0 -> ((CPClass) arg0).getName()));

  121.         for (final ClassFileEntry entry2 : entries) {
  122.             final ConstantPoolEntry entry = (ConstantPoolEntry) entry2;
  123.             if (entry.getGlobalIndex() == -1) {
  124.                 if (entry instanceof CPUTF8) {
  125.                     cpUtf8sNotInCpAll.add(entry);
  126.                 } else if (entry instanceof CPClass) {
  127.                     cpClassesNotInCpAll.add(entry);
  128.                 } else {
  129.                     throw new Error("error");
  130.                 }
  131.             } else {
  132.                 inCpAll.add(entry);
  133.             }
  134.         }
  135.         entries.clear();
  136.         entries.addAll(inCpAll);
  137.         entries.addAll(cpUtf8sNotInCpAll);
  138.         entries.addAll(cpClassesNotInCpAll);
  139.     }

  140.     public void resolve(final Segment segment) {
  141.         initialSort();
  142.         sortClassPool();

  143.         resolved = true;

  144.         entries.forEach(entry -> entry.resolve(this));
  145.         others.forEach(entry -> entry.resolve(this));
  146.     }

  147.     public int size() {
  148.         return entries.size();
  149.     }

  150.     protected void sortClassPool() {
  151.         // Now that everything has been resolved, do one
  152.         // final sort of the class pool. This fixes up
  153.         // references to objects which need to be at the
  154.         // start of the class pool

  155.         final List<ClassFileEntry> startOfPool = new ArrayList<>(entries.size());
  156.         final List<ClassFileEntry> finalSort = new ArrayList<>(entries.size());

  157.         for (final ClassFileEntry entry : entries) {
  158.             if (mustStartClassPool.contains(entry)) {
  159.                 startOfPool.add(entry);
  160.             } else {
  161.                 finalSort.add(entry);
  162.             }
  163.         }

  164.         // copy over and rebuild the cache
  165.         //
  166.         indexCache = new HashMap<>(entries.size());
  167.         int index = 0;

  168.         entries.clear();

  169.         for (final ClassFileEntry entry : startOfPool) {
  170.             indexCache.put(entry, Integer.valueOf(index));

  171.             if (entry instanceof CPLong || entry instanceof CPDouble) {
  172.                 entries.add(entry); // these get 2 slots because of their size
  173.                 entries.add(entry);
  174.                 index += 2;
  175.             } else {
  176.                 entries.add(entry);
  177.                 index += 1;
  178.             }
  179.         }

  180.         for (final ClassFileEntry entry : finalSort) {
  181.             indexCache.put(entry, Integer.valueOf(index));

  182.             if (entry instanceof CPLong || entry instanceof CPDouble) {
  183.                 entries.add(entry); // these get 2 slots because of their size
  184.                 entries.add(entry);
  185.                 index += 2;
  186.             } else {
  187.                 entries.add(entry);
  188.                 index += 1;
  189.             }
  190.         }

  191.     }
  192. }