org.apache.commons.compress.harmony.unpack200.IcBands Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of commons-compress Show documentation
Show all versions of commons-compress Show documentation
Apache Commons Compress software defines an API for working with
compression and archive formats. These include: bzip2, gzip, pack200,
lzma, xz, Snappy, traditional Unix Compress, DEFLATE, DEFLATE64, LZ4,
Brotli, Zstandard and ar, cpio, jar, tar, zip, dump, 7z, arj.
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.compress.harmony.unpack200;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.compress.harmony.pack200.Codec;
import org.apache.commons.compress.harmony.pack200.Pack200Exception;
import org.apache.commons.compress.harmony.unpack200.bytecode.CPClass;
import org.apache.commons.compress.harmony.unpack200.bytecode.ClassConstantPool;
import org.apache.commons.compress.harmony.unpack200.bytecode.ClassFileEntry;
import org.apache.commons.compress.harmony.unpack200.bytecode.ConstantPoolEntry;
/**
* Inner Class Bands
*/
public class IcBands extends BandSet {
private IcTuple[] icAll;
private final String[] cpUTF8;
private final String[] cpClass;
private Map thisClassToTuple;
private Map> outerClassToTuples;
/**
* @param segment TODO
*/
public IcBands(final Segment segment) {
super(segment);
this.cpClass = segment.getCpBands().getCpClass();
this.cpUTF8 = segment.getCpBands().getCpUTF8();
}
public IcTuple[] getIcTuples() {
return icAll;
}
/**
* Answer the relevant IcTuples for the specified className and class constant pool.
*
* @param className String name of the class X for ic_relevant(X)
* @param cp ClassConstantPool used to generate ic_relevant(X)
* @return array of IcTuple
*/
public IcTuple[] getRelevantIcTuples(final String className, final ClassConstantPool cp) {
final Set relevantTuplesContains = new HashSet<>();
final List relevantTuples = new ArrayList<>();
final List relevantCandidates = outerClassToTuples.get(className);
if (relevantCandidates != null) {
for (int index = 0; index < relevantCandidates.size(); index++) {
final IcTuple tuple = relevantCandidates.get(index);
relevantTuplesContains.add(tuple);
relevantTuples.add(tuple);
}
}
final List entries = cp.entries();
// For every class constant in both ic_this_class and cp,
// add it to ic_relevant. Repeat until no more
// changes to ic_relevant.
for (int eIndex = 0; eIndex < entries.size(); eIndex++) {
final ConstantPoolEntry entry = (ConstantPoolEntry) entries.get(eIndex);
if (entry instanceof CPClass) {
final CPClass clazz = (CPClass) entry;
final IcTuple relevant = thisClassToTuple.get(clazz.name);
if (relevant != null && relevantTuplesContains.add(relevant)) {
relevantTuples.add(relevant);
}
}
}
// Not part of spec: fix up by adding to relevantTuples the parents
// of inner classes which are themselves inner classes.
// i.e., I think that if Foo$Bar$Baz gets added, Foo$Bar needs to be
// added
// as well.
final List tuplesToScan = new ArrayList<>(relevantTuples);
final List tuplesToAdd = new ArrayList<>();
while (tuplesToScan.size() > 0) {
tuplesToAdd.clear();
for (int index = 0; index < tuplesToScan.size(); index++) {
final IcTuple aRelevantTuple = tuplesToScan.get(index);
final IcTuple relevant = thisClassToTuple.get(aRelevantTuple.outerClassString());
if (relevant != null && !aRelevantTuple.outerIsAnonymous()) {
tuplesToAdd.add(relevant);
}
}
tuplesToScan.clear();
for (int index = 0; index < tuplesToAdd.size(); index++) {
final IcTuple tuple = tuplesToAdd.get(index);
if (relevantTuplesContains.add(tuple)) {
relevantTuples.add(tuple);
tuplesToScan.add(tuple);
}
}
}
// End not part of the spec. Ugh.
// Now order the result as a subsequence of ic_all
relevantTuples.sort((arg0, arg1) -> {
final int index1 = arg0.getTupleIndex();
final Integer index2 = Integer.valueOf(arg1.getTupleIndex());
return Integer.compare(index1, index2);
});
return relevantTuples.toArray(IcTuple.EMPTY_ARRAY);
}
/*
* (non-Javadoc)
*
* @see org.apache.commons.compress.harmony.unpack200.BandSet#unpack(java.io.InputStream)
*/
@Override
public void read(final InputStream in) throws IOException, Pack200Exception {
// Read IC bands
final int innerClassCount = header.getInnerClassCount();
final int[] icThisClassInts = decodeBandInt("ic_this_class", in, Codec.UDELTA5, innerClassCount);
final String[] icThisClass = getReferences(icThisClassInts, cpClass);
final int[] icFlags = decodeBandInt("ic_flags", in, Codec.UNSIGNED5, innerClassCount);
final int outerClasses = SegmentUtils.countBit16(icFlags);
final int[] icOuterClassInts = decodeBandInt("ic_outer_class", in, Codec.DELTA5, outerClasses);
final String[] icOuterClass = new String[outerClasses];
for (int i = 0; i < icOuterClass.length; i++) {
if (icOuterClassInts[i] == 0) {
icOuterClass[i] = null;
} else {
icOuterClass[i] = cpClass[icOuterClassInts[i] - 1];
}
}
final int[] icNameInts = decodeBandInt("ic_name", in, Codec.DELTA5, outerClasses);
final String[] icName = new String[outerClasses];
for (int i = 0; i < icName.length; i++) {
if (icNameInts[i] == 0) {
icName[i] = null;
} else {
icName[i] = cpUTF8[icNameInts[i] - 1];
}
}
// Construct IC tuples
icAll = new IcTuple[icThisClass.length];
int index = 0;
for (int i = 0; i < icThisClass.length; i++) {
final String icTupleC = icThisClass[i];
final int icTupleF = icFlags[i];
String icTupleC2 = null;
String icTupleN = null;
final int cIndex = icThisClassInts[i];
int c2Index = -1;
int nIndex = -1;
if ((icFlags[i] & 1 << 16) != 0) {
icTupleC2 = icOuterClass[index];
icTupleN = icName[index];
c2Index = icOuterClassInts[index] - 1;
nIndex = icNameInts[index] - 1;
index++;
}
icAll[i] = new IcTuple(icTupleC, icTupleF, icTupleC2, icTupleN, cIndex, c2Index, nIndex, i);
}
}
@Override
public void unpack() throws IOException, Pack200Exception {
final IcTuple[] allTuples = getIcTuples();
thisClassToTuple = new HashMap<>(allTuples.length);
outerClassToTuples = new HashMap<>(allTuples.length);
for (final IcTuple tuple : allTuples) {
// generate mapping thisClassString -> IcTuple
// presumably this relation is 1:1
//
final Object result = thisClassToTuple.put(tuple.thisClassString(), tuple);
if (result != null) {
throw new Error("Collision detected in mapping. " + "There are at least two inner clases with the same name.");
}
// generate mapping outerClassString -> IcTuple
// this relation is 1:M
// If it's not anon and the outer is not anon, it could be relevant
if (!tuple.isAnonymous() && !tuple.outerIsAnonymous() || tuple.nestedExplicitFlagSet()) {
// add tuple to corresponding bucket
final String key = tuple.outerClassString();
outerClassToTuples.computeIfAbsent(key, k -> new ArrayList<>()).add(tuple);
}
}
}
}