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}