View Javadoc
1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one or more
3    *  contributor license agreements.  See the NOTICE file distributed with
4    *  this work for additional information regarding copyright ownership.
5    *  The ASF licenses this file to You under the Apache License, Version 2.0
6    *  (the "License"); you may not use this file except in compliance with
7    *  the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   *  Unless required by applicable law or agreed to in writing, software
12   *  distributed under the License is distributed on an "AS IS" BASIS,
13   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   *  See the License for the specific language governing permissions and
15   *  limitations under the License.
16   */
17  package org.apache.commons.compress.harmony.pack200;
18  
19  import java.io.IOException;
20  import java.io.OutputStream;
21  import java.util.ArrayList;
22  import java.util.Arrays;
23  import java.util.HashMap;
24  import java.util.List;
25  import java.util.Map;
26  
27  import org.objectweb.asm.Attribute;
28  
29  /**
30   * Attribute Definition bands define how any unknown attributes should be read by the decompressor.
31   */
32  public class AttributeDefinitionBands extends BandSet {
33  
34      public static class AttributeDefinition {
35  
36          public int index;
37          public int contextType;
38          public CPUTF8 name;
39          public CPUTF8 layout;
40  
41          public AttributeDefinition(final int index, final int contextType, final CPUTF8 name, final CPUTF8 layout) {
42              this.index = index;
43              this.contextType = contextType;
44              this.name = name;
45              this.layout = layout;
46          }
47  
48      }
49  
50      /**
51       * {@value}
52       */
53      public static final int CONTEXT_CLASS = 0;
54  
55      /**
56       * {@value}
57       */
58      public static final int CONTEXT_CODE = 3;
59  
60      /**
61       * {@value}
62       */
63      public static final int CONTEXT_FIELD = 1;
64  
65      /**
66       * {@value}
67       */
68      public static final int CONTEXT_METHOD = 2;
69  
70      private final List<AttributeDefinition> classAttributeLayouts = new ArrayList<>();
71      private final List<AttributeDefinition> methodAttributeLayouts = new ArrayList<>();
72      private final List<AttributeDefinition> fieldAttributeLayouts = new ArrayList<>();
73  
74      private final List<AttributeDefinition> codeAttributeLayouts = new ArrayList<>();
75  
76      private final List<AttributeDefinition> attributeDefinitions = new ArrayList<>();
77      private final CpBands cpBands;
78  
79      private final Segment segment;
80  
81      public AttributeDefinitionBands(final Segment segment, final int effort, final Attribute[] attributePrototypes) {
82          super(effort, segment.getSegmentHeader());
83          this.cpBands = segment.getCpBands();
84          this.segment = segment;
85          final Map<String, String> classLayouts = new HashMap<>();
86          final Map<String, String> methodLayouts = new HashMap<>();
87          final Map<String, String> fieldLayouts = new HashMap<>();
88          final Map<String, String> codeLayouts = new HashMap<>();
89  
90          for (final Attribute attributePrototype : attributePrototypes) {
91              final NewAttribute newAttribute = (NewAttribute) attributePrototype;
92              if (!(newAttribute instanceof NewAttribute.ErrorAttribute) && !(newAttribute instanceof NewAttribute.PassAttribute)
93                      && !(newAttribute instanceof NewAttribute.StripAttribute)) {
94                  if (newAttribute.isContextClass()) {
95                      classLayouts.put(newAttribute.type, newAttribute.getLayout());
96                  }
97                  if (newAttribute.isContextMethod()) {
98                      methodLayouts.put(newAttribute.type, newAttribute.getLayout());
99                  }
100                 if (newAttribute.isContextField()) {
101                     fieldLayouts.put(newAttribute.type, newAttribute.getLayout());
102                 }
103                 if (newAttribute.isContextCode()) {
104                     codeLayouts.put(newAttribute.type, newAttribute.getLayout());
105                 }
106             }
107         }
108         if (classLayouts.size() > 7) {
109             segmentHeader.setHave_class_flags_hi(true);
110         }
111         if (methodLayouts.size() > 6) {
112             segmentHeader.setHave_method_flags_hi(true);
113         }
114         if (fieldLayouts.size() > 10) {
115             segmentHeader.setHave_field_flags_hi(true);
116         }
117         if (codeLayouts.size() > 15) {
118             segmentHeader.setHave_code_flags_hi(true);
119         }
120         int[] availableClassIndices = { 25, 26, 27, 28, 29, 30, 31 };
121         if (classLayouts.size() > 7) {
122             availableClassIndices = addHighIndices(availableClassIndices);
123         }
124         addAttributeDefinitions(classLayouts, availableClassIndices, CONTEXT_CLASS);
125         int[] availableMethodIndices = { 26, 27, 28, 29, 30, 31 };
126         if (methodAttributeLayouts.size() > 6) {
127             availableMethodIndices = addHighIndices(availableMethodIndices);
128         }
129         addAttributeDefinitions(methodLayouts, availableMethodIndices, CONTEXT_METHOD);
130         int[] availableFieldIndices = { 18, 23, 24, 25, 26, 27, 28, 29, 30, 31 };
131         if (fieldAttributeLayouts.size() > 10) {
132             availableFieldIndices = addHighIndices(availableFieldIndices);
133         }
134         addAttributeDefinitions(fieldLayouts, availableFieldIndices, CONTEXT_FIELD);
135         int[] availableCodeIndices = { 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 };
136         if (codeAttributeLayouts.size() > 15) {
137             availableCodeIndices = addHighIndices(availableCodeIndices);
138         }
139         addAttributeDefinitions(codeLayouts, availableCodeIndices, CONTEXT_CODE);
140     }
141 
142     private void addAttributeDefinitions(final Map<String, String> layoutMap, final int[] availableIndices, final int contextType) {
143         final int i = 0;
144         layoutMap.forEach((name, layout) -> {
145             final int index = availableIndices[i];
146             final AttributeDefinition definition = new AttributeDefinition(index, contextType, cpBands.getCPUtf8(name), cpBands.getCPUtf8(layout));
147             attributeDefinitions.add(definition);
148             switch (contextType) {
149             case CONTEXT_CLASS:
150                 classAttributeLayouts.add(definition);
151                 break;
152             case CONTEXT_METHOD:
153                 methodAttributeLayouts.add(definition);
154                 break;
155             case CONTEXT_FIELD:
156                 fieldAttributeLayouts.add(definition);
157                 break;
158             case CONTEXT_CODE:
159                 codeAttributeLayouts.add(definition);
160             }
161         });
162     }
163 
164     private int[] addHighIndices(final int[] availableIndices) {
165         final int[] temp = Arrays.copyOf(availableIndices, availableIndices.length + 32);
166         int j = 32;
167         for (int i = availableIndices.length; i < temp.length; i++) {
168             temp[i] = j;
169             j++;
170         }
171         return temp;
172     }
173 
174     private void addSyntheticDefinitions() {
175         final boolean anySytheticClasses = segment.getClassBands().isAnySyntheticClasses();
176         final boolean anySyntheticMethods = segment.getClassBands().isAnySyntheticMethods();
177         final boolean anySyntheticFields = segment.getClassBands().isAnySyntheticFields();
178         if (anySytheticClasses || anySyntheticMethods || anySyntheticFields) {
179             final CPUTF8 syntheticUTF = cpBands.getCPUtf8("Synthetic");
180             final CPUTF8 emptyUTF = cpBands.getCPUtf8("");
181             if (anySytheticClasses) {
182                 attributeDefinitions.add(new AttributeDefinition(12, CONTEXT_CLASS, syntheticUTF, emptyUTF));
183             }
184             if (anySyntheticMethods) {
185                 attributeDefinitions.add(new AttributeDefinition(12, CONTEXT_METHOD, syntheticUTF, emptyUTF));
186             }
187             if (anySyntheticFields) {
188                 attributeDefinitions.add(new AttributeDefinition(12, CONTEXT_FIELD, syntheticUTF, emptyUTF));
189             }
190         }
191     }
192 
193     /**
194      * 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
195      * while classes were being read.
196      */
197     public void finaliseBands() {
198         addSyntheticDefinitions();
199         segmentHeader.setAttribute_definition_count(attributeDefinitions.size());
200     }
201 
202     public List<AttributeDefinition> getClassAttributeLayouts() {
203         return classAttributeLayouts;
204     }
205 
206     public List<AttributeDefinition> getCodeAttributeLayouts() {
207         return codeAttributeLayouts;
208     }
209 
210     public List<AttributeDefinition> getFieldAttributeLayouts() {
211         return fieldAttributeLayouts;
212     }
213 
214     public List<AttributeDefinition> getMethodAttributeLayouts() {
215         return methodAttributeLayouts;
216     }
217 
218     @Override
219     public void pack(final OutputStream out) throws IOException, Pack200Exception {
220         PackingUtils.log("Writing attribute definition bands...");
221         final int[] attributeDefinitionHeader = new int[attributeDefinitions.size()];
222         final int[] attributeDefinitionName = new int[attributeDefinitions.size()];
223         final int[] attributeDefinitionLayout = new int[attributeDefinitions.size()];
224         for (int i = 0; i < attributeDefinitionLayout.length; i++) {
225             final AttributeDefinition def = attributeDefinitions.get(i);
226             attributeDefinitionHeader[i] = def.contextType | def.index + 1 << 2;
227             attributeDefinitionName[i] = def.name.getIndex();
228             attributeDefinitionLayout[i] = def.layout.getIndex();
229         }
230 
231         byte[] encodedBand = encodeBandInt("attributeDefinitionHeader", attributeDefinitionHeader, Codec.BYTE1);
232         out.write(encodedBand);
233         PackingUtils.log("Wrote " + encodedBand.length + " bytes from attributeDefinitionHeader[" + attributeDefinitionHeader.length + "]");
234 
235         encodedBand = encodeBandInt("attributeDefinitionName", attributeDefinitionName, Codec.UNSIGNED5);
236         out.write(encodedBand);
237         PackingUtils.log("Wrote " + encodedBand.length + " bytes from attributeDefinitionName[" + attributeDefinitionName.length + "]");
238 
239         encodedBand = encodeBandInt("attributeDefinitionLayout", attributeDefinitionLayout, Codec.UNSIGNED5);
240         out.write(encodedBand);
241         PackingUtils.log("Wrote " + encodedBand.length + " bytes from attributeDefinitionLayout[" + attributeDefinitionLayout.length + "]");
242     }
243 }