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.HashMap; 023import java.util.HashSet; 024import java.util.List; 025import java.util.Map; 026import java.util.TreeSet; 027 028import org.apache.commons.compress.harmony.unpack200.Segment; 029 030/** 031 * The Class constant pool 032 */ 033public class ClassConstantPool { 034 035 protected HashSet entriesContainsSet = new HashSet(); 036 protected HashSet othersContainsSet = new HashSet(); 037 038 private final HashSet mustStartClassPool = new HashSet(); 039 040 protected Map indexCache; 041 042 private final List others = new ArrayList(500); 043 private final List entries = new ArrayList(500); 044 045 private boolean resolved; 046 047 public ClassFileEntry add(final ClassFileEntry entry) { 048 if (entry instanceof ByteCode) { 049 return null; 050 } 051 if (entry instanceof ConstantPoolEntry) { 052 if (entriesContainsSet.add(entry)) { 053 entries.add(entry); 054 } 055 } else if (othersContainsSet.add(entry)) { 056 others.add(entry); 057 } 058 059 return entry; 060 } 061 062 public void addNestedEntries() { 063 boolean added = true; 064 065 // initial assignment 066 final ArrayList parents = new ArrayList(512); 067 final ArrayList children = new ArrayList(512); 068 069 // adding old entries 070 parents.addAll(entries); 071 parents.addAll(others); 072 073 // while there any parents to traverse and at least one change in target 074 // storage was made 075 while (added || parents.size() > 0) { 076 077 children.clear(); 078 079 final int entriesOriginalSize = entries.size(); 080 final int othersOriginalSize = others.size(); 081 082 // get the parents' children and add them to buffer 083 // concurrently add parents to target storage 084 for (int indexParents = 0; indexParents < parents.size(); indexParents++) { 085 final ClassFileEntry entry = (ClassFileEntry) parents.get(indexParents); 086 087 // traverse children 088 final ClassFileEntry[] entryChildren = entry.getNestedClassFileEntries(); 089 children.addAll(Arrays.asList(entryChildren)); 090 091 final boolean isAtStart = (entry instanceof ByteCode) && ((ByteCode) entry).nestedMustStartClassPool(); 092 093 if (isAtStart) { 094 mustStartClassPool.addAll(Arrays.asList(entryChildren)); 095 } 096 097 // add parent 098 add(entry); 099 } 100 101 added = !(entries.size() == entriesOriginalSize && others.size() == othersOriginalSize); 102 103 // parents are not needed anymore 104 // children now become parents 105 parents.clear(); 106 parents.addAll(children); 107 } 108 } 109 110 public int indexOf(final ClassFileEntry entry) { 111 if (!resolved) { 112 throw new IllegalStateException("Constant pool is not yet resolved; this does not make any sense"); 113 } 114 if (null == indexCache) { 115 throw new IllegalStateException("Index cache is not initialized!"); 116 } 117 final Integer entryIndex = ((Integer) indexCache.get(entry)); 118 // If the entry isn't found, answer -1. Otherwise answer the entry. 119 if (entryIndex != null) { 120 return entryIndex.intValue() + 1; 121 } 122 return -1; 123 } 124 125 public int size() { 126 return entries.size(); 127 } 128 129 public ClassFileEntry get(int i) { 130 if (!resolved) { 131 throw new IllegalStateException("Constant pool is not yet resolved; this does not make any sense"); 132 } 133 return (ClassFileEntry) entries.get(--i); 134 } 135 136 public void resolve(final Segment segment) { 137 initialSort(); 138 sortClassPool(); 139 140 resolved = true; 141 142 for (int it = 0; it < entries.size(); it++) { 143 final ClassFileEntry entry = (ClassFileEntry) entries.get(it); 144 entry.resolve(this); 145 } 146 147 for (int it = 0; it < others.size(); it++) { 148 final ClassFileEntry entry = (ClassFileEntry) others.get(it); 149 entry.resolve(this); 150 } 151 152 } 153 154 private void initialSort() { 155 final TreeSet inCpAll = new TreeSet( 156 (arg0, arg1) -> ((ConstantPoolEntry) arg0).getGlobalIndex() - ((ConstantPoolEntry) arg1).getGlobalIndex()); 157 final TreeSet cpUtf8sNotInCpAll = new TreeSet( 158 (arg0, arg1) -> ((CPUTF8) arg0).underlyingString().compareTo(((CPUTF8) arg1).underlyingString())); 159 final TreeSet cpClassesNotInCpAll = new TreeSet( 160 (arg0, arg1) -> ((CPClass) arg0).getName().compareTo(((CPClass) arg1).getName())); 161 162 for (int index = 0; index < entries.size(); index++) { 163 final ConstantPoolEntry entry = (ConstantPoolEntry) entries.get(index); 164 if (entry.getGlobalIndex() == -1) { 165 if (entry instanceof CPUTF8) { 166 cpUtf8sNotInCpAll.add(entry); 167 } else if (entry instanceof CPClass) { 168 cpClassesNotInCpAll.add(entry); 169 } else { 170 throw new Error("error"); 171 } 172 } else { 173 inCpAll.add(entry); 174 } 175 } 176 entries.clear(); 177 entries.addAll(inCpAll); 178 entries.addAll(cpUtf8sNotInCpAll); 179 entries.addAll(cpClassesNotInCpAll); 180 } 181 182 public List entries() { 183 return Collections.unmodifiableList(entries); 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 ArrayList startOfPool = new ArrayList(entries.size()); 193 final ArrayList finalSort = new ArrayList(entries.size()); 194 195 for (int i = 0; i < entries.size(); i++) { 196 final ClassFileEntry nextEntry = (ClassFileEntry) entries.get(i); 197 if (mustStartClassPool.contains(nextEntry)) { 198 startOfPool.add(nextEntry); 199 } else { 200 finalSort.add(nextEntry); 201 } 202 } 203 204 // copy over and rebuild the cache 205 // 206 indexCache = new HashMap(entries.size()); 207 int index = 0; 208 209 entries.clear(); 210 211 for (int itIndex = 0; itIndex < startOfPool.size(); itIndex++) { 212 final ClassFileEntry entry = (ClassFileEntry) startOfPool.get(itIndex); 213 indexCache.put(entry, Integer.valueOf(index)); 214 215 if (entry instanceof CPLong || entry instanceof CPDouble) { 216 entries.add(entry); // these get 2 slots because of their size 217 entries.add(entry); 218 index += 2; 219 } else { 220 entries.add(entry); 221 index += 1; 222 } 223 } 224 225 for (int itFinal = 0; itFinal < finalSort.size(); itFinal++) { 226 final ClassFileEntry entry = (ClassFileEntry) finalSort.get(itFinal); 227 indexCache.put(entry, Integer.valueOf(index)); 228 229 if (entry instanceof CPLong || entry instanceof CPDouble) { 230 entries.add(entry); // these get 2 slots because of their size 231 entries.add(entry); 232 index += 2; 233 } else { 234 entries.add(entry); 235 index += 1; 236 } 237 } 238 239 } 240 241 public ClassFileEntry addWithNestedEntries(final ClassFileEntry entry) { 242 add(entry); 243 final ClassFileEntry[] nestedEntries = entry.getNestedClassFileEntries(); 244 for (int i = 0; i < nestedEntries.length; i++) { 245 addWithNestedEntries(nestedEntries[i]); 246 } 247 return entry; 248 } 249}