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.ByteArrayInputStream; 020import java.io.IOException; 021import java.io.InputStream; 022import java.io.OutputStream; 023import java.io.StringReader; 024import java.io.UncheckedIOException; 025import java.util.ArrayList; 026import java.util.Collections; 027import java.util.List; 028import java.util.Map; 029 030import org.apache.commons.compress.harmony.pack200.AttributeDefinitionBands.AttributeDefinition; 031import org.objectweb.asm.Label; 032 033/** 034 * Set of bands relating to a non-predefined attribute that has had a layout definition given to pack200 (e.g. via one 035 * of the -C, -M, -F or -D command line options) 036 */ 037public class NewAttributeBands extends BandSet { 038 039 protected List<AttributeLayoutElement> attributeLayoutElements; 040 private int[] backwardsCallCounts; 041 private final CpBands cpBands; 042 private final AttributeDefinition def; 043 private boolean usedAtLeastOnce; 044 045 // used when parsing 046 private Integral lastPIntegral; 047 048 public NewAttributeBands(final int effort, final CpBands cpBands, final SegmentHeader header, 049 final AttributeDefinition def) throws IOException { 050 super(effort, header); 051 this.def = def; 052 this.cpBands = cpBands; 053 parseLayout(); 054 } 055 056 public void addAttribute(final NewAttribute attribute) { 057 usedAtLeastOnce = true; 058 final InputStream stream = new ByteArrayInputStream(attribute.getBytes()); 059 for (AttributeLayoutElement attributeLayoutElement : attributeLayoutElements) { 060 attributeLayoutElement.addAttributeToBand(attribute, stream); 061 } 062 } 063 064 @Override 065 public void pack(final OutputStream outputStream) throws IOException, Pack200Exception { 066 for (AttributeLayoutElement attributeLayoutElement : attributeLayoutElements) { 067 attributeLayoutElement.pack(outputStream); 068 } 069 } 070 071 public String getAttributeName() { 072 return def.name.getUnderlyingString(); 073 } 074 075 public int getFlagIndex() { 076 return def.index; 077 } 078 079 public int[] numBackwardsCalls() { 080 return backwardsCallCounts; 081 } 082 083 public boolean isUsedAtLeastOnce() { 084 return usedAtLeastOnce; 085 } 086 087 private void parseLayout() throws IOException { 088 final String layout = def.layout.getUnderlyingString(); 089 if (attributeLayoutElements == null) { 090 attributeLayoutElements = new ArrayList<>(); 091 final StringReader reader = new StringReader(layout); 092 AttributeLayoutElement e; 093 while ((e = readNextAttributeElement(reader)) != null) { 094 attributeLayoutElements.add(e); 095 } 096 resolveCalls(); 097 } 098 } 099 100 /** 101 * Resolve calls in the attribute layout and returns the number of backwards callables 102 * 103 * @param tokens - the attribute layout as a List of AttributeElements 104 */ 105 private void resolveCalls() { 106 for (int i = 0; i < attributeLayoutElements.size(); i++) { 107 final AttributeLayoutElement element = attributeLayoutElements.get(i); 108 if (element instanceof Callable) { 109 final Callable callable = (Callable) element; 110 final List<LayoutElement> body = callable.body; // Look for calls in the body 111 for (LayoutElement layoutElement : body) { 112 // Set the callable for each call 113 resolveCallsForElement(i, callable, layoutElement); 114 } 115 } 116 } 117 int backwardsCallableIndex = 0; 118 for (AttributeLayoutElement attributeLayoutElement : attributeLayoutElements) { 119 if (attributeLayoutElement instanceof Callable) { 120 final Callable callable = (Callable) attributeLayoutElement; 121 if (callable.isBackwardsCallable) { 122 callable.setBackwardsCallableIndex(backwardsCallableIndex); 123 backwardsCallableIndex++; 124 } 125 } 126 } 127 backwardsCallCounts = new int[backwardsCallableIndex]; 128 } 129 130 private void resolveCallsForElement(final int i, final Callable currentCallable, 131 final LayoutElement layoutElement) { 132 if (layoutElement instanceof Call) { 133 final Call call = (Call) layoutElement; 134 int index = call.callableIndex; 135 if (index == 0) { // Calls the parent callable 136 call.setCallable(currentCallable); 137 } else if (index > 0) { // Forwards call 138 for (int k = i + 1; k < attributeLayoutElements.size(); k++) { 139 final AttributeLayoutElement el = attributeLayoutElements.get(k); 140 if (el instanceof Callable) { 141 index--; 142 if (index == 0) { 143 call.setCallable((Callable) el); 144 break; 145 } 146 } 147 } 148 } else { // Backwards call 149 for (int k = i - 1; k >= 0; k--) { 150 final AttributeLayoutElement el = attributeLayoutElements.get(k); 151 if (el instanceof Callable) { 152 index++; 153 if (index == 0) { 154 call.setCallable((Callable) el); 155 break; 156 } 157 } 158 } 159 } 160 } else if (layoutElement instanceof Replication) { 161 final List<LayoutElement> children = ((Replication) layoutElement).layoutElements; 162 for (LayoutElement child : children) { 163 resolveCallsForElement(i, currentCallable, child); 164 } 165 } 166 } 167 168 private AttributeLayoutElement readNextAttributeElement(final StringReader reader) throws IOException { 169 reader.mark(1); 170 final int next = reader.read(); 171 if (next == -1) { 172 return null; 173 } 174 if (next == '[') { 175 return new Callable(readBody(getStreamUpToMatchingBracket(reader))); 176 } 177 reader.reset(); 178 return readNextLayoutElement(reader); 179 } 180 181 private LayoutElement readNextLayoutElement(final StringReader reader) throws IOException { 182 final int nextChar = reader.read(); 183 if (nextChar == -1) { 184 return null; 185 } 186 187 switch (nextChar) { 188 // Integrals 189 case 'B': 190 case 'H': 191 case 'I': 192 case 'V': 193 return new Integral(new String(new char[] {(char) nextChar})); 194 case 'S': 195 case 'F': 196 return new Integral(new String(new char[] {(char) nextChar, (char) reader.read()})); 197 case 'P': 198 reader.mark(1); 199 if (reader.read() != 'O') { 200 reader.reset(); 201 lastPIntegral = new Integral("P" + (char) reader.read()); 202 return lastPIntegral; 203 } 204 lastPIntegral = new Integral("PO" + (char) reader.read(), lastPIntegral); 205 return lastPIntegral; 206 case 'O': 207 reader.mark(1); 208 if (reader.read() != 'S') { 209 reader.reset(); 210 return new Integral("O" + (char) reader.read(), lastPIntegral); 211 } 212 return new Integral("OS" + (char) reader.read(), lastPIntegral); 213 214 // Replication 215 case 'N': 216 final char uint_type = (char) reader.read(); 217 reader.read(); // '[' 218 final String str = readUpToMatchingBracket(reader); 219 return new Replication("" + uint_type, str); 220 221 // Union 222 case 'T': 223 String int_type = String.valueOf((char) reader.read()); 224 if (int_type.equals("S")) { 225 int_type += (char) reader.read(); 226 } 227 final List<UnionCase> unionCases = new ArrayList<>(); 228 UnionCase c; 229 while ((c = readNextUnionCase(reader)) != null) { 230 unionCases.add(c); 231 } 232 reader.read(); // '(' 233 reader.read(); // ')' 234 reader.read(); // '[' 235 List<LayoutElement> body = null; 236 reader.mark(1); 237 final char next = (char) reader.read(); 238 if (next != ']') { 239 reader.reset(); 240 body = readBody(getStreamUpToMatchingBracket(reader)); 241 } 242 return new Union(int_type, unionCases, body); 243 244 // Call 245 case '(': 246 final int number = readNumber(reader).intValue(); 247 reader.read(); // ')' 248 return new Call(number); 249 // Reference 250 case 'K': 251 case 'R': 252 final StringBuilder string = new StringBuilder("").append((char) nextChar).append((char) reader.read()); 253 final char nxt = (char) reader.read(); 254 string.append(nxt); 255 if (nxt == 'N') { 256 string.append((char) reader.read()); 257 } 258 return new Reference(string.toString()); 259 } 260 return null; 261 } 262 263 /** 264 * Read a UnionCase from the stream 265 * 266 * @param reader 267 * @return 268 * @throws IOException If an I/O error occurs. 269 */ 270 private UnionCase readNextUnionCase(final StringReader reader) throws IOException { 271 reader.mark(2); 272 reader.read(); // '(' 273 final int next = reader.read(); 274 char ch = (char) next; 275 if (ch == ')' || next == -1) { 276 reader.reset(); 277 return null; 278 } 279 reader.reset(); 280 reader.read(); // '(' 281 final List<Integer> tags = new ArrayList<>(); 282 Integer nextTag; 283 do { 284 nextTag = readNumber(reader); 285 if (nextTag != null) { 286 tags.add(nextTag); 287 reader.read(); // ',' or ')' 288 } 289 } while (nextTag != null); 290 reader.read(); // '[' 291 reader.mark(1); 292 ch = (char) reader.read(); 293 if (ch == ']') { 294 return new UnionCase(tags); 295 } 296 reader.reset(); 297 return new UnionCase(tags, readBody(getStreamUpToMatchingBracket(reader))); 298 } 299 300 /** 301 * An AttributeLayoutElement is a part of an attribute layout and has one or more bands associated with it, which 302 * transmit the AttributeElement data for successive Attributes of this type. 303 */ 304 public interface AttributeLayoutElement { 305 306 void addAttributeToBand(NewAttribute attribute, InputStream inputStream); 307 308 void pack(OutputStream ouputStream) throws IOException, Pack200Exception; 309 310 void renumberBci(IntList bciRenumbering, Map<Label, Integer> labelsToOffsets); 311 312 } 313 314 public abstract class LayoutElement implements AttributeLayoutElement { 315 316 protected int getLength(final char uint_type) { 317 int length = 0; 318 switch (uint_type) { 319 case 'B': 320 length = 1; 321 break; 322 case 'H': 323 length = 2; 324 break; 325 case 'I': 326 length = 4; 327 break; 328 case 'V': 329 length = 0; 330 break; 331 } 332 return length; 333 } 334 } 335 336 public class Integral extends LayoutElement { 337 338 private final String tag; 339 340 private final List band = new ArrayList(); 341 private final BHSDCodec defaultCodec; 342 343 // used for bytecode offsets (OH and POH) 344 private Integral previousIntegral; 345 private int previousPValue; 346 347 public Integral(final String tag) { 348 this.tag = tag; 349 this.defaultCodec = getCodec(tag); 350 } 351 352 public Integral(final String tag, final Integral previousIntegral) { 353 this.tag = tag; 354 this.defaultCodec = getCodec(tag); 355 this.previousIntegral = previousIntegral; 356 } 357 358 public String getTag() { 359 return tag; 360 } 361 362 @Override 363 public void addAttributeToBand(final NewAttribute attribute, final InputStream inputStream) { 364 Object val = null; 365 int value = 0; 366 if (tag.equals("B") || tag.equals("FB")) { 367 value = readInteger(1, inputStream) & 0xFF; // unsigned byte 368 } else if (tag.equals("SB")) { 369 value = readInteger(1, inputStream); 370 } else if (tag.equals("H") || tag.equals("FH")) { 371 value = readInteger(2, inputStream) & 0xFFFF; // unsigned short 372 } else if (tag.equals("SH")) { 373 value = readInteger(2, inputStream); 374 } else if (tag.equals("I") || tag.equals("FI")) { 375 value = readInteger(4, inputStream); 376 } else if (tag.equals("SI")) { 377 value = readInteger(4, inputStream); 378 } else if (tag.equals("V") || tag.equals("FV") || tag.equals("SV")) { 379 // Not currently supported 380 } else if (tag.startsWith("PO") || tag.startsWith("OS")) { 381 final char uint_type = tag.substring(2).toCharArray()[0]; 382 final int length = getLength(uint_type); 383 value = readInteger(length, inputStream); 384 value += previousIntegral.previousPValue; 385 val = attribute.getLabel(value); 386 previousPValue = value; 387 } else if (tag.startsWith("P")) { 388 final char uint_type = tag.substring(1).toCharArray()[0]; 389 final int length = getLength(uint_type); 390 value = readInteger(length, inputStream); 391 val = attribute.getLabel(value); 392 previousPValue = value; 393 } else if (tag.startsWith("O")) { 394 final char uint_type = tag.substring(1).toCharArray()[0]; 395 final int length = getLength(uint_type); 396 value = readInteger(length, inputStream); 397 value += previousIntegral.previousPValue; 398 val = attribute.getLabel(value); 399 previousPValue = value; 400 } 401 if (val == null) { 402 val = Integer.valueOf(value); 403 } 404 band.add(val); 405 } 406 407 @Override 408 public void pack(final OutputStream outputStream) throws IOException, Pack200Exception { 409 PackingUtils.log("Writing new attribute bands..."); 410 final byte[] encodedBand = encodeBandInt(tag, integerListToArray(band), defaultCodec); 411 outputStream.write(encodedBand); 412 PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + tag + "[" + band.size() + "]"); 413 } 414 415 public int latestValue() { 416 return ((Integer) band.get(band.size() - 1)).intValue(); 417 } 418 419 @Override 420 public void renumberBci(final IntList bciRenumbering, final Map<Label, Integer> labelsToOffsets) { 421 if (tag.startsWith("O") || tag.startsWith("PO")) { 422 renumberOffsetBci(previousIntegral.band, bciRenumbering, labelsToOffsets); 423 } else if (tag.startsWith("P")) { 424 for (int i = band.size() - 1; i >= 0; i--) { 425 final Object label = band.get(i); 426 if (label instanceof Integer) { 427 break; 428 } 429 if (label instanceof Label) { 430 band.remove(i); 431 final Integer bytecodeIndex = labelsToOffsets.get(label); 432 band.add(i, Integer.valueOf(bciRenumbering.get(bytecodeIndex.intValue()))); 433 } 434 } 435 } 436 } 437 438 private void renumberOffsetBci(final List relative, final IntList bciRenumbering, final Map<Label, Integer> labelsToOffsets) { 439 for (int i = band.size() - 1; i >= 0; i--) { 440 final Object label = band.get(i); 441 if (label instanceof Integer) { 442 break; 443 } 444 if (label instanceof Label) { 445 band.remove(i); 446 final Integer bytecodeIndex = labelsToOffsets.get(label); 447 final Integer renumberedOffset = Integer 448 .valueOf(bciRenumbering.get(bytecodeIndex.intValue()) - ((Integer) relative.get(i)).intValue()); 449 band.add(i, renumberedOffset); 450 } 451 } 452 } 453 454 } 455 456 /** 457 * A replication is an array of layout elements, with an associated count 458 */ 459 public class Replication extends LayoutElement { 460 461 private final Integral countElement; 462 463 private final List<LayoutElement> layoutElements = new ArrayList<>(); 464 465 public Integral getCountElement() { 466 return countElement; 467 } 468 469 public List<LayoutElement> getLayoutElements() { 470 return layoutElements; 471 } 472 473 public Replication(final String tag, final String contents) throws IOException { 474 this.countElement = new Integral(tag); 475 final StringReader stream = new StringReader(contents); 476 LayoutElement e; 477 while ((e = readNextLayoutElement(stream)) != null) { 478 layoutElements.add(e); 479 } 480 } 481 482 @Override 483 public void addAttributeToBand(final NewAttribute attribute, final InputStream inputStream) { 484 countElement.addAttributeToBand(attribute, inputStream); 485 final int count = countElement.latestValue(); 486 for (int i = 0; i < count; i++) { 487 for (AttributeLayoutElement layoutElement : layoutElements) { 488 layoutElement.addAttributeToBand(attribute, inputStream); 489 } 490 } 491 } 492 493 @Override 494 public void pack(final OutputStream out) throws IOException, Pack200Exception { 495 countElement.pack(out); 496 for (AttributeLayoutElement layoutElement : layoutElements) { 497 layoutElement.pack(out); 498 } 499 } 500 501 @Override 502 public void renumberBci(final IntList bciRenumbering, final Map<Label, Integer> labelsToOffsets) { 503 for (AttributeLayoutElement layoutElement : layoutElements) { 504 layoutElement.renumberBci(bciRenumbering, labelsToOffsets); 505 } 506 } 507 } 508 509 /** 510 * A Union is a type of layout element where the tag value acts as a selector for one of the union cases 511 */ 512 public class Union extends LayoutElement { 513 514 private final Integral unionTag; 515 private final List<UnionCase> unionCases; 516 private final List<LayoutElement> defaultCaseBody; 517 518 public Union(final String tag, final List<UnionCase> unionCases, final List<LayoutElement> body) { 519 this.unionTag = new Integral(tag); 520 this.unionCases = unionCases; 521 this.defaultCaseBody = body; 522 } 523 524 @Override 525 public void addAttributeToBand(final NewAttribute attribute, final InputStream inputStream) { 526 unionTag.addAttributeToBand(attribute, inputStream); 527 final long tag = unionTag.latestValue(); 528 boolean defaultCase = true; 529 for (UnionCase unionCase : unionCases) { 530 if (unionCase.hasTag(tag)) { 531 defaultCase = false; 532 unionCase.addAttributeToBand(attribute, inputStream); 533 } 534 } 535 if (defaultCase) { 536 for (LayoutElement layoutElement : defaultCaseBody) { 537 layoutElement.addAttributeToBand(attribute, inputStream); 538 } 539 } 540 } 541 542 @Override 543 public void pack(final OutputStream outputStream) throws IOException, Pack200Exception { 544 unionTag.pack(outputStream); 545 for (UnionCase unionCase : unionCases) { 546 unionCase.pack(outputStream); 547 } 548 for (LayoutElement element : defaultCaseBody) { 549 element.pack(outputStream); 550 } 551 } 552 553 @Override 554 public void renumberBci(final IntList bciRenumbering, final Map<Label, Integer> labelsToOffsets) { 555 for (UnionCase unionCase : unionCases) { 556 unionCase.renumberBci(bciRenumbering, labelsToOffsets); 557 } 558 for (LayoutElement element : defaultCaseBody) { 559 element.renumberBci(bciRenumbering, labelsToOffsets); 560 } 561 } 562 563 public Integral getUnionTag() { 564 return unionTag; 565 } 566 567 public List<UnionCase> getUnionCases() { 568 return unionCases; 569 } 570 571 public List<LayoutElement> getDefaultCaseBody() { 572 return defaultCaseBody; 573 } 574 } 575 576 public class Call extends LayoutElement { 577 578 private final int callableIndex; 579 private Callable callable; 580 581 public Call(final int callableIndex) { 582 this.callableIndex = callableIndex; 583 } 584 585 public void setCallable(final Callable callable) { 586 this.callable = callable; 587 if (callableIndex < 1) { 588 callable.setBackwardsCallable(); 589 } 590 } 591 592 @Override 593 public void addAttributeToBand(final NewAttribute attribute, final InputStream inputStream) { 594 callable.addAttributeToBand(attribute, inputStream); 595 if (callableIndex < 1) { 596 callable.addBackwardsCall(); 597 } 598 } 599 600 @Override 601 public void pack(final OutputStream outputStream) { 602 // do nothing here as pack will be called on the callable at another time 603 } 604 605 @Override 606 public void renumberBci(final IntList bciRenumbering, final Map<Label, Integer> labelsToOffsets) { 607 // do nothing here as renumberBci will be called on the callable at another time 608 } 609 610 public int getCallableIndex() { 611 return callableIndex; 612 } 613 614 public Callable getCallable() { 615 return callable; 616 } 617 } 618 619 /** 620 * Constant Pool Reference 621 */ 622 public class Reference extends LayoutElement { 623 624 private final String tag; 625 626 private List<ConstantPoolEntry> band; 627 628 private boolean nullsAllowed = false; 629 630 public Reference(final String tag) { 631 this.tag = tag; 632 nullsAllowed = tag.indexOf('N') != -1; 633 } 634 635 @Override 636 public void addAttributeToBand(final NewAttribute attribute, final InputStream inputStream) { 637 final int index = readInteger(4, inputStream); 638 if (tag.startsWith("RC")) { // Class 639 band.add(cpBands.getCPClass(attribute.readClass(index))); 640 } else if (tag.startsWith("RU")) { // UTF8 String 641 band.add(cpBands.getCPUtf8(attribute.readUTF8(index))); 642 } else if (tag.startsWith("RS")) { // Signature 643 band.add(cpBands.getCPSignature(attribute.readUTF8(index))); 644 } else { // Constant 645 band.add(cpBands.getConstant(attribute.readConst(index))); 646 } 647 // TODO method and field references 648 } 649 650 public String getTag() { 651 return tag; 652 } 653 654 @Override 655 public void pack(final OutputStream outputStream) throws IOException, Pack200Exception { 656 int[] ints; 657 if (nullsAllowed) { 658 ints = cpEntryOrNullListToArray(band); 659 } else { 660 ints = cpEntryListToArray(band); 661 } 662 final byte[] encodedBand = encodeBandInt(tag, ints, Codec.UNSIGNED5); 663 outputStream.write(encodedBand); 664 PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + tag + "[" + ints.length + "]"); 665 } 666 667 @Override 668 public void renumberBci(final IntList bciRenumbering, final Map<Label, Integer> labelsToOffsets) { 669 // nothing to do here 670 } 671 672 } 673 674 public class Callable implements AttributeLayoutElement { 675 676 private final List<LayoutElement> body; 677 678 private boolean isBackwardsCallable; 679 680 private int backwardsCallableIndex; 681 682 public Callable(final List<LayoutElement> body) { 683 this.body = body; 684 } 685 686 public void setBackwardsCallableIndex(final int backwardsCallableIndex) { 687 this.backwardsCallableIndex = backwardsCallableIndex; 688 } 689 690 public void addBackwardsCall() { 691 backwardsCallCounts[backwardsCallableIndex]++; 692 } 693 694 public boolean isBackwardsCallable() { 695 return isBackwardsCallable; 696 } 697 698 /** 699 * Tells this Callable that it is a backwards callable 700 */ 701 public void setBackwardsCallable() { 702 this.isBackwardsCallable = true; 703 } 704 705 @Override 706 public void addAttributeToBand(final NewAttribute attribute, final InputStream inputStream) { 707 for (AttributeLayoutElement element : body) { 708 element.addAttributeToBand(attribute, inputStream); 709 } 710 } 711 712 @Override 713 public void pack(final OutputStream outputStream) throws IOException, Pack200Exception { 714 for (AttributeLayoutElement element : body) { 715 element.pack(outputStream); 716 } 717 } 718 719 @Override 720 public void renumberBci(final IntList bciRenumbering, final Map<Label, Integer> labelsToOffsets) { 721 for (AttributeLayoutElement element : body) { 722 element.renumberBci(bciRenumbering, labelsToOffsets); 723 } 724 } 725 726 public List<LayoutElement> getBody() { 727 return body; 728 } 729 } 730 731 /** 732 * A Union case 733 */ 734 public class UnionCase extends LayoutElement { 735 736 private final List<LayoutElement> body; 737 738 private final List<Integer> tags; 739 740 public UnionCase(final List<Integer> tags) { 741 this.tags = tags; 742 this.body = Collections.EMPTY_LIST; 743 } 744 745 public boolean hasTag(final long l) { 746 return tags.contains(Integer.valueOf((int) l)); 747 } 748 749 public UnionCase(final List<Integer> tags, final List<LayoutElement> body) { 750 this.tags = tags; 751 this.body = body; 752 } 753 754 @Override 755 public void addAttributeToBand(final NewAttribute attribute, final InputStream inputStream) { 756 for (LayoutElement element : body) { 757 element.addAttributeToBand(attribute, inputStream); 758 } 759 } 760 761 @Override 762 public void pack(final OutputStream outputStream) throws IOException, Pack200Exception { 763 for (LayoutElement element : body) { 764 element.pack(outputStream); 765 } 766 } 767 768 @Override 769 public void renumberBci(final IntList bciRenumbering, final Map<Label, Integer> labelsToOffsets) { 770 for (LayoutElement element : body) { 771 element.renumberBci(bciRenumbering, labelsToOffsets); 772 } 773 } 774 775 public List<LayoutElement> getBody() { 776 return body; 777 } 778 } 779 780 /** 781 * Utility method to get the contents of the given stream, up to the next ']', (ignoring pairs of brackets '[' and 782 * ']') 783 * 784 * @param reader 785 * @return 786 * @throws IOException If an I/O error occurs. 787 */ 788 private StringReader getStreamUpToMatchingBracket(final StringReader reader) throws IOException { 789 final StringBuilder sb = new StringBuilder(); 790 int foundBracket = -1; 791 while (foundBracket != 0) { 792 int read = reader.read(); 793 if (read == -1) { 794 break; 795 } 796 final char c = (char) read; 797 if (c == ']') { 798 foundBracket++; 799 } 800 if (c == '[') { 801 foundBracket--; 802 } 803 if (!(foundBracket == 0)) { 804 sb.append(c); 805 } 806 } 807 return new StringReader(sb.toString()); 808 } 809 810 private int readInteger(final int i, final InputStream inputStream) { 811 int result = 0; 812 for (int j = 0; j < i; j++) { 813 try { 814 result = result << 8 | inputStream.read(); 815 } catch (final IOException e) { 816 throw new UncheckedIOException("Error reading unknown attribute", e); 817 } 818 } 819 // use casting to preserve sign 820 if (i == 1) { 821 result = (byte) result; 822 } 823 if (i == 2) { 824 result = (short) result; 825 } 826 return result; 827 } 828 829 /** 830 * Returns the {@link BHSDCodec} that should be used for the given layout element 831 * 832 * @param layoutElement 833 */ 834 private BHSDCodec getCodec(final String layoutElement) { 835 if (layoutElement.indexOf('O') >= 0) { 836 return Codec.BRANCH5; 837 } 838 if (layoutElement.indexOf('P') >= 0) { 839 return Codec.BCI5; 840 } 841 if (layoutElement.indexOf('S') >= 0 && layoutElement.indexOf("KS") < 0 //$NON-NLS-1$ 842 && layoutElement.indexOf("RS") < 0) { //$NON-NLS-1$ 843 return Codec.SIGNED5; 844 } 845 if (layoutElement.indexOf('B') >= 0) { 846 return Codec.BYTE1; 847 } 848 return Codec.UNSIGNED5; 849 } 850 851 /** 852 * Utility method to get the contents of the given stream, up to the next ']', (ignoring pairs of brackets '[' and 853 * ']') 854 * 855 * @param reader 856 * @return 857 * @throws IOException If an I/O error occurs. 858 */ 859 private String readUpToMatchingBracket(final StringReader reader) throws IOException { 860 final StringBuilder sb = new StringBuilder(); 861 int foundBracket = -1; 862 while (foundBracket != 0) { 863 int read = reader.read(); 864 if (read == -1) { 865 break; 866 } 867 final char c = (char) read; 868 if (c == ']') { 869 foundBracket++; 870 } 871 if (c == '[') { 872 foundBracket--; 873 } 874 if (!(foundBracket == 0)) { 875 sb.append(c); 876 } 877 } 878 return sb.toString(); 879 } 880 881 /** 882 * Read a number from the stream and return it 883 * 884 * @param stream 885 * @return 886 * @throws IOException If an I/O error occurs. 887 */ 888 private Integer readNumber(final StringReader stream) throws IOException { 889 stream.mark(1); 890 final char first = (char) stream.read(); 891 final boolean negative = first == '-'; 892 if (!negative) { 893 stream.reset(); 894 } 895 stream.mark(100); 896 int i; 897 int length = 0; 898 while ((i = (stream.read())) != -1 && Character.isDigit((char) i)) { 899 length++; 900 } 901 stream.reset(); 902 if (length == 0) { 903 return null; 904 } 905 final char[] digits = new char[length]; 906 final int read = stream.read(digits); 907 if (read != digits.length) { 908 throw new IOException("Error reading from the input stream"); 909 } 910 return Integer.valueOf(Integer.parseInt((negative ? "-" : "") + new String(digits))); 911 } 912 913 /** 914 * Read a 'body' section of the layout from the given stream 915 * 916 * @param reader 917 * @return List of LayoutElements 918 * @throws IOException If an I/O error occurs. 919 */ 920 private List<LayoutElement> readBody(final StringReader reader) throws IOException { 921 final List<LayoutElement> layoutElements = new ArrayList<>(); 922 LayoutElement e; 923 while ((e = readNextLayoutElement(reader)) != null) { 924 layoutElements.add(e); 925 } 926 return layoutElements; 927 } 928 929 /** 930 * Renumber any bytecode indexes or offsets as described in section 5.5.2 of the pack200 specification 931 * 932 * @param bciRenumbering TODO 933 * @param labelsToOffsets TODO 934 */ 935 public void renumberBci(final IntList bciRenumbering, final Map<Label, Integer> labelsToOffsets) { 936 for (AttributeLayoutElement attributeLayoutElement : attributeLayoutElements) { 937 attributeLayoutElement.renumberBci(bciRenumbering, labelsToOffsets); 938 } 939 } 940 941}