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