001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * https://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.apache.commons.compress.harmony.pack200; 020 021import java.io.IOException; 022import java.io.OutputStream; 023import java.util.ArrayList; 024import java.util.Arrays; 025import java.util.HashMap; 026import java.util.List; 027import java.util.Map; 028 029import org.objectweb.asm.Attribute; 030 031/** 032 * Attribute Definition bands define how any unknown attributes should be read by the decompressor. 033 */ 034public class AttributeDefinitionBands extends BandSet { 035 036 public static class AttributeDefinition { 037 038 public int index; 039 public int contextType; 040 public CPUTF8 name; 041 public CPUTF8 layout; 042 043 public AttributeDefinition(final int index, final int contextType, final CPUTF8 name, final CPUTF8 layout) { 044 this.index = index; 045 this.contextType = contextType; 046 this.name = name; 047 this.layout = layout; 048 } 049 050 } 051 052 /** 053 * {@value} 054 */ 055 public static final int CONTEXT_CLASS = 0; 056 057 /** 058 * {@value} 059 */ 060 public static final int CONTEXT_CODE = 3; 061 062 /** 063 * {@value} 064 */ 065 public static final int CONTEXT_FIELD = 1; 066 067 /** 068 * {@value} 069 */ 070 public static final int CONTEXT_METHOD = 2; 071 072 private final List<AttributeDefinition> classAttributeLayouts = new ArrayList<>(); 073 private final List<AttributeDefinition> methodAttributeLayouts = new ArrayList<>(); 074 private final List<AttributeDefinition> fieldAttributeLayouts = new ArrayList<>(); 075 076 private final List<AttributeDefinition> codeAttributeLayouts = new ArrayList<>(); 077 078 private final List<AttributeDefinition> attributeDefinitions = new ArrayList<>(); 079 private final CpBands cpBands; 080 081 private final Segment segment; 082 083 public AttributeDefinitionBands(final Segment segment, final int effort, final Attribute[] attributePrototypes) { 084 super(effort, segment.getSegmentHeader()); 085 this.cpBands = segment.getCpBands(); 086 this.segment = segment; 087 final Map<String, String> classLayouts = new HashMap<>(); 088 final Map<String, String> methodLayouts = new HashMap<>(); 089 final Map<String, String> fieldLayouts = new HashMap<>(); 090 final Map<String, String> codeLayouts = new HashMap<>(); 091 092 for (final Attribute attributePrototype : attributePrototypes) { 093 final NewAttribute newAttribute = (NewAttribute) attributePrototype; 094 if (!(newAttribute instanceof NewAttribute.ErrorAttribute) && !(newAttribute instanceof NewAttribute.PassAttribute) 095 && !(newAttribute instanceof NewAttribute.StripAttribute)) { 096 if (newAttribute.isContextClass()) { 097 classLayouts.put(newAttribute.type, newAttribute.getLayout()); 098 } 099 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}