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