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