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 int indexOf(final ClassFileEntry entry) { 112 if (!resolved) { 113 throw new IllegalStateException("Constant pool is not yet resolved; this does not make any sense"); 114 } 115 if (null == indexCache) { 116 throw new IllegalStateException("Index cache is not initialized!"); 117 } 118 final Integer entryIndex = (indexCache.get(entry)); 119 // If the entry isn't found, answer -1. Otherwise answer the entry. 120 if (entryIndex != null) { 121 return entryIndex.intValue() + 1; 122 } 123 return -1; 124 } 125 126 public int size() { 127 return entries.size(); 128 } 129 130 public ClassFileEntry get(int i) { 131 if (!resolved) { 132 throw new IllegalStateException("Constant pool is not yet resolved; this does not make any sense"); 133 } 134 return entries.get(--i); 135 } 136 137 public void resolve(final Segment segment) { 138 initialSort(); 139 sortClassPool(); 140 141 resolved = true; 142 143 entries.forEach(entry -> entry.resolve(this)); 144 others.forEach(entry -> entry.resolve(this)); 145 } 146 147 private void initialSort() { 148 final TreeSet<ClassFileEntry> inCpAll = new TreeSet<>( 149 Comparator.comparingInt(arg0 -> ((ConstantPoolEntry) arg0).getGlobalIndex())); 150 final TreeSet<ClassFileEntry> cpUtf8sNotInCpAll = new TreeSet<>( 151 Comparator.comparing(arg0 -> ((CPUTF8) arg0).underlyingString())); 152 final TreeSet<ClassFileEntry> cpClassesNotInCpAll = new TreeSet<>( 153 Comparator.comparing(arg0 -> ((CPClass) arg0).getName())); 154 155 for (ClassFileEntry entry2 : entries) { 156 final ConstantPoolEntry entry = (ConstantPoolEntry) entry2; 157 if (entry.getGlobalIndex() == -1) { 158 if (entry instanceof CPUTF8) { 159 cpUtf8sNotInCpAll.add(entry); 160 } else if (entry instanceof CPClass) { 161 cpClassesNotInCpAll.add(entry); 162 } else { 163 throw new Error("error"); 164 } 165 } else { 166 inCpAll.add(entry); 167 } 168 } 169 entries.clear(); 170 entries.addAll(inCpAll); 171 entries.addAll(cpUtf8sNotInCpAll); 172 entries.addAll(cpClassesNotInCpAll); 173 } 174 175 public List<ClassFileEntry> entries() { 176 return Collections.unmodifiableList(entries); 177 } 178 179 protected void sortClassPool() { 180 // Now that everything has been resolved, do one 181 // final sort of the class pool. This fixes up 182 // references to objects which need to be at the 183 // start of the class pool 184 185 final List<ClassFileEntry> startOfPool = new ArrayList<>(entries.size()); 186 final List<ClassFileEntry> finalSort = new ArrayList<>(entries.size()); 187 188 for (ClassFileEntry entry : entries) { 189 if (mustStartClassPool.contains(entry)) { 190 startOfPool.add(entry); 191 } else { 192 finalSort.add(entry); 193 } 194 } 195 196 // copy over and rebuild the cache 197 // 198 indexCache = new HashMap<>(entries.size()); 199 int index = 0; 200 201 entries.clear(); 202 203 for (ClassFileEntry entry : startOfPool) { 204 indexCache.put(entry, Integer.valueOf(index)); 205 206 if (entry instanceof CPLong || entry instanceof CPDouble) { 207 entries.add(entry); // these get 2 slots because of their size 208 entries.add(entry); 209 index += 2; 210 } else { 211 entries.add(entry); 212 index += 1; 213 } 214 } 215 216 for (ClassFileEntry entry : finalSort) { 217 indexCache.put(entry, Integer.valueOf(index)); 218 219 if (entry instanceof CPLong || entry instanceof CPDouble) { 220 entries.add(entry); // these get 2 slots because of their size 221 entries.add(entry); 222 index += 2; 223 } else { 224 entries.add(entry); 225 index += 1; 226 } 227 } 228 229 } 230 231 public ClassFileEntry addWithNestedEntries(final ClassFileEntry entry) { 232 add(entry); 233 for (ClassFileEntry nestedEntry : entry.getNestedClassFileEntries()) { 234 addWithNestedEntries(nestedEntry); 235 } 236 return entry; 237 } 238}