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