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.HashMap; 023import java.util.Iterator; 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 final int CONTEXT_CLASS = 0; 035 public static final int CONTEXT_CODE = 3; 036 public static final int CONTEXT_FIELD = 1; 037 public static final int CONTEXT_METHOD = 2; 038 039 private final List<AttributeDefinition> classAttributeLayouts = new ArrayList<>(); 040 private final List<AttributeDefinition> methodAttributeLayouts = new ArrayList<>(); 041 private final List<AttributeDefinition> fieldAttributeLayouts = new ArrayList<>(); 042 private final List<AttributeDefinition> codeAttributeLayouts = new ArrayList<>(); 043 044 private final List<AttributeDefinition> attributeDefinitions = new ArrayList<>(); 045 046 private final CpBands cpBands; 047 private final Segment segment; 048 049 public AttributeDefinitionBands(final Segment segment, final int effort, final Attribute[] attributePrototypes) { 050 super(effort, segment.getSegmentHeader()); 051 this.cpBands = segment.getCpBands(); 052 this.segment = segment; 053 final Map<String, String> classLayouts = new HashMap<>(); 054 final Map<String, String> methodLayouts = new HashMap<>(); 055 final Map<String, String> fieldLayouts = new HashMap<>(); 056 final Map<String, String> codeLayouts = new HashMap<>(); 057 058 for (Attribute attributePrototype : attributePrototypes) { 059 final NewAttribute newAttribute = (NewAttribute) attributePrototype; 060 if (!(newAttribute instanceof NewAttribute.ErrorAttribute) 061 && !(newAttribute instanceof NewAttribute.PassAttribute) 062 && !(newAttribute instanceof NewAttribute.StripAttribute)) { 063 if (newAttribute.isContextClass()) { 064 classLayouts.put(newAttribute.type, newAttribute.getLayout()); 065 } 066 if (newAttribute.isContextMethod()) { 067 methodLayouts.put(newAttribute.type, newAttribute.getLayout()); 068 } 069 if (newAttribute.isContextField()) { 070 fieldLayouts.put(newAttribute.type, newAttribute.getLayout()); 071 } 072 if (newAttribute.isContextCode()) { 073 codeLayouts.put(newAttribute.type, newAttribute.getLayout()); 074 } 075 } 076 } 077 if (classLayouts.size() > 7) { 078 segmentHeader.setHave_class_flags_hi(true); 079 } 080 if (methodLayouts.size() > 6) { 081 segmentHeader.setHave_method_flags_hi(true); 082 } 083 if (fieldLayouts.size() > 10) { 084 segmentHeader.setHave_field_flags_hi(true); 085 } 086 if (codeLayouts.size() > 15) { 087 segmentHeader.setHave_code_flags_hi(true); 088 } 089 int[] availableClassIndices = {25, 26, 27, 28, 29, 30, 31}; 090 if (classLayouts.size() > 7) { 091 availableClassIndices = addHighIndices(availableClassIndices); 092 } 093 addAttributeDefinitions(classLayouts, availableClassIndices, CONTEXT_CLASS); 094 int[] availableMethodIndices = {26, 27, 28, 29, 30, 31}; 095 if (methodAttributeLayouts.size() > 6) { 096 availableMethodIndices = addHighIndices(availableMethodIndices); 097 } 098 addAttributeDefinitions(methodLayouts, availableMethodIndices, CONTEXT_METHOD); 099 int[] availableFieldIndices = {18, 23, 24, 25, 26, 27, 28, 29, 30, 31}; 100 if (fieldAttributeLayouts.size() > 10) { 101 availableFieldIndices = addHighIndices(availableFieldIndices); 102 } 103 addAttributeDefinitions(fieldLayouts, availableFieldIndices, CONTEXT_FIELD); 104 int[] availableCodeIndices = {17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; 105 if (codeAttributeLayouts.size() > 15) { 106 availableCodeIndices = addHighIndices(availableCodeIndices); 107 } 108 addAttributeDefinitions(codeLayouts, availableCodeIndices, CONTEXT_CODE); 109 } 110 111 /** 112 * All input classes for the segment have now been read in, so this method is called so that this class can 113 * calculate/complete anything it could not do while classes were being read. 114 */ 115 public void finaliseBands() { 116 addSyntheticDefinitions(); 117 segmentHeader.setAttribute_definition_count(attributeDefinitions.size()); 118 } 119 120 @Override 121 public void pack(final OutputStream out) throws IOException, Pack200Exception { 122 PackingUtils.log("Writing attribute definition bands..."); 123 final int[] attributeDefinitionHeader = new int[attributeDefinitions.size()]; 124 final int[] attributeDefinitionName = new int[attributeDefinitions.size()]; 125 final int[] attributeDefinitionLayout = new int[attributeDefinitions.size()]; 126 for (int i = 0; i < attributeDefinitionLayout.length; i++) { 127 final AttributeDefinition def = attributeDefinitions.get(i); 128 attributeDefinitionHeader[i] = def.contextType | (def.index + 1 << 2); 129 attributeDefinitionName[i] = def.name.getIndex(); 130 attributeDefinitionLayout[i] = def.layout.getIndex(); 131 } 132 133 byte[] encodedBand = encodeBandInt("attributeDefinitionHeader", attributeDefinitionHeader, Codec.BYTE1); 134 out.write(encodedBand); 135 PackingUtils.log("Wrote " + encodedBand.length + " bytes from attributeDefinitionHeader[" 136 + attributeDefinitionHeader.length + "]"); 137 138 encodedBand = encodeBandInt("attributeDefinitionName", attributeDefinitionName, Codec.UNSIGNED5); 139 out.write(encodedBand); 140 PackingUtils.log("Wrote " + encodedBand.length + " bytes from attributeDefinitionName[" 141 + attributeDefinitionName.length + "]"); 142 143 encodedBand = encodeBandInt("attributeDefinitionLayout", attributeDefinitionLayout, Codec.UNSIGNED5); 144 out.write(encodedBand); 145 PackingUtils.log("Wrote " + encodedBand.length + " bytes from attributeDefinitionLayout[" 146 + attributeDefinitionLayout.length + "]"); 147 } 148 149 private void addSyntheticDefinitions() { 150 final boolean anySytheticClasses = segment.getClassBands().isAnySyntheticClasses(); 151 final boolean anySyntheticMethods = segment.getClassBands().isAnySyntheticMethods(); 152 final boolean anySyntheticFields = segment.getClassBands().isAnySyntheticFields(); 153 if (anySytheticClasses || anySyntheticMethods || anySyntheticFields) { 154 final CPUTF8 syntheticUTF = cpBands.getCPUtf8("Synthetic"); 155 final CPUTF8 emptyUTF = cpBands.getCPUtf8(""); 156 if (anySytheticClasses) { 157 attributeDefinitions.add(new AttributeDefinition(12, CONTEXT_CLASS, syntheticUTF, emptyUTF)); 158 } 159 if (anySyntheticMethods) { 160 attributeDefinitions.add(new AttributeDefinition(12, CONTEXT_METHOD, syntheticUTF, emptyUTF)); 161 } 162 if (anySyntheticFields) { 163 attributeDefinitions.add(new AttributeDefinition(12, CONTEXT_FIELD, syntheticUTF, emptyUTF)); 164 } 165 } 166 } 167 168 private int[] addHighIndices(final int[] availableIndices) { 169 final int[] temp = new int[availableIndices.length + 32]; 170 System.arraycopy(availableIndices, 0, temp, 0, availableIndices.length); 171 int j = 32; 172 for (int i = availableIndices.length; i < temp.length; i++) { 173 temp[i] = j; 174 j++; 175 } 176 return temp; 177 } 178 179 private void addAttributeDefinitions(final Map<String, String> layoutMap, final int[] availableIndices, final int contextType) { 180 final int i = 0; 181 layoutMap.forEach((name, layout) -> { 182 final int index = availableIndices[i]; 183 final AttributeDefinition definition = new AttributeDefinition(index, contextType, cpBands.getCPUtf8(name), cpBands.getCPUtf8(layout)); 184 attributeDefinitions.add(definition); 185 switch (contextType) { 186 case CONTEXT_CLASS: 187 classAttributeLayouts.add(definition); 188 break; 189 case CONTEXT_METHOD: 190 methodAttributeLayouts.add(definition); 191 break; 192 case CONTEXT_FIELD: 193 fieldAttributeLayouts.add(definition); 194 break; 195 case CONTEXT_CODE: 196 codeAttributeLayouts.add(definition); 197 } 198 }); 199 } 200 201 public List<AttributeDefinition> getClassAttributeLayouts() { 202 return classAttributeLayouts; 203 } 204 205 public List<AttributeDefinition> getMethodAttributeLayouts() { 206 return methodAttributeLayouts; 207 } 208 209 public List<AttributeDefinition> getFieldAttributeLayouts() { 210 return fieldAttributeLayouts; 211 } 212 213 public List<AttributeDefinition> getCodeAttributeLayouts() { 214 return codeAttributeLayouts; 215 } 216 217 public static class AttributeDefinition { 218 219 public int index; 220 public int contextType; 221 public CPUTF8 name; 222 public CPUTF8 layout; 223 224 public AttributeDefinition(final int index, final int contextType, final CPUTF8 name, final CPUTF8 layout) { 225 this.index = index; 226 this.contextType = contextType; 227 this.name = name; 228 this.layout = layout; 229 } 230 231 } 232}