    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)
    093                && !(newAttribute instanceof NewAttribute.PassAttribute)
    094                && !(newAttribute instanceof NewAttribute.StripAttribute)) {
    095                if (newAttribute.isContextClass()) {
    096                    classLayouts.put(newAttribute.type, newAttribute.getLayout());
    097                }
    098                if (newAttribute.isContextMethod()) {
    099                    methodLayouts.put(newAttribute.type, newAttribute.getLayout());
    100                }
    101                if (newAttribute.isContextField()) {
    102                    fieldLayouts.put(newAttribute.type, newAttribute.getLayout());
    103                }
    104                if (newAttribute.isContextCode()) {
    105                    codeLayouts.put(newAttribute.type, newAttribute.getLayout());
    106                }
    107            }
    108        }
    109        if (classLayouts.size() > 7) {
    110            segmentHeader.setHave_class_flags_hi(true);
    111        }
    112        if (methodLayouts.size() > 6) {
    113            segmentHeader.setHave_method_flags_hi(true);
    114        }
    115        if (fieldLayouts.size() > 10) {
    116            segmentHeader.setHave_field_flags_hi(true);
    117        }
    118        if (codeLayouts.size() > 15) {
    119            segmentHeader.setHave_code_flags_hi(true);
    120        }
    121        int[] availableClassIndices = {25, 26, 27, 28, 29, 30, 31};
    122        if (classLayouts.size() > 7) {
    123            availableClassIndices = addHighIndices(availableClassIndices);
    124        }
    125        addAttributeDefinitions(classLayouts, availableClassIndices, CONTEXT_CLASS);
    126        int[] availableMethodIndices = {26, 27, 28, 29, 30, 31};
    127        if (methodAttributeLayouts.size() > 6) {
    128            availableMethodIndices = addHighIndices(availableMethodIndices);
    129        }
    130        addAttributeDefinitions(methodLayouts, availableMethodIndices, CONTEXT_METHOD);
    131        int[] availableFieldIndices = {18, 23, 24, 25, 26, 27, 28, 29, 30, 31};
    132        if (fieldAttributeLayouts.size() > 10) {
    133            availableFieldIndices = addHighIndices(availableFieldIndices);
    134        }
    135        addAttributeDefinitions(fieldLayouts, availableFieldIndices, CONTEXT_FIELD);
    136        int[] availableCodeIndices = {17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
    137        if (codeAttributeLayouts.size() > 15) {
    138            availableCodeIndices = addHighIndices(availableCodeIndices);
    139        }
    140        addAttributeDefinitions(codeLayouts, availableCodeIndices, CONTEXT_CODE);
    141    }
    142
    143    private void addAttributeDefinitions(final Map<String, String> layoutMap, final int[] availableIndices, final int contextType) {
    144        final int i = 0;
    145        layoutMap.forEach((name, layout) -> {
    146            final int index = availableIndices[i];
    147            final AttributeDefinition definition = new AttributeDefinition(index, contextType, cpBands.getCPUtf8(name), cpBands.getCPUtf8(layout));
    148            attributeDefinitions.add(definition);
    149            switch (contextType) {
    150            case CONTEXT_CLASS:
    151                classAttributeLayouts.add(definition);
    152                break;
    153            case CONTEXT_METHOD:
    154                methodAttributeLayouts.add(definition);
    155                break;
    156            case CONTEXT_FIELD:
    157                fieldAttributeLayouts.add(definition);
    158                break;
    159            case CONTEXT_CODE:
    160                codeAttributeLayouts.add(definition);
    161            }
    162        });
    163    }
    164
    165    private int[] addHighIndices(final int[] availableIndices) {
    166        final int[] temp = Arrays.copyOf(availableIndices, availableIndices.length + 32);
    167        int j = 32;
    168        for (int i = availableIndices.length; i < temp.length; i++) {
    169            temp[i] = j;
    170            j++;
    171        }
    172        return temp;
    173    }
    174
    175    private void addSyntheticDefinitions() {
    176        final boolean anySytheticClasses = segment.getClassBands().isAnySyntheticClasses();
    177        final boolean anySyntheticMethods = segment.getClassBands().isAnySyntheticMethods();
    178        final boolean anySyntheticFields = segment.getClassBands().isAnySyntheticFields();
    179        if (anySytheticClasses || anySyntheticMethods || anySyntheticFields) {
    180            final CPUTF8 syntheticUTF = cpBands.getCPUtf8("Synthetic");
    181            final CPUTF8 emptyUTF = cpBands.getCPUtf8("");
    182            if (anySytheticClasses) {
    183                attributeDefinitions.add(new AttributeDefinition(12, CONTEXT_CLASS, syntheticUTF, emptyUTF));
    184            }
    185            if (anySyntheticMethods) {
    186                attributeDefinitions.add(new AttributeDefinition(12, CONTEXT_METHOD, syntheticUTF, emptyUTF));
    187            }
    188            if (anySyntheticFields) {
    189                attributeDefinitions.add(new AttributeDefinition(12, CONTEXT_FIELD, syntheticUTF, emptyUTF));
    190            }
    191        }
    192    }
    193
    194    /**
    195     * All input classes for the segment have now been read in, so this method is called so that this class can
    196     * calculate/complete anything it could not do while classes were being read.
    197     */
    198    public void finaliseBands() {
    199        addSyntheticDefinitions();
    200        segmentHeader.setAttribute_definition_count(attributeDefinitions.size());
    201    }
    202
    203    public List<AttributeDefinition> getClassAttributeLayouts() {
    204        return classAttributeLayouts;
    205    }
    206
    207    public List<AttributeDefinition> getCodeAttributeLayouts() {
    208        return codeAttributeLayouts;
    209    }
    210
    211    public List<AttributeDefinition> getFieldAttributeLayouts() {
    212        return fieldAttributeLayouts;
    213    }
    214
    215    public List<AttributeDefinition> getMethodAttributeLayouts() {
    216        return methodAttributeLayouts;
    217    }
    218
    219    @Override
    220    public void pack(final OutputStream out) throws IOException, Pack200Exception {
    221        PackingUtils.log("Writing attribute definition bands...");
    222        final int[] attributeDefinitionHeader = new int[attributeDefinitions.size()];
    223        final int[] attributeDefinitionName = new int[attributeDefinitions.size()];
    224        final int[] attributeDefinitionLayout = new int[attributeDefinitions.size()];
    225        for (int i = 0; i < attributeDefinitionLayout.length; i++) {
    226            final AttributeDefinition def = attributeDefinitions.get(i);
    227            attributeDefinitionHeader[i] = def.contextType | def.index + 1 << 2;
    228            attributeDefinitionName[i] = def.name.getIndex();
    229            attributeDefinitionLayout[i] = def.layout.getIndex();
    230        }
    231
    232        byte[] encodedBand = encodeBandInt("attributeDefinitionHeader", attributeDefinitionHeader, Codec.BYTE1);
    233        out.write(encodedBand);
    234        PackingUtils.log("Wrote " + encodedBand.length + " bytes from attributeDefinitionHeader["
    235            + attributeDefinitionHeader.length + "]");
    236
    237        encodedBand = encodeBandInt("attributeDefinitionName", attributeDefinitionName, Codec.UNSIGNED5);
    238        out.write(encodedBand);
    239        PackingUtils.log("Wrote " + encodedBand.length + " bytes from attributeDefinitionName["
    240            + attributeDefinitionName.length + "]");
    241
    242        encodedBand = encodeBandInt("attributeDefinitionLayout", attributeDefinitionLayout, Codec.UNSIGNED5);
    243        out.write(encodedBand);
    244        PackingUtils.log("Wrote " + encodedBand.length + " bytes from attributeDefinitionLayout["
    245            + attributeDefinitionLayout.length + "]");
    246    }
    247}