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