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