View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   https://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.commons.compress.harmony.pack200;
20  
21  import java.io.IOException;
22  import java.io.OutputStream;
23  import java.util.ArrayList;
24  import java.util.Arrays;
25  import java.util.HashMap;
26  import java.util.List;
27  import java.util.Map;
28  
29  import org.objectweb.asm.Attribute;
30  
31  /**
32   * Attribute Definition bands define how any unknown attributes should be read by the decompressor.
33   */
34  public class AttributeDefinitionBands extends BandSet {
35  
36      public static class AttributeDefinition {
37  
38          public int index;
39          public int contextType;
40          public CPUTF8 name;
41          public CPUTF8 layout;
42  
43          public AttributeDefinition(final int index, final int contextType, final CPUTF8 name, final CPUTF8 layout) {
44              this.index = index;
45              this.contextType = contextType;
46              this.name = name;
47              this.layout = layout;
48          }
49  
50      }
51  
52      /**
53       * {@value}
54       */
55      public static final int CONTEXT_CLASS = 0;
56  
57      /**
58       * {@value}
59       */
60      public static final int CONTEXT_CODE = 3;
61  
62      /**
63       * {@value}
64       */
65      public static final int CONTEXT_FIELD = 1;
66  
67      /**
68       * {@value}
69       */
70      public static final int CONTEXT_METHOD = 2;
71  
72      private final List<AttributeDefinition> classAttributeLayouts = new ArrayList<>();
73      private final List<AttributeDefinition> methodAttributeLayouts = new ArrayList<>();
74      private final List<AttributeDefinition> fieldAttributeLayouts = new ArrayList<>();
75  
76      private final List<AttributeDefinition> codeAttributeLayouts = new ArrayList<>();
77  
78      private final List<AttributeDefinition> attributeDefinitions = new ArrayList<>();
79      private final CpBands cpBands;
80  
81      private final Segment segment;
82  
83      public AttributeDefinitionBands(final Segment segment, final int effort, final Attribute[] attributePrototypes) {
84          super(effort, segment.getSegmentHeader());
85          this.cpBands = segment.getCpBands();
86          this.segment = segment;
87          final Map<String, String> classLayouts = new HashMap<>();
88          final Map<String, String> methodLayouts = new HashMap<>();
89          final Map<String, String> fieldLayouts = new HashMap<>();
90          final Map<String, String> codeLayouts = new HashMap<>();
91  
92          for (final Attribute attributePrototype : attributePrototypes) {
93              final NewAttribute newAttribute = (NewAttribute) attributePrototype;
94              if (!(newAttribute instanceof NewAttribute.ErrorAttribute) && !(newAttribute instanceof NewAttribute.PassAttribute)
95                      && !(newAttribute instanceof NewAttribute.StripAttribute)) {
96                  if (newAttribute.isContextClass()) {
97                      classLayouts.put(newAttribute.type, newAttribute.getLayout());
98                  }
99                  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 }