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}