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}