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.List; 024import java.util.Map; 025 026import org.objectweb.asm.Label; 027 028/** 029 * Bytecode bands (corresponds to the <code>bc_bands</code> set of bands in the pack200 specification) 030 */ 031public class BcBands extends BandSet { 032 033 private final CpBands cpBands; 034 private final Segment segment; 035 036 public BcBands(final CpBands cpBands, final Segment segment, final int effort) { 037 super(effort, segment.getSegmentHeader()); 038 this.cpBands = cpBands; 039 this.segment = segment; 040 } 041 042 private final IntList bcCodes = new IntList(); 043 private final IntList bcCaseCount = new IntList(); 044 private final IntList bcCaseValue = new IntList(); 045 private final IntList bcByte = new IntList(); 046 private final IntList bcShort = new IntList(); 047 private final IntList bcLocal = new IntList(); 048 private final List bcLabel = new ArrayList(); 049 private final List bcIntref = new ArrayList(); 050 private final List bcFloatRef = new ArrayList(); 051 private final List bcLongRef = new ArrayList(); 052 private final List bcDoubleRef = new ArrayList(); 053 private final List bcStringRef = new ArrayList(); 054 private final List bcClassRef = new ArrayList(); 055 private final List bcFieldRef = new ArrayList(); 056 private final List bcMethodRef = new ArrayList(); 057 private final List bcIMethodRef = new ArrayList(); 058 private List bcThisField = new ArrayList(); 059 private final List bcSuperField = new ArrayList(); 060 private List bcThisMethod = new ArrayList(); 061 private List bcSuperMethod = new ArrayList(); 062 private List bcInitRef = new ArrayList(); 063 064 private String currentClass; 065 private String superClass; 066 private String currentNewClass; 067 068 private static final int MULTIANEWARRAY = 197; 069 private static final int ALOAD_0 = 42; 070 private static final int WIDE = 196; 071 private static final int INVOKEINTERFACE = 185; 072 private static final int TABLESWITCH = 170; 073 private static final int IINC = 132; 074 private static final int LOOKUPSWITCH = 171; 075 private static final int endMarker = 255; 076 077 private final IntList bciRenumbering = new IntList(); 078 private final Map labelsToOffsets = new HashMap(); 079 private int byteCodeOffset; 080 private int renumberedOffset; 081 private final IntList bcLabelRelativeOffsets = new IntList(); 082 083 public void setCurrentClass(final String name, final String superName) { 084 currentClass = name; 085 superClass = superName; 086 } 087 088 /** 089 * All input classes for the segment have now been read in, so this method is called so that this class can 090 * calculate/complete anything it could not do while classes were being read. 091 */ 092 public void finaliseBands() { 093 bcThisField = getIndexInClass(bcThisField); 094 bcThisMethod = getIndexInClass(bcThisMethod); 095 bcSuperMethod = getIndexInClass(bcSuperMethod); 096 bcInitRef = getIndexInClassForConstructor(bcInitRef); 097 } 098 099 @Override 100 public void pack(final OutputStream out) throws IOException, Pack200Exception { 101 PackingUtils.log("Writing byte code bands..."); 102 byte[] encodedBand = encodeBandInt("bcCodes", bcCodes.toArray(), Codec.BYTE1); 103 out.write(encodedBand); 104 PackingUtils.log("Wrote " + encodedBand.length + " bytes from bcCodes[" + bcCodes.size() + "]"); 105 106 encodedBand = encodeBandInt("bcCaseCount", bcCaseCount.toArray(), Codec.UNSIGNED5); 107 out.write(encodedBand); 108 PackingUtils.log("Wrote " + encodedBand.length + " bytes from bcCaseCount[" + bcCaseCount.size() + "]"); 109 110 encodedBand = encodeBandInt("bcCaseValue", bcCaseValue.toArray(), Codec.DELTA5); 111 out.write(encodedBand); 112 PackingUtils.log("Wrote " + encodedBand.length + " bytes from bcCaseValue[" + bcCaseValue.size() + "]"); 113 114 encodedBand = encodeBandInt("bcByte", bcByte.toArray(), Codec.BYTE1); 115 out.write(encodedBand); 116 PackingUtils.log("Wrote " + encodedBand.length + " bytes from bcByte[" + bcByte.size() + "]"); 117 118 encodedBand = encodeBandInt("bcShort", bcShort.toArray(), Codec.DELTA5); 119 out.write(encodedBand); 120 PackingUtils.log("Wrote " + encodedBand.length + " bytes from bcShort[" + bcShort.size() + "]"); 121 122 encodedBand = encodeBandInt("bcLocal", bcLocal.toArray(), Codec.UNSIGNED5); 123 out.write(encodedBand); 124 PackingUtils.log("Wrote " + encodedBand.length + " bytes from bcLocal[" + bcLocal.size() + "]"); 125 126 encodedBand = encodeBandInt("bcLabel", integerListToArray(bcLabel), Codec.BRANCH5); 127 out.write(encodedBand); 128 PackingUtils.log("Wrote " + encodedBand.length + " bytes from bcLabel[" + bcLabel.size() + "]"); 129 130 encodedBand = encodeBandInt("bcIntref", cpEntryListToArray(bcIntref), Codec.DELTA5); 131 out.write(encodedBand); 132 PackingUtils.log("Wrote " + encodedBand.length + " bytes from bcIntref[" + bcIntref.size() + "]"); 133 134 encodedBand = encodeBandInt("bcFloatRef", cpEntryListToArray(bcFloatRef), Codec.DELTA5); 135 out.write(encodedBand); 136 PackingUtils.log("Wrote " + encodedBand.length + " bytes from bcFloatRef[" + bcFloatRef.size() + "]"); 137 138 encodedBand = encodeBandInt("bcLongRef", cpEntryListToArray(bcLongRef), Codec.DELTA5); 139 out.write(encodedBand); 140 PackingUtils.log("Wrote " + encodedBand.length + " bytes from bcLongRef[" + bcLongRef.size() + "]"); 141 142 encodedBand = encodeBandInt("bcDoubleRef", cpEntryListToArray(bcDoubleRef), Codec.DELTA5); 143 out.write(encodedBand); 144 PackingUtils.log("Wrote " + encodedBand.length + " bytes from bcDoubleRef[" + bcDoubleRef.size() + "]"); 145 146 encodedBand = encodeBandInt("bcStringRef", cpEntryListToArray(bcStringRef), Codec.DELTA5); 147 out.write(encodedBand); 148 PackingUtils.log("Wrote " + encodedBand.length + " bytes from bcStringRef[" + bcStringRef.size() + "]"); 149 150 encodedBand = encodeBandInt("bcClassRef", cpEntryOrNullListToArray(bcClassRef), Codec.UNSIGNED5); 151 out.write(encodedBand); 152 PackingUtils.log("Wrote " + encodedBand.length + " bytes from bcClassRef[" + bcClassRef.size() + "]"); 153 154 encodedBand = encodeBandInt("bcFieldRef", cpEntryListToArray(bcFieldRef), Codec.DELTA5); 155 out.write(encodedBand); 156 PackingUtils.log("Wrote " + encodedBand.length + " bytes from bcFieldRef[" + bcFieldRef.size() + "]"); 157 158 encodedBand = encodeBandInt("bcMethodRef", cpEntryListToArray(bcMethodRef), Codec.UNSIGNED5); 159 out.write(encodedBand); 160 PackingUtils.log("Wrote " + encodedBand.length + " bytes from bcMethodRef[" + bcMethodRef.size() + "]"); 161 162 encodedBand = encodeBandInt("bcIMethodRef", cpEntryListToArray(bcIMethodRef), Codec.DELTA5); 163 out.write(encodedBand); 164 PackingUtils.log("Wrote " + encodedBand.length + " bytes from bcIMethodRef[" + bcIMethodRef.size() + "]"); 165 166 encodedBand = encodeBandInt("bcThisField", integerListToArray(bcThisField), Codec.UNSIGNED5); 167 out.write(encodedBand); 168 PackingUtils.log("Wrote " + encodedBand.length + " bytes from bcThisField[" + bcThisField.size() + "]"); 169 170 encodedBand = encodeBandInt("bcSuperField", integerListToArray(bcSuperField), Codec.UNSIGNED5); 171 out.write(encodedBand); 172 PackingUtils.log("Wrote " + encodedBand.length + " bytes from bcSuperField[" + bcSuperField.size() + "]"); 173 174 encodedBand = encodeBandInt("bcThisMethod", integerListToArray(bcThisMethod), Codec.UNSIGNED5); 175 out.write(encodedBand); 176 PackingUtils.log("Wrote " + encodedBand.length + " bytes from bcThisMethod[" + bcThisMethod.size() + "]"); 177 178 encodedBand = encodeBandInt("bcSuperMethod", integerListToArray(bcSuperMethod), Codec.UNSIGNED5); 179 out.write(encodedBand); 180 PackingUtils.log("Wrote " + encodedBand.length + " bytes from bcSuperMethod[" + bcSuperMethod.size() + "]"); 181 182 encodedBand = encodeBandInt("bcInitRef", integerListToArray(bcInitRef), Codec.UNSIGNED5); 183 out.write(encodedBand); 184 PackingUtils.log("Wrote " + encodedBand.length + " bytes from bcInitRef[" + bcInitRef.size() + "]"); 185 186 // out.write(encodeBandInt(cpEntryintegerListToArray(bcEscRef), 187 // Codec.UNSIGNED5)); 188 // out.write(encodeBandInt(integerListToArray(bcEscRefSize), 189 // Codec.UNSIGNED5)); 190 // out.write(encodeBandInt(integerListToArray(bcEscSize), 191 // Codec.UNSIGNED5)); 192 // out.write(encodeBandInt(integerListToArray(bcEscByte), Codec.BYTE1)); 193 } 194 195 private List getIndexInClass(final List cPMethodOrFieldList) { 196 final List indices = new ArrayList(cPMethodOrFieldList.size()); 197 for (int i = 0; i < cPMethodOrFieldList.size(); i++) { 198 final CPMethodOrField cpMF = (CPMethodOrField) cPMethodOrFieldList.get(i); 199 indices.add(Integer.valueOf(cpMF.getIndexInClass())); 200 } 201 return indices; 202 } 203 204 private List getIndexInClassForConstructor(final List cPMethodList) { 205 final List indices = new ArrayList(cPMethodList.size()); 206 for (int i = 0; i < cPMethodList.size(); i++) { 207 final CPMethodOrField cpMF = (CPMethodOrField) cPMethodList.get(i); 208 indices.add(Integer.valueOf(cpMF.getIndexInClassForConstructor())); 209 } 210 return indices; 211 } 212 213 public void visitEnd() { 214 for (int i = 0; i < bciRenumbering.size(); i++) { 215 if (bciRenumbering.get(i) == -1) { 216 bciRenumbering.remove(i); 217 bciRenumbering.add(i, ++renumberedOffset); 218 } 219 } 220 if (renumberedOffset != 0) { 221 if (renumberedOffset + 1 != bciRenumbering.size()) { 222 throw new RuntimeException("Mistake made with renumbering"); 223 } 224 for (int i = bcLabel.size() - 1; i >= 0; i--) { 225 final Object label = bcLabel.get(i); 226 if (label instanceof Integer) { 227 break; 228 } 229 if (label instanceof Label) { 230 bcLabel.remove(i); 231 final Integer offset = (Integer) labelsToOffsets.get(label); 232 final int relativeOffset = bcLabelRelativeOffsets.get(i); 233 bcLabel.add(i, 234 Integer.valueOf(bciRenumbering.get(offset.intValue()) - bciRenumbering.get(relativeOffset))); 235 } 236 } 237 bcCodes.add(endMarker); 238 segment.getClassBands().doBciRenumbering(bciRenumbering, labelsToOffsets); 239 bciRenumbering.clear(); 240 labelsToOffsets.clear(); 241 byteCodeOffset = 0; 242 renumberedOffset = 0; 243 } 244 } 245 246 public void visitLabel(final Label label) { 247 labelsToOffsets.put(label, Integer.valueOf(byteCodeOffset)); 248 } 249 250 public void visitFieldInsn(int opcode, final String owner, final String name, final String desc) { 251 byteCodeOffset += 3; 252 updateRenumbering(); 253 boolean aload_0 = false; 254 if (bcCodes.size() > 0 && (bcCodes.get(bcCodes.size() - 1)) == ALOAD_0) { 255 bcCodes.remove(bcCodes.size() - 1); 256 aload_0 = true; 257 } 258 final CPMethodOrField cpField = cpBands.getCPField(owner, name, desc); 259 if (aload_0) { 260 opcode += 7; 261 } 262 if (owner.equals(currentClass)) { 263 opcode += 24; // change to getstatic_this, putstatic_this etc. 264 bcThisField.add(cpField); 265// } else if (owner.equals(superClass)) { 266// opcode += 38; // change to getstatic_super etc. 267// bcSuperField.add(cpField); 268 } else { 269 if (aload_0) { 270 opcode -= 7; 271 bcCodes.add(ALOAD_0); // add aload_0 back in because 272 // there's no special rewrite in 273 // this case. 274 } 275 bcFieldRef.add(cpField); 276 } 277 aload_0 = false; 278 bcCodes.add(opcode); 279 } 280 281 private void updateRenumbering() { 282 if (bciRenumbering.isEmpty()) { 283 bciRenumbering.add(0); 284 } 285 renumberedOffset++; 286 for (int i = bciRenumbering.size(); i < byteCodeOffset; i++) { 287 bciRenumbering.add(-1); 288 } 289 bciRenumbering.add(renumberedOffset); 290 } 291 292 public void visitIincInsn(final int var, final int increment) { 293 if (var > 255 || increment > 255) { 294 byteCodeOffset += 6; 295 bcCodes.add(WIDE); 296 bcCodes.add(IINC); 297 bcLocal.add(var); 298 bcShort.add(increment); 299 } else { 300 byteCodeOffset += 3; 301 bcCodes.add(IINC); 302 bcLocal.add(var); 303 bcByte.add(increment & 0xFF); 304 } 305 updateRenumbering(); 306 } 307 308 public void visitInsn(final int opcode) { 309 if (opcode >= 202) { 310 throw new RuntimeException("Non-standard bytecode instructions not supported"); 311 } 312 bcCodes.add(opcode); 313 byteCodeOffset++; 314 updateRenumbering(); 315 } 316 317 public void visitIntInsn(final int opcode, final int operand) { 318 switch (opcode) { 319 case 17: // sipush 320 bcCodes.add(opcode); 321 bcShort.add(operand); 322 byteCodeOffset += 3; 323 break; 324 case 16: // bipush 325 case 188: // newarray 326 bcCodes.add(opcode); 327 bcByte.add(operand & 0xFF); 328 byteCodeOffset += 2; 329 } 330 updateRenumbering(); 331 } 332 333 public void visitJumpInsn(final int opcode, final Label label) { 334 bcCodes.add(opcode); 335 bcLabel.add(label); 336 bcLabelRelativeOffsets.add(byteCodeOffset); 337 byteCodeOffset += 3; 338 updateRenumbering(); 339 } 340 341 public void visitLdcInsn(final Object cst) { 342 final CPConstant constant = cpBands.getConstant(cst); 343 if (segment.lastConstantHadWideIndex() || constant instanceof CPLong || constant instanceof CPDouble) { 344 byteCodeOffset += 3; 345 if (constant instanceof CPInt) { 346 bcCodes.add(237); // ildc_w 347 bcIntref.add(constant); 348 } else if (constant instanceof CPFloat) { 349 bcCodes.add(238); // fldc 350 bcFloatRef.add(constant); 351 } else if (constant instanceof CPLong) { 352 bcCodes.add(20); // lldc2_w 353 bcLongRef.add(constant); 354 } else if (constant instanceof CPDouble) { 355 bcCodes.add(239); // dldc2_w 356 bcDoubleRef.add(constant); 357 } else if (constant instanceof CPString) { 358 bcCodes.add(19); // aldc 359 bcStringRef.add(constant); 360 } else if (constant instanceof CPClass) { 361 bcCodes.add(236); // cldc 362 bcClassRef.add(constant); 363 } else { 364 throw new RuntimeException("Constant should not be null"); 365 } 366 } else { 367 byteCodeOffset += 2; 368 if (constant instanceof CPInt) { 369 bcCodes.add(234); // ildc 370 bcIntref.add(constant); 371 } else if (constant instanceof CPFloat) { 372 bcCodes.add(235); // fldc 373 bcFloatRef.add(constant); 374 } else if (constant instanceof CPString) { 375 bcCodes.add(18); // aldc 376 bcStringRef.add(constant); 377 } else if (constant instanceof CPClass) { 378 bcCodes.add(233); // cldc 379 bcClassRef.add(constant); 380 } 381 } 382 updateRenumbering(); 383 } 384 385 public void visitLookupSwitchInsn(final Label dflt, final int[] keys, final Label[] labels) { 386 bcCodes.add(LOOKUPSWITCH); 387 bcLabel.add(dflt); 388 bcLabelRelativeOffsets.add(byteCodeOffset); 389 bcCaseCount.add(keys.length); 390 for (int i = 0; i < labels.length; i++) { 391 bcCaseValue.add(keys[i]); 392 bcLabel.add(labels[i]); 393 bcLabelRelativeOffsets.add(byteCodeOffset); 394 } 395 final int padding = (byteCodeOffset + 1) % 4 == 0 ? 0 : 4 - ((byteCodeOffset + 1) % 4); 396 byteCodeOffset += 1 + padding + 8 + 8 * keys.length; 397 updateRenumbering(); 398 } 399 400 public void visitMethodInsn(int opcode, final String owner, final String name, final String desc) { 401 byteCodeOffset += 3; 402 switch (opcode) { 403 case 182: // invokevirtual 404 case 183: // invokespecial 405 case 184: // invokestatic 406 boolean aload_0 = false; 407 if (bcCodes.size() > 0 && (bcCodes.get(bcCodes.size() - 1)) == (ALOAD_0)) { 408 bcCodes.remove(bcCodes.size() - 1); 409 aload_0 = true; 410 opcode += 7; 411 } 412 if (owner.equals(currentClass)) { 413 opcode += 24; // change to invokevirtual_this, 414 // invokespecial_this etc. 415 416 if (name.equals("<init>") && opcode == 207) { 417 opcode = 230; // invokespecial_this_init 418 bcInitRef.add(cpBands.getCPMethod(owner, name, desc)); 419 } else { 420 bcThisMethod.add(cpBands.getCPMethod(owner, name, desc)); 421 } 422 } else if (owner.equals(superClass)) { // TODO 423 opcode += 38; // change to invokevirtual_super, 424 // invokespecial_super etc. 425 if (name.equals("<init>") && opcode == 221) { 426 opcode = 231; // invokespecial_super_init 427 bcInitRef.add(cpBands.getCPMethod(owner, name, desc)); 428 } else { 429 bcSuperMethod.add(cpBands.getCPMethod(owner, name, desc)); 430 } 431 } else { 432 if (aload_0) { 433 opcode -= 7; 434 bcCodes.add(ALOAD_0); // add aload_0 back in 435 // because there's no 436 // special rewrite in this 437 // case. 438 } 439 if (name.equals("<init>") && opcode == 183 && owner.equals(currentNewClass)) { 440 opcode = 232; // invokespecial_new_init 441 bcInitRef.add(cpBands.getCPMethod(owner, name, desc)); 442 } else { 443 bcMethodRef.add(cpBands.getCPMethod(owner, name, desc)); 444 } 445 } 446 bcCodes.add(opcode); 447 break; 448 case 185: // invokeinterface 449 byteCodeOffset += 2; 450 final CPMethodOrField cpIMethod = cpBands.getCPIMethod(owner, name, desc); 451 bcIMethodRef.add(cpIMethod); 452 bcCodes.add(INVOKEINTERFACE); 453 break; 454 } 455 updateRenumbering(); 456 } 457 458 public void visitMultiANewArrayInsn(final String desc, final int dimensions) { 459 byteCodeOffset += 4; 460 updateRenumbering(); 461 bcCodes.add(MULTIANEWARRAY); 462 bcClassRef.add(cpBands.getCPClass(desc)); 463 bcByte.add(dimensions & 0xFF); 464 } 465 466 public void visitTableSwitchInsn(final int min, final int max, final Label dflt, final Label[] labels) { 467 bcCodes.add(TABLESWITCH); 468 bcLabel.add(dflt); 469 bcLabelRelativeOffsets.add(byteCodeOffset); 470 bcCaseValue.add(min); 471 final int count = labels.length; 472 bcCaseCount.add(count); 473 for (int i = 0; i < count; i++) { 474 bcLabel.add(labels[i]); 475 bcLabelRelativeOffsets.add(byteCodeOffset); 476 } 477 final int padding = byteCodeOffset % 4 == 0 ? 0 : 4 - (byteCodeOffset % 4); 478 byteCodeOffset += (padding + 12 + 4 * labels.length); 479 updateRenumbering(); 480 } 481 482 public void visitTypeInsn(final int opcode, final String type) { 483 // NEW, ANEWARRAY, CHECKCAST or INSTANCEOF 484 byteCodeOffset += 3; 485 updateRenumbering(); 486 bcCodes.add(opcode); 487 bcClassRef.add(cpBands.getCPClass(type)); 488 if (opcode == 187) { // NEW 489 currentNewClass = type; 490 } 491 } 492 493 public void visitVarInsn(final int opcode, final int var) { 494 // ILOAD, LLOAD, FLOAD, DLOAD, ALOAD, ISTORE, LSTORE, FSTORE, DSTORE, ASTORE or RET 495 if (var > 255) { 496 byteCodeOffset += 4; 497 bcCodes.add(WIDE); 498 bcCodes.add(opcode); 499 bcLocal.add(var); 500 } else if (var > 3 || opcode == 169 /* RET */) { 501 byteCodeOffset += 2; 502 bcCodes.add(opcode); 503 bcLocal.add(var); 504 } else { 505 byteCodeOffset += 1; 506 switch (opcode) { 507 case 21: // ILOAD 508 case 54: // ISTORE 509 bcCodes.add(opcode + 5 + var); 510 break; 511 case 22: // LLOAD 512 case 55: // LSTORE 513 bcCodes.add(opcode + 8 + var); 514 break; 515 case 23: // FLOAD 516 case 56: // FSTORE 517 bcCodes.add(opcode + 11 + var); 518 break; 519 case 24: // DLOAD 520 case 57: // DSTORE 521 bcCodes.add(opcode + 14 + var); 522 break; 523 case 25: // A_LOAD 524 case 58: // A_STORE 525 bcCodes.add(opcode + 17 + var); 526 break; 527 } 528 } 529 updateRenumbering(); 530 } 531 532}