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}