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