    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.pack200;
    018
    019import java.io.IOException;
    020import java.io.OutputStream;
    021import java.util.ArrayList;
    022import java.util.Arrays;
    023import java.util.HashMap;
    024import java.util.List;
    025import java.util.Map;
    026
    027import org.objectweb.asm.Attribute;
    028
    029/**
    030 * Attribute Definition bands define how any unknown attributes should be read by the decompressor.
    031 */
    032public class AttributeDefinitionBands extends BandSet {
    033
    034    public static class AttributeDefinition {
    035
    036        public int index;
    037        public int contextType;
    038        public CPUTF8 name;
    039        public CPUTF8 layout;
    040
    041        public AttributeDefinition(final int index, final int contextType, final CPUTF8 name, final CPUTF8 layout) {
    042            this.index = index;
    043            this.contextType = contextType;
    044            this.name = name;
    045            this.layout = layout;
    046        }
    047
    048    }
    049    public static final int CONTEXT_CLASS = 0;
    050    public static final int CONTEXT_CODE = 3;
    051    public static final int CONTEXT_FIELD = 1;
    052
    053    public static final int CONTEXT_METHOD = 2;
    054    private final List<AttributeDefinition> classAttributeLayouts = new ArrayList<>();
    055    private final List<AttributeDefinition> methodAttributeLayouts = new ArrayList<>();
    056    private final List<AttributeDefinition> fieldAttributeLayouts = new ArrayList<>();
    057
    058    private final List<AttributeDefinition> codeAttributeLayouts = new ArrayList<>();
    059
    060    private final List<AttributeDefinition> attributeDefinitions = new ArrayList<>();
    061    private final CpBands cpBands;
    062
    063    private final Segment segment;
    064
    065    public AttributeDefinitionBands(final Segment segment, final int effort, final Attribute[] attributePrototypes) {
    066        super(effort, segment.getSegmentHeader());
    067        this.cpBands = segment.getCpBands();
    068        this.segment = segment;
    069        final Map<String, String> classLayouts = new HashMap<>();
    070        final Map<String, String> methodLayouts = new HashMap<>();
    071        final Map<String, String> fieldLayouts = new HashMap<>();
    072        final Map<String, String> codeLayouts = new HashMap<>();
    073
    074        for (final Attribute attributePrototype : attributePrototypes) {
    075            final NewAttribute newAttribute = (NewAttribute) attributePrototype;
    076            if (!(newAttribute instanceof NewAttribute.ErrorAttribute)
    077                && !(newAttribute instanceof NewAttribute.PassAttribute)
    078                && !(newAttribute instanceof NewAttribute.StripAttribute)) {
    079                if (newAttribute.isContextClass()) {
    080                    classLayouts.put(newAttribute.type, newAttribute.getLayout());
    081                }
    082                if (newAttribute.isContextMethod()) {
    083                    methodLayouts.put(newAttribute.type, newAttribute.getLayout());
    084                }
    085                if (newAttribute.isContextField()) {
    086                    fieldLayouts.put(newAttribute.type, newAttribute.getLayout());
    087                }
    088                if (newAttribute.isContextCode()) {
    089                    codeLayouts.put(newAttribute.type, newAttribute.getLayout());
    090                }
    091            }
    092        }
    093        if (classLayouts.size() > 7) {
    094            segmentHeader.setHave_class_flags_hi(true);
    095        }
    096        if (methodLayouts.size() > 6) {
    097            segmentHeader.setHave_method_flags_hi(true);
    098        }
    099        if (fieldLayouts.size() > 10) {
    100            segmentHeader.setHave_field_flags_hi(true);
    101        }
    102        if (codeLayouts.size() > 15) {
    103            segmentHeader.setHave_code_flags_hi(true);
    104        }
    105        int[] availableClassIndices = {25, 26, 27, 28, 29, 30, 31};
    106        if (classLayouts.size() > 7) {
    107            availableClassIndices = addHighIndices(availableClassIndices);
    108        }
    109        addAttributeDefinitions(classLayouts, availableClassIndices, CONTEXT_CLASS);
    110        int[] availableMethodIndices = {26, 27, 28, 29, 30, 31};
    111        if (methodAttributeLayouts.size() > 6) {
    112            availableMethodIndices = addHighIndices(availableMethodIndices);
    113        }
    114        addAttributeDefinitions(methodLayouts, availableMethodIndices, CONTEXT_METHOD);
    115        int[] availableFieldIndices = {18, 23, 24, 25, 26, 27, 28, 29, 30, 31};
    116        if (fieldAttributeLayouts.size() > 10) {
    117            availableFieldIndices = addHighIndices(availableFieldIndices);
    118        }
    119        addAttributeDefinitions(fieldLayouts, availableFieldIndices, CONTEXT_FIELD);
    120        int[] availableCodeIndices = {17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
    121        if (codeAttributeLayouts.size() > 15) {
    122            availableCodeIndices = addHighIndices(availableCodeIndices);
    123        }
    124        addAttributeDefinitions(codeLayouts, availableCodeIndices, CONTEXT_CODE);
    125    }
    126
    127    private void addAttributeDefinitions(final Map<String, String> layoutMap, final int[] availableIndices, final int contextType) {
    128        final int i = 0;
    129        layoutMap.forEach((name, layout) -> {
    130            final int index = availableIndices[i];
    131            final AttributeDefinition definition = new AttributeDefinition(index, contextType, cpBands.getCPUtf8(name), cpBands.getCPUtf8(layout));
    132            attributeDefinitions.add(definition);
    133            switch (contextType) {
    134            case CONTEXT_CLASS:
    135                classAttributeLayouts.add(definition);
    136                break;
    137            case CONTEXT_METHOD:
    138                methodAttributeLayouts.add(definition);
    139                break;
    140            case CONTEXT_FIELD:
    141                fieldAttributeLayouts.add(definition);
    142                break;
    143            case CONTEXT_CODE:
    144                codeAttributeLayouts.add(definition);
    145            }
    146        });
    147    }
    148
    149    private int[] addHighIndices(final int[] availableIndices) {
    150        final int[] temp = Arrays.copyOf(availableIndices, availableIndices.length + 32);
    151        int j = 32;
    152        for (int i = availableIndices.length; i < temp.length; i++) {
    153            temp[i] = j;
    154            j++;
    155        }
    156        return temp;
    157    }
    158
    159    private void addSyntheticDefinitions() {
    160        final boolean anySytheticClasses = segment.getClassBands().isAnySyntheticClasses();
    161        final boolean anySyntheticMethods = segment.getClassBands().isAnySyntheticMethods();
    162        final boolean anySyntheticFields = segment.getClassBands().isAnySyntheticFields();
    163        if (anySytheticClasses || anySyntheticMethods || anySyntheticFields) {
    164            final CPUTF8 syntheticUTF = cpBands.getCPUtf8("Synthetic");
    165            final CPUTF8 emptyUTF = cpBands.getCPUtf8("");
    166            if (anySytheticClasses) {
    167                attributeDefinitions.add(new AttributeDefinition(12, CONTEXT_CLASS, syntheticUTF, emptyUTF));
    168            }
    169            if (anySyntheticMethods) {
    170                attributeDefinitions.add(new AttributeDefinition(12, CONTEXT_METHOD, syntheticUTF, emptyUTF));
    171            }
    172            if (anySyntheticFields) {
    173                attributeDefinitions.add(new AttributeDefinition(12, CONTEXT_FIELD, syntheticUTF, emptyUTF));
    174            }
    175        }
    176    }
    177
    178    /**
    179     * All input classes for the segment have now been read in, so this method is called so that this class can
    180     * calculate/complete anything it could not do while classes were being read.
    181     */
    182    public void finaliseBands() {
    183        addSyntheticDefinitions();
    184        segmentHeader.setAttribute_definition_count(attributeDefinitions.size());
    185    }
    186
    187    public List<AttributeDefinition> getClassAttributeLayouts() {
    188        return classAttributeLayouts;
    189    }
    190
    191    public List<AttributeDefinition> getCodeAttributeLayouts() {
    192        return codeAttributeLayouts;
    193    }
    194
    195    public List<AttributeDefinition> getFieldAttributeLayouts() {
    196        return fieldAttributeLayouts;
    197    }
    198
    199    public List<AttributeDefinition> getMethodAttributeLayouts() {
    200        return methodAttributeLayouts;
    201    }
    202
    203    @Override
    204    public void pack(final OutputStream out) throws IOException, Pack200Exception {
    205        PackingUtils.log("Writing attribute definition bands...");
    206        final int[] attributeDefinitionHeader = new int[attributeDefinitions.size()];
    207        final int[] attributeDefinitionName = new int[attributeDefinitions.size()];
    208        final int[] attributeDefinitionLayout = new int[attributeDefinitions.size()];
    209        for (int i = 0; i < attributeDefinitionLayout.length; i++) {
    210            final AttributeDefinition def = attributeDefinitions.get(i);
    211            attributeDefinitionHeader[i] = def.contextType | (def.index + 1 << 2);
    212            attributeDefinitionName[i] = def.name.getIndex();
    213            attributeDefinitionLayout[i] = def.layout.getIndex();
    214        }
    215
    216        byte[] encodedBand = encodeBandInt("attributeDefinitionHeader", attributeDefinitionHeader, Codec.BYTE1);
    217        out.write(encodedBand);
    218        PackingUtils.log("Wrote " + encodedBand.length + " bytes from attributeDefinitionHeader["
    219            + attributeDefinitionHeader.length + "]");
    220
    221        encodedBand = encodeBandInt("attributeDefinitionName", attributeDefinitionName, Codec.UNSIGNED5);
    222        out.write(encodedBand);
    223        PackingUtils.log("Wrote " + encodedBand.length + " bytes from attributeDefinitionName["
    224            + attributeDefinitionName.length + "]");
    225
    226        encodedBand = encodeBandInt("attributeDefinitionLayout", attributeDefinitionLayout, Codec.UNSIGNED5);
    227        out.write(encodedBand);
    228        PackingUtils.log("Wrote " + encodedBand.length + " bytes from attributeDefinitionLayout["
    229            + attributeDefinitionLayout.length + "]");
    230    }
    231}