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