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