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; 020 021import java.io.IOException; 022import java.io.InputStream; 023import java.util.ArrayList; 024import java.util.HashMap; 025import java.util.HashSet; 026import java.util.List; 027import java.util.Map; 028import java.util.Set; 029 030import org.apache.commons.compress.harmony.pack200.Codec; 031import org.apache.commons.compress.harmony.pack200.Pack200Exception; 032import org.apache.commons.compress.harmony.unpack200.bytecode.CPClass; 033import org.apache.commons.compress.harmony.unpack200.bytecode.ClassConstantPool; 034import org.apache.commons.compress.harmony.unpack200.bytecode.ClassFileEntry; 035import org.apache.commons.compress.harmony.unpack200.bytecode.ConstantPoolEntry; 036 037/** 038 * Inner Class Bands 039 */ 040public class IcBands extends BandSet { 041 042 private IcTuple[] icAll; 043 044 private final String[] cpUTF8; 045 046 private final String[] cpClass; 047 048 private Map<String, IcTuple> thisClassToTuple; 049 private Map<String, List<IcTuple>> outerClassToTuples; 050 051 /** 052 * @param segment TODO 053 */ 054 public IcBands(final Segment segment) { 055 super(segment); 056 this.cpClass = segment.getCpBands().getCpClass(); 057 this.cpUTF8 = segment.getCpBands().getCpUTF8(); 058 } 059 060 public IcTuple[] getIcTuples() { 061 return icAll; 062 } 063 064 /** 065 * Answer the relevant IcTuples for the specified className and class constant pool. 066 * 067 * @param className String name of the class X for ic_relevant(X) 068 * @param cp ClassConstantPool used to generate ic_relevant(X) 069 * @return array of IcTuple 070 */ 071 public IcTuple[] getRelevantIcTuples(final String className, final ClassConstantPool cp) { 072 final Set<IcTuple> relevantTuplesContains = new HashSet<>(); 073 final List<IcTuple> relevantTuples = new ArrayList<>(); 074 075 final List<IcTuple> relevantCandidates = outerClassToTuples.get(className); 076 if (relevantCandidates != null) { 077 for (int index = 0; index < relevantCandidates.size(); index++) { 078 final IcTuple tuple = relevantCandidates.get(index); 079 relevantTuplesContains.add(tuple); 080 relevantTuples.add(tuple); 081 } 082 } 083 084 final List<ClassFileEntry> entries = cp.entries(); 085 086 // For every class constant in both ic_this_class and cp, 087 // add it to ic_relevant. Repeat until no more 088 // changes to ic_relevant. 089 090 for (int eIndex = 0; eIndex < entries.size(); eIndex++) { 091 final ConstantPoolEntry entry = (ConstantPoolEntry) entries.get(eIndex); 092 if (entry instanceof CPClass) { 093 final CPClass clazz = (CPClass) entry; 094 final IcTuple relevant = thisClassToTuple.get(clazz.name); 095 if (relevant != null && relevantTuplesContains.add(relevant)) { 096 relevantTuples.add(relevant); 097 } 098 } 099 } 100 101 // Not part of spec: fix up by adding to relevantTuples the parents 102 // of inner classes which are themselves inner classes. 103 // i.e., I think that if Foo$Bar$Baz gets added, Foo$Bar needs to be 104 // added 105 // as well. 106 107 final List<IcTuple> tuplesToScan = new ArrayList<>(relevantTuples); 108 final List<IcTuple> tuplesToAdd = new ArrayList<>(); 109 110 while (tuplesToScan.size() > 0) { 111 112 tuplesToAdd.clear(); 113 for (int index = 0; index < tuplesToScan.size(); index++) { 114 final IcTuple aRelevantTuple = tuplesToScan.get(index); 115 final IcTuple relevant = thisClassToTuple.get(aRelevantTuple.outerClassString()); 116 if (relevant != null && !aRelevantTuple.outerIsAnonymous()) { 117 tuplesToAdd.add(relevant); 118 } 119 } 120 121 tuplesToScan.clear(); 122 for (int index = 0; index < tuplesToAdd.size(); index++) { 123 final IcTuple tuple = tuplesToAdd.get(index); 124 if (relevantTuplesContains.add(tuple)) { 125 relevantTuples.add(tuple); 126 tuplesToScan.add(tuple); 127 } 128 } 129 130 } 131 132 // End not part of the spec. Ugh. 133 134 // Now order the result as a subsequence of ic_all 135 relevantTuples.sort((arg0, arg1) -> { 136 final int index1 = arg0.getTupleIndex(); 137 final Integer index2 = Integer.valueOf(arg1.getTupleIndex()); 138 return Integer.compare(index1, index2); 139 }); 140 141 return relevantTuples.toArray(IcTuple.EMPTY_ARRAY); 142 } 143 144 /* 145 * (non-Javadoc) 146 * 147 * @see org.apache.commons.compress.harmony.unpack200.BandSet#unpack(java.io.InputStream) 148 */ 149 @Override 150 public void read(final InputStream in) throws IOException, Pack200Exception { 151 // Read IC bands 152 final int innerClassCount = header.getInnerClassCount(); 153 final int[] icThisClassInts = decodeBandInt("ic_this_class", in, Codec.UDELTA5, innerClassCount); 154 final String[] icThisClass = getReferences(icThisClassInts, cpClass); 155 final int[] icFlags = decodeBandInt("ic_flags", in, Codec.UNSIGNED5, innerClassCount); 156 final int outerClasses = SegmentUtils.countBit16(icFlags); 157 final int[] icOuterClassInts = decodeBandInt("ic_outer_class", in, Codec.DELTA5, outerClasses); 158 final String[] icOuterClass = new String[outerClasses]; 159 for (int i = 0; i < icOuterClass.length; i++) { 160 if (icOuterClassInts[i] == 0) { 161 icOuterClass[i] = null; 162 } else { 163 icOuterClass[i] = cpClass[icOuterClassInts[i] - 1]; 164 } 165 } 166 final int[] icNameInts = decodeBandInt("ic_name", in, Codec.DELTA5, outerClasses); 167 final String[] icName = new String[outerClasses]; 168 for (int i = 0; i < icName.length; i++) { 169 if (icNameInts[i] == 0) { 170 icName[i] = null; 171 } else { 172 icName[i] = cpUTF8[icNameInts[i] - 1]; 173 } 174 } 175 176 // Construct IC tuples 177 icAll = new IcTuple[icThisClass.length]; 178 int index = 0; 179 for (int i = 0; i < icThisClass.length; i++) { 180 final String icTupleC = icThisClass[i]; 181 final int icTupleF = icFlags[i]; 182 String icTupleC2 = null; 183 String icTupleN = null; 184 final int cIndex = icThisClassInts[i]; 185 int c2Index = -1; 186 int nIndex = -1; 187 if ((icFlags[i] & 1 << 16) != 0) { 188 icTupleC2 = icOuterClass[index]; 189 icTupleN = icName[index]; 190 c2Index = icOuterClassInts[index] - 1; 191 nIndex = icNameInts[index] - 1; 192 index++; 193 } 194 icAll[i] = new IcTuple(icTupleC, icTupleF, icTupleC2, icTupleN, cIndex, c2Index, nIndex, i); 195 } 196 } 197 198 @Override 199 public void unpack() throws IOException, Pack200Exception { 200 final IcTuple[] allTuples = getIcTuples(); 201 thisClassToTuple = new HashMap<>(allTuples.length); 202 outerClassToTuples = new HashMap<>(allTuples.length); 203 for (final IcTuple tuple : allTuples) { 204 205 // generate mapping thisClassString -> IcTuple 206 // presumably this relation is 1:1 207 // 208 final Object result = thisClassToTuple.put(tuple.thisClassString(), tuple); 209 if (result != null) { 210 throw new Error("Collision detected in <thisClassString, IcTuple> mapping. There are at least two inner clases with the same name."); 211 } 212 213 // generate mapping outerClassString -> IcTuple 214 // this relation is 1:M 215 216 // If it's not anon and the outer is not anon, it could be relevant 217 if (!tuple.isAnonymous() && !tuple.outerIsAnonymous() || tuple.nestedExplicitFlagSet()) { 218 219 // add tuple to corresponding bucket 220 final String key = tuple.outerClassString(); 221 outerClassToTuples.computeIfAbsent(key, k -> new ArrayList<>()).add(tuple); 222 } 223 } 224 } 225 226}