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