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}