1 /* 2 * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.java.util.jar.pack; 27 28 import com.sun.java.util.jar.pack.ConstantPool.Entry; 29 import com.sun.java.util.jar.pack.ConstantPool.Index; 30 import java.io.ByteArrayOutputStream; 31 import java.io.IOException; 32 import java.util.ArrayList; 33 import java.util.Arrays; 34 import java.util.Collection; 35 import java.util.Collections; 36 import java.util.HashMap; 37 import java.util.List; 38 import java.util.Map; 39 import static com.sun.java.util.jar.pack.Constants.*; 40 41 /** 42 * Represents an attribute in a class-file. 43 * Takes care to remember where constant pool indexes occur. 44 * Implements the "little language" of Pack200 for describing 45 * attribute layouts. 46 * @author John Rose 47 */ 48 class Attribute implements Comparable<Attribute> { 49 // Attribute instance fields. 50 51 Layout def; // the name and format of this attr 52 byte[] bytes; // the actual bytes 53 Object fixups; // reference relocations, if any are required 54 55 public String name() { return def.name(); } 56 public Layout layout() { return def; } 57 public byte[] bytes() { return bytes; } 58 public int size() { return bytes.length; } 59 public Entry getNameRef() { return def.getNameRef(); } 60 61 private Attribute(Attribute old) { 62 this.def = old.def; 63 this.bytes = old.bytes; 64 this.fixups = old.fixups; 65 } 66 67 public Attribute(Layout def, byte[] bytes, Object fixups) { 68 this.def = def; 69 this.bytes = bytes; 70 this.fixups = fixups; 71 Fixups.setBytes(fixups, bytes); 72 } 73 public Attribute(Layout def, byte[] bytes) { 74 this(def, bytes, null); 75 } 76 77 public Attribute addContent(byte[] bytes, Object fixups) { 78 assert(isCanonical()); 79 if (bytes.length == 0 && fixups == null) 80 return this; 81 Attribute res = new Attribute(this); 82 res.bytes = bytes; 83 res.fixups = fixups; 84 Fixups.setBytes(fixups, bytes); 85 return res; 86 } 87 public Attribute addContent(byte[] bytes) { 88 return addContent(bytes, null); 89 } 90 91 public void finishRefs(Index ix) { 92 if (fixups != null) { 93 Fixups.finishRefs(fixups, bytes, ix); 94 fixups = null; 95 } 96 } 97 98 public boolean isCanonical() { 99 return this == def.canon; 100 } 101 102 public int compareTo(Attribute that) { 103 return this.def.compareTo(that.def); 104 } 105 106 private static final Map<List<Attribute>, List<Attribute>> canonLists = new HashMap<>(); 107 private static final Map<Layout, Attribute> attributes = new HashMap<>(); 108 private static final Map<Layout, Attribute> standardDefs = new HashMap<>(); 109 110 // Canonicalized lists of trivial attrs (Deprecated, etc.) 111 // are used by trimToSize, in order to reduce footprint 112 // of some common cases. (Note that Code attributes are 113 // always zero size.) 114 public static List<Attribute> getCanonList(List<Attribute> al) { 115 synchronized (canonLists) { 116 List<Attribute> cl = canonLists.get(al); 117 if (cl == null) { 118 cl = new ArrayList<>(al.size()); 119 cl.addAll(al); 120 cl = Collections.unmodifiableList(cl); 121 canonLists.put(al, cl); 122 } 123 return cl; 124 } 125 } 126 127 // Find the canonical empty attribute with the given ctype, name, layout. 128 public static Attribute find(int ctype, String name, String layout) { 129 Layout key = Layout.makeKey(ctype, name, layout); 130 synchronized (attributes) { 131 Attribute a = attributes.get(key); 132 if (a == null) { 133 a = new Layout(ctype, name, layout).canonicalInstance(); 134 attributes.put(key, a); 135 } 136 return a; 137 } 138 } 139 140 public static Layout keyForLookup(int ctype, String name) { 141 return Layout.makeKey(ctype, name); 142 } 143 144 // Find canonical empty attribute with given ctype and name, 145 // and with the standard layout. 146 public static Attribute lookup(Map<Layout, Attribute> defs, int ctype, 147 String name) { 148 if (defs == null) { 149 defs = standardDefs; 150 } 151 return defs.get(Layout.makeKey(ctype, name)); 152 } 153 154 public static Attribute define(Map<Layout, Attribute> defs, int ctype, 155 String name, String layout) { 156 Attribute a = find(ctype, name, layout); 157 defs.put(Layout.makeKey(ctype, name), a); 158 return a; 159 } 160 161 static { 162 Map<Layout, Attribute> sd = standardDefs; 163 define(sd, ATTR_CONTEXT_CLASS, "Signature", "RSH"); 164 define(sd, ATTR_CONTEXT_CLASS, "Synthetic", ""); 165 define(sd, ATTR_CONTEXT_CLASS, "Deprecated", ""); 166 define(sd, ATTR_CONTEXT_CLASS, "SourceFile", "RUH"); 167 define(sd, ATTR_CONTEXT_CLASS, "EnclosingMethod", "RCHRDNH"); 168 define(sd, ATTR_CONTEXT_CLASS, "InnerClasses", "NH[RCHRCNHRUNHFH]"); 169 define(sd, ATTR_CONTEXT_CLASS, "BootstrapMethods", "NH[RMHNH[KLH]]"); 170 171 define(sd, ATTR_CONTEXT_FIELD, "Signature", "RSH"); 172 define(sd, ATTR_CONTEXT_FIELD, "Synthetic", ""); 173 define(sd, ATTR_CONTEXT_FIELD, "Deprecated", ""); 174 define(sd, ATTR_CONTEXT_FIELD, "ConstantValue", "KQH"); 175 176 define(sd, ATTR_CONTEXT_METHOD, "Signature", "RSH"); 177 define(sd, ATTR_CONTEXT_METHOD, "Synthetic", ""); 178 define(sd, ATTR_CONTEXT_METHOD, "Deprecated", ""); 179 define(sd, ATTR_CONTEXT_METHOD, "Exceptions", "NH[RCH]"); 180 //define(sd, ATTR_CONTEXT_METHOD, "Code", "HHNI[B]NH[PHPOHPOHRCNH]NH[RUHNI[B]]"); 181 182 define(sd, ATTR_CONTEXT_CODE, "StackMapTable", 183 ("[NH[(1)]]" + 184 "[TB" + 185 "(64-127)[(2)]" + 186 "(247)[(1)(2)]" + 187 "(248-251)[(1)]" + 188 "(252)[(1)(2)]" + 189 "(253)[(1)(2)(2)]" + 190 "(254)[(1)(2)(2)(2)]" + 191 "(255)[(1)NH[(2)]NH[(2)]]" + 192 "()[]" + 193 "]" + 194 "[H]" + 195 "[TB(7)[RCH](8)[PH]()[]]")); 196 197 define(sd, ATTR_CONTEXT_CODE, "LineNumberTable", "NH[PHH]"); 198 define(sd, ATTR_CONTEXT_CODE, "LocalVariableTable", "NH[PHOHRUHRSHH]"); 199 define(sd, ATTR_CONTEXT_CODE, "LocalVariableTypeTable", "NH[PHOHRUHRSHH]"); 200 //define(sd, ATTR_CONTEXT_CODE, "CharacterRangeTable", "NH[PHPOHIIH]"); 201 //define(sd, ATTR_CONTEXT_CODE, "CoverageTable", "NH[PHHII]"); 202 203 // Note: Code and InnerClasses are special-cased elsewhere. 204 // Their layout specs. are given here for completeness. 205 // The Code spec is incomplete, in that it does not distinguish 206 // bytecode bytes or locate CP references. 207 // The BootstrapMethods attribute is also special-cased 208 // elsewhere as an appendix to the local constant pool. 209 } 210 211 // Metadata. 212 // 213 // We define metadata using similar layouts 214 // for all five kinds of metadata attributes. 215 // 216 // Regular annotations are a counted list of [RSHNH[RUH(1)]][...] 217 // pack.method.attribute.RuntimeVisibleAnnotations=[NH[(1)]][RSHNH[RUH(1)]][TB...] 218 // 219 // Parameter annotations are a counted list of regular annotations. 220 // pack.method.attribute.RuntimeVisibleParameterAnnotations=[NH[(1)]][NH[(1)]][RSHNH[RUH(1)]][TB...] 221 // 222 // RuntimeInvisible annotations are defined similarly... 223 // Non-method annotations are defined similarly... 224 // 225 // Annotation are a simple tagged value [TB...] 226 // pack.attribute.method.AnnotationDefault=[TB...] 227 // 228 static { 229 String mdLayouts[] = { 230 Attribute.normalizeLayoutString 231 ("" 232 +"\n # parameter_annotations :=" 233 +"\n [ NB[(1)] ] # forward call to annotations" 234 ), 235 Attribute.normalizeLayoutString 236 ("" 237 +"\n # annotations :=" 238 +"\n [ NH[(1)] ] # forward call to annotation" 239 +"\n " 240 +"\n # annotation :=" 241 +"\n [RSH" 242 +"\n NH[RUH (1)] # forward call to value" 243 +"\n ]" 244 ), 245 Attribute.normalizeLayoutString 246 ("" 247 +"\n # value :=" 248 +"\n [TB # Callable 2 encodes one tagged value." 249 +"\n (\\B,\\C,\\I,\\S,\\Z)[KIH]" 250 +"\n (\\D)[KDH]" 251 +"\n (\\F)[KFH]" 252 +"\n (\\J)[KJH]" 253 +"\n (\\c)[RSH]" 254 +"\n (\\e)[RSH RUH]" 255 +"\n (\\s)[RUH]" 256 +"\n (\\[)[NH[(0)]] # backward self-call to value" 257 +"\n (\\@)[RSH NH[RUH (0)]] # backward self-call to value" 258 +"\n ()[] ]" 259 ) 260 }; 261 Map<Layout, Attribute> sd = standardDefs; 262 String defaultLayout = mdLayouts[2]; 263 String annotationsLayout = mdLayouts[1] + mdLayouts[2]; 264 String paramsLayout = mdLayouts[0] + annotationsLayout; 265 for (int ctype = 0; ctype < ATTR_CONTEXT_LIMIT; ctype++) { 266 if (ctype == ATTR_CONTEXT_CODE) continue; 267 define(sd, ctype, 268 "RuntimeVisibleAnnotations", annotationsLayout); 269 define(sd, ctype, 270 "RuntimeInvisibleAnnotations", annotationsLayout); 271 if (ctype == ATTR_CONTEXT_METHOD) { 272 define(sd, ctype, 273 "RuntimeVisibleParameterAnnotations", paramsLayout); 274 define(sd, ctype, 275 "RuntimeInvisibleParameterAnnotations", paramsLayout); 276 define(sd, ctype, 277 "AnnotationDefault", defaultLayout); 278 } 279 } 280 } 281 282 public static String contextName(int ctype) { 283 switch (ctype) { 284 case ATTR_CONTEXT_CLASS: return "class"; 285 case ATTR_CONTEXT_FIELD: return "field"; 286 case ATTR_CONTEXT_METHOD: return "method"; 287 case ATTR_CONTEXT_CODE: return "code"; 288 } 289 return null; 290 } 291 292 /** Base class for any attributed object (Class, Field, Method, Code). 293 * Flags are included because they are used to help transmit the 294 * presence of attributes. That is, flags are a mix of modifier 295 * bits and attribute indicators. 296 */ 297 public static abstract 298 class Holder { 299 300 // We need this abstract method to interpret embedded CP refs. 301 protected abstract Entry[] getCPMap(); 302 303 protected int flags; // defined here for convenience 304 protected List<Attribute> attributes; 305 306 public int attributeSize() { 307 return (attributes == null) ? 0 : attributes.size(); 308 } 309 310 public void trimToSize() { 311 if (attributes == null) { 312 return; 313 } 314 if (attributes.isEmpty()) { 315 attributes = null; 316 return; 317 } 318 if (attributes instanceof ArrayList) { 319 ArrayList<Attribute> al = (ArrayList<Attribute>)attributes; 320 al.trimToSize(); 321 boolean allCanon = true; 322 for (Attribute a : al) { 323 if (!a.isCanonical()) { 324 allCanon = false; 325 } 326 if (a.fixups != null) { 327 assert(!a.isCanonical()); 328 a.fixups = Fixups.trimToSize(a.fixups); 329 } 330 } 331 if (allCanon) { 332 // Replace private writable attribute list 333 // with only trivial entries by public unique 334 // immutable attribute list with the same entries. 335 attributes = getCanonList(al); 336 } 337 } 338 } 339 340 public void addAttribute(Attribute a) { 341 if (attributes == null) 342 attributes = new ArrayList<>(3); 343 else if (!(attributes instanceof ArrayList)) 344 attributes = new ArrayList<>(attributes); // unfreeze it 345 attributes.add(a); 346 } 347 348 public Attribute removeAttribute(Attribute a) { 349 if (attributes == null) return null; 350 if (!attributes.contains(a)) return null; 351 if (!(attributes instanceof ArrayList)) 352 attributes = new ArrayList<>(attributes); // unfreeze it 353 attributes.remove(a); 354 return a; 355 } 356 357 public Attribute getAttribute(int n) { 358 return attributes.get(n); 359 } 360 361 protected void visitRefs(int mode, Collection<Entry> refs) { 362 if (attributes == null) return; 363 for (Attribute a : attributes) { 364 a.visitRefs(this, mode, refs); 365 } 366 } 367 368 static final List<Attribute> noAttributes = Arrays.asList(new Attribute[0]); 369 370 public List<Attribute> getAttributes() { 371 if (attributes == null) 372 return noAttributes; 373 return attributes; 374 } 375 376 public void setAttributes(List<Attribute> attrList) { 377 if (attrList.isEmpty()) 378 attributes = null; 379 else 380 attributes = attrList; 381 } 382 383 public Attribute getAttribute(String attrName) { 384 if (attributes == null) return null; 385 for (Attribute a : attributes) { 386 if (a.name().equals(attrName)) 387 return a; 388 } 389 return null; 390 } 391 392 public Attribute getAttribute(Layout attrDef) { 393 if (attributes == null) return null; 394 for (Attribute a : attributes) { 395 if (a.layout() == attrDef) 396 return a; 397 } 398 return null; 399 } 400 401 public Attribute removeAttribute(String attrName) { 402 return removeAttribute(getAttribute(attrName)); 403 } 404 405 public Attribute removeAttribute(Layout attrDef) { 406 return removeAttribute(getAttribute(attrDef)); 407 } 408 409 public void strip(String attrName) { 410 removeAttribute(getAttribute(attrName)); 411 } 412 } 413 414 // Lightweight interface to hide details of band structure. 415 // Also used for testing. 416 public static abstract 417 class ValueStream { 418 public int getInt(int bandIndex) { throw undef(); } 419 public void putInt(int bandIndex, int value) { throw undef(); } 420 public Entry getRef(int bandIndex) { throw undef(); } 421 public void putRef(int bandIndex, Entry ref) { throw undef(); } 422 // Note: decodeBCI goes w/ getInt/Ref; encodeBCI goes w/ putInt/Ref 423 public int decodeBCI(int bciCode) { throw undef(); } 424 public int encodeBCI(int bci) { throw undef(); } 425 public void noteBackCall(int whichCallable) { /* ignore by default */ } 426 private RuntimeException undef() { 427 return new UnsupportedOperationException("ValueStream method"); 428 } 429 } 430 431 // Element kinds: 432 static final byte EK_INT = 1; // B H I SH etc. 433 static final byte EK_BCI = 2; // PH POH etc. 434 static final byte EK_BCO = 3; // OH etc. 435 static final byte EK_FLAG = 4; // FH etc. 436 static final byte EK_REPL = 5; // NH[...] etc. 437 static final byte EK_REF = 6; // RUH, RUNH, KQH, etc. 438 static final byte EK_UN = 7; // TB(...)[...] etc. 439 static final byte EK_CASE = 8; // (...)[...] etc. 440 static final byte EK_CALL = 9; // (0), (1), etc. 441 static final byte EK_CBLE = 10; // [...][...] etc. 442 static final byte EF_SIGN = 1<<0; // INT is signed 443 static final byte EF_DELTA = 1<<1; // BCI/BCI value is diff'ed w/ previous 444 static final byte EF_NULL = 1<<2; // null REF is expected/allowed 445 static final byte EF_BACK = 1<<3; // call, callable, case is backward 446 static final int NO_BAND_INDEX = -1; 447 448 /** A "class" of attributes, characterized by a context-type, name 449 * and format. The formats are specified in a "little language". 450 */ 451 public static 452 class Layout implements Comparable<Layout> { 453 int ctype; // attribute context type, e.g., ATTR_CONTEXT_CODE 454 String name; // name of attribute 455 boolean hasRefs; // this kind of attr contains CP refs? 456 String layout; // layout specification 457 int bandCount; // total number of elems 458 Element[] elems; // tokenization of layout 459 Attribute canon; // canonical instance of this layout 460 461 public int ctype() { return ctype; } 462 public String name() { return name; } 463 public String layout() { return layout; } 464 public Attribute canonicalInstance() { return canon; } 465 466 public Entry getNameRef() { 467 return ConstantPool.getUtf8Entry(name()); 468 } 469 470 public boolean isEmpty() { 471 return layout.isEmpty(); 472 } 473 474 public Layout(int ctype, String name, String layout) { 475 this.ctype = ctype; 476 this.name = name.intern(); 477 this.layout = layout.intern(); 478 assert(ctype < ATTR_CONTEXT_LIMIT); 479 boolean hasCallables = layout.startsWith("["); 480 try { 481 if (!hasCallables) { 482 this.elems = tokenizeLayout(this, -1, layout); 483 } else { 484 String[] bodies = splitBodies(layout); 485 // Make the callables now, so they can be linked immediately. 486 Element[] lelems = new Element[bodies.length]; 487 this.elems = lelems; 488 for (int i = 0; i < lelems.length; i++) { 489 Element ce = this.new Element(); 490 ce.kind = EK_CBLE; 491 ce.removeBand(); 492 ce.bandIndex = NO_BAND_INDEX; 493 ce.layout = bodies[i]; 494 lelems[i] = ce; 495 } 496 // Next fill them in. 497 for (int i = 0; i < lelems.length; i++) { 498 Element ce = lelems[i]; 499 ce.body = tokenizeLayout(this, i, bodies[i]); 500 } 501 //System.out.println(Arrays.asList(elems)); 502 } 503 } catch (StringIndexOutOfBoundsException ee) { 504 // simplest way to catch syntax errors... 505 throw new RuntimeException("Bad attribute layout: "+layout, ee); 506 } 507 // Some uses do not make a fresh one for each occurrence. 508 // For example, if layout == "", we only need one attr to share. 509 canon = new Attribute(this, noBytes); 510 } 511 private Layout() {} 512 static Layout makeKey(int ctype, String name, String layout) { 513 Layout def = new Layout(); 514 def.ctype = ctype; 515 def.name = name.intern(); 516 def.layout = layout.intern(); 517 assert(ctype < ATTR_CONTEXT_LIMIT); 518 return def; 519 } 520 static Layout makeKey(int ctype, String name) { 521 return makeKey(ctype, name, ""); 522 } 523 524 public Attribute addContent(byte[] bytes, Object fixups) { 525 return canon.addContent(bytes, fixups); 526 } 527 public Attribute addContent(byte[] bytes) { 528 return canon.addContent(bytes, null); 529 } 530 531 public boolean equals(Object x) { 532 return ( x != null) && ( x.getClass() == Layout.class ) && 533 equals((Layout)x); 534 } 535 public boolean equals(Layout that) { 536 return this.name.equals(that.name) 537 && this.layout.equals(that.layout) 538 && this.ctype == that.ctype; 539 } 540 public int hashCode() { 541 return (((17 + name.hashCode()) 542 * 37 + layout.hashCode()) 543 * 37 + ctype); 544 } 545 public int compareTo(Layout that) { 546 int r; 547 r = this.name.compareTo(that.name); 548 if (r != 0) return r; 549 r = this.layout.compareTo(that.layout); 550 if (r != 0) return r; 551 return this.ctype - that.ctype; 552 } 553 public String toString() { 554 String str = contextName(ctype)+"."+name+"["+layout+"]"; 555 // If -ea, print out more informative strings! 556 assert((str = stringForDebug()) != null); 557 return str; 558 } 559 private String stringForDebug() { 560 return contextName(ctype)+"."+name+Arrays.asList(elems); 561 } 562 563 public 564 class Element { 565 String layout; // spelling in the little language 566 byte flags; // EF_SIGN, etc. 567 byte kind; // EK_UINT, etc. 568 byte len; // scalar length of element 569 byte refKind; // CONSTANT_String, etc. 570 int bandIndex; // which band does this element govern? 571 int value; // extra parameter 572 Element[] body; // extra data (for replications, unions, calls) 573 574 boolean flagTest(byte mask) { return (flags & mask) != 0; } 575 576 Element() { 577 bandIndex = bandCount++; 578 } 579 580 void removeBand() { 581 --bandCount; 582 assert(bandIndex == bandCount); 583 bandIndex = NO_BAND_INDEX; 584 } 585 586 public boolean hasBand() { 587 return bandIndex >= 0; 588 } 589 public String toString() { 590 String str = layout; 591 // If -ea, print out more informative strings! 592 assert((str = stringForDebug()) != null); 593 return str; 594 } 595 private String stringForDebug() { 596 Element[] lbody = this.body; 597 switch (kind) { 598 case EK_CALL: 599 lbody = null; 600 break; 601 case EK_CASE: 602 if (flagTest(EF_BACK)) 603 lbody = null; 604 break; 605 } 606 return layout 607 + (!hasBand()?"":"#"+bandIndex) 608 + "<"+ (flags==0?"":""+flags)+kind+len 609 + (refKind==0?"":""+refKind) + ">" 610 + (value==0?"":"("+value+")") 611 + (lbody==null?"": ""+Arrays.asList(lbody)); 612 } 613 } 614 615 public boolean hasCallables() { 616 return (elems.length > 0 && elems[0].kind == EK_CBLE); 617 } 618 static private final Element[] noElems = {}; 619 public Element[] getCallables() { 620 if (hasCallables()) { 621 Element[] nelems = Arrays.copyOf(elems, elems.length); 622 return nelems; 623 } else 624 return noElems; // no callables at all 625 } 626 public Element[] getEntryPoint() { 627 if (hasCallables()) 628 return elems[0].body; // body of first callable 629 else { 630 Element[] nelems = Arrays.copyOf(elems, elems.length); 631 return nelems; // no callables; whole body 632 } 633 } 634 635 /** Return a sequence of tokens from the given attribute bytes. 636 * Sequence elements will be 1-1 correspondent with my layout tokens. 637 */ 638 public void parse(Holder holder, 639 byte[] bytes, int pos, int len, ValueStream out) { 640 int end = parseUsing(getEntryPoint(), 641 holder, bytes, pos, len, out); 642 if (end != pos + len) 643 throw new InternalError("layout parsed "+(end-pos)+" out of "+len+" bytes"); 644 } 645 /** Given a sequence of tokens, return the attribute bytes. 646 * Sequence elements must be 1-1 correspondent with my layout tokens. 647 * The returned object is a cookie for Fixups.finishRefs, which 648 * must be used to harden any references into integer indexes. 649 */ 650 public Object unparse(ValueStream in, ByteArrayOutputStream out) { 651 Object[] fixups = { null }; 652 unparseUsing(getEntryPoint(), fixups, in, out); 653 return fixups[0]; // return ref-bearing cookie, if any 654 } 655 656 public String layoutForClassVersion(Package.Version vers) { 657 if (vers.lessThan(JAVA6_MAX_CLASS_VERSION)) { 658 // Disallow layout syntax in the oldest protocol version. 659 return expandCaseDashNotation(layout); 660 } 661 return layout; 662 } 663 } 664 665 public static 666 class FormatException extends IOException { 667 private static final long serialVersionUID = -2542243830788066513L; 668 669 private int ctype; 670 private String name; 671 String layout; 672 public FormatException(String message, 673 int ctype, String name, String layout) { 674 super(ATTR_CONTEXT_NAME[ctype]+ " attribute \"" + name + "\"" + 675 (message == null? "" : (": " + message))); 676 this.ctype = ctype; 677 this.name = name; 678 this.layout = layout; 679 } 680 public FormatException(String message, 681 int ctype, String name) { 682 this(message, ctype, name, null); 683 } 684 } 685 686 void visitRefs(Holder holder, int mode, final Collection<Entry> refs) { 687 if (mode == VRM_CLASSIC) { 688 refs.add(getNameRef()); 689 } 690 // else the name is owned by the layout, and is processed elsewhere 691 if (bytes.length == 0) return; // quick exit 692 if (!def.hasRefs) return; // quick exit 693 if (fixups != null) { 694 Fixups.visitRefs(fixups, refs); 695 return; 696 } 697 // References (to a local cpMap) are embedded in the bytes. 698 def.parse(holder, bytes, 0, bytes.length, 699 new ValueStream() { 700 public void putInt(int bandIndex, int value) { 701 } 702 public void putRef(int bandIndex, Entry ref) { 703 refs.add(ref); 704 } 705 public int encodeBCI(int bci) { 706 return bci; 707 } 708 }); 709 } 710 711 public void parse(Holder holder, byte[] bytes, int pos, int len, ValueStream out) { 712 def.parse(holder, bytes, pos, len, out); 713 } 714 public Object unparse(ValueStream in, ByteArrayOutputStream out) { 715 return def.unparse(in, out); 716 } 717 718 public String toString() { 719 return def 720 +"{"+(bytes == null ? -1 : size())+"}" 721 +(fixups == null? "": fixups.toString()); 722 } 723 724 /** Remove any informal "pretty printing" from the layout string. 725 * Removes blanks and control chars. 726 * Removes '#' comments (to end of line). 727 * Replaces '\c' by the decimal code of the character c. 728 * Replaces '0xNNN' by the decimal code of the hex number NNN. 729 */ 730 static public 731 String normalizeLayoutString(String layout) { 732 StringBuilder buf = new StringBuilder(); 733 for (int i = 0, len = layout.length(); i < len; ) { 734 char ch = layout.charAt(i++); 735 if (ch <= ' ') { 736 // Skip whitespace and control chars 737 continue; 738 } else if (ch == '#') { 739 // Skip to end of line. 740 int end1 = layout.indexOf('\n', i); 741 int end2 = layout.indexOf('\r', i); 742 if (end1 < 0) end1 = len; 743 if (end2 < 0) end2 = len; 744 i = Math.min(end1, end2); 745 } else if (ch == '\\') { 746 // Map a character reference to its decimal code. 747 buf.append((int) layout.charAt(i++)); 748 } else if (ch == '0' && layout.startsWith("0x", i-1)) { 749 // Map a hex numeral to its decimal code. 750 int start = i-1; 751 int end = start+2; 752 while (end < len) { 753 int dig = layout.charAt(end); 754 if ((dig >= '0' && dig <= '9') || 755 (dig >= 'a' && dig <= 'f')) 756 ++end; 757 else 758 break; 759 } 760 if (end > start) { 761 String num = layout.substring(start, end); 762 buf.append(Integer.decode(num)); 763 i = end; 764 } else { 765 buf.append(ch); 766 } 767 } else { 768 buf.append(ch); 769 } 770 } 771 String result = buf.toString(); 772 if (false && !result.equals(layout)) { 773 Utils.log.info("Normalizing layout string"); 774 Utils.log.info(" From: "+layout); 775 Utils.log.info(" To: "+result); 776 } 777 return result; 778 } 779 780 /// Subroutines for parsing and unparsing: 781 782 /** Parse the attribute layout language. 783 <pre> 784 attribute_layout: 785 ( layout_element )* | ( callable )+ 786 layout_element: 787 ( integral | replication | union | call | reference ) 788 789 callable: 790 '[' body ']' 791 body: 792 ( layout_element )+ 793 794 integral: 795 ( unsigned_int | signed_int | bc_index | bc_offset | flag ) 796 unsigned_int: 797 uint_type 798 signed_int: 799 'S' uint_type 800 any_int: 801 ( unsigned_int | signed_int ) 802 bc_index: 803 ( 'P' uint_type | 'PO' uint_type ) 804 bc_offset: 805 'O' any_int 806 flag: 807 'F' uint_type 808 uint_type: 809 ( 'B' | 'H' | 'I' | 'V' ) 810 811 replication: 812 'N' uint_type '[' body ']' 813 814 union: 815 'T' any_int (union_case)* '(' ')' '[' (body)? ']' 816 union_case: 817 '(' union_case_tag (',' union_case_tag)* ')' '[' (body)? ']' 818 union_case_tag: 819 ( numeral | numeral '-' numeral ) 820 call: 821 '(' numeral ')' 822 823 reference: 824 reference_type ( 'N' )? uint_type 825 reference_type: 826 ( constant_ref | schema_ref | utf8_ref | untyped_ref ) 827 constant_ref: 828 ( 'KI' | 'KJ' | 'KF' | 'KD' | 'KS' | 'KQ' | 'KM' | 'KT' | 'KL' ) 829 schema_ref: 830 ( 'RC' | 'RS' | 'RD' | 'RF' | 'RM' | 'RI' | 'RY' | 'RB' | 'RN' ) 831 utf8_ref: 832 'RU' 833 untyped_ref: 834 'RQ' 835 836 numeral: 837 '(' ('-')? (digit)+ ')' 838 digit: 839 ( '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' ) 840 </pre> 841 */ 842 static //private 843 Layout.Element[] tokenizeLayout(Layout self, int curCble, String layout) { 844 List<Layout.Element> col = new ArrayList<>(layout.length()); 845 tokenizeLayout(self, curCble, layout, col); 846 Layout.Element[] res = new Layout.Element[col.size()]; 847 col.toArray(res); 848 return res; 849 } 850 static //private 851 void tokenizeLayout(Layout self, int curCble, String layout, List<Layout.Element> col) { 852 boolean prevBCI = false; 853 for (int len = layout.length(), i = 0; i < len; ) { 854 int start = i; 855 int body; 856 Layout.Element e = self.new Element(); 857 byte kind; 858 //System.out.println("at "+i+": ..."+layout.substring(i)); 859 // strip a prefix 860 switch (layout.charAt(i++)) { 861 /// layout_element: integral 862 case 'B': case 'H': case 'I': case 'V': // unsigned_int 863 kind = EK_INT; 864 --i; // reparse 865 i = tokenizeUInt(e, layout, i); 866 break; 867 case 'S': // signed_int 868 kind = EK_INT; 869 --i; // reparse 870 i = tokenizeSInt(e, layout, i); 871 break; 872 case 'P': // bc_index 873 kind = EK_BCI; 874 if (layout.charAt(i++) == 'O') { 875 // bc_index: 'PO' tokenizeUInt 876 e.flags |= EF_DELTA; 877 // must follow P or PO: 878 if (!prevBCI) 879 { i = -i; continue; } // fail 880 i++; // move forward 881 } 882 --i; // reparse 883 i = tokenizeUInt(e, layout, i); 884 break; 885 case 'O': // bc_offset 886 kind = EK_BCO; 887 e.flags |= EF_DELTA; 888 // must follow P or PO: 889 if (!prevBCI) 890 { i = -i; continue; } // fail 891 i = tokenizeSInt(e, layout, i); 892 break; 893 case 'F': // flag 894 kind = EK_FLAG; 895 i = tokenizeUInt(e, layout, i); 896 break; 897 case 'N': // replication: 'N' uint '[' elem ... ']' 898 kind = EK_REPL; 899 i = tokenizeUInt(e, layout, i); 900 if (layout.charAt(i++) != '[') 901 { i = -i; continue; } // fail 902 i = skipBody(layout, body = i); 903 e.body = tokenizeLayout(self, curCble, 904 layout.substring(body, i++)); 905 break; 906 case 'T': // union: 'T' any_int union_case* '(' ')' '[' body ']' 907 kind = EK_UN; 908 i = tokenizeSInt(e, layout, i); 909 List<Layout.Element> cases = new ArrayList<>(); 910 for (;;) { 911 // Keep parsing cases until we hit the default case. 912 if (layout.charAt(i++) != '(') 913 { i = -i; break; } // fail 914 int beg = i; 915 i = layout.indexOf(')', i); 916 String cstr = layout.substring(beg, i++); 917 int cstrlen = cstr.length(); 918 if (layout.charAt(i++) != '[') 919 { i = -i; break; } // fail 920 // Check for duplication. 921 if (layout.charAt(i) == ']') 922 body = i; // missing body, which is legal here 923 else 924 i = skipBody(layout, body = i); 925 Layout.Element[] cbody 926 = tokenizeLayout(self, curCble, 927 layout.substring(body, i++)); 928 if (cstrlen == 0) { 929 Layout.Element ce = self.new Element(); 930 ce.body = cbody; 931 ce.kind = EK_CASE; 932 ce.removeBand(); 933 cases.add(ce); 934 break; // done with the whole union 935 } else { 936 // Parse a case string. 937 boolean firstCaseNum = true; 938 for (int cp = 0, endp;; cp = endp+1) { 939 // Look for multiple case tags: 940 endp = cstr.indexOf(',', cp); 941 if (endp < 0) endp = cstrlen; 942 String cstr1 = cstr.substring(cp, endp); 943 if (cstr1.length() == 0) 944 cstr1 = "empty"; // will fail parse 945 int value0, value1; 946 // Check for a case range (new in 1.6). 947 int dash = findCaseDash(cstr1, 0); 948 if (dash >= 0) { 949 value0 = parseIntBefore(cstr1, dash); 950 value1 = parseIntAfter(cstr1, dash); 951 if (value0 >= value1) 952 { i = -i; break; } // fail 953 } else { 954 value0 = value1 = Integer.parseInt(cstr1); 955 } 956 // Add a case for each value in value0..value1 957 for (;; value0++) { 958 Layout.Element ce = self.new Element(); 959 ce.body = cbody; // all cases share one body 960 ce.kind = EK_CASE; 961 ce.removeBand(); 962 if (!firstCaseNum) 963 // "backward case" repeats a body 964 ce.flags |= EF_BACK; 965 firstCaseNum = false; 966 ce.value = value0; 967 cases.add(ce); 968 if (value0 == value1) break; 969 } 970 if (endp == cstrlen) { 971 break; // done with this case 972 } 973 } 974 } 975 } 976 e.body = new Layout.Element[cases.size()]; 977 cases.toArray(e.body); 978 e.kind = kind; 979 for (int j = 0; j < e.body.length-1; j++) { 980 Layout.Element ce = e.body[j]; 981 if (matchCase(e, ce.value) != ce) { 982 // Duplicate tag. 983 { i = -i; break; } // fail 984 } 985 } 986 break; 987 case '(': // call: '(' '-'? digit+ ')' 988 kind = EK_CALL; 989 e.removeBand(); 990 i = layout.indexOf(')', i); 991 String cstr = layout.substring(start+1, i++); 992 int offset = Integer.parseInt(cstr); 993 int target = curCble + offset; 994 if (!(offset+"").equals(cstr) || 995 self.elems == null || 996 target < 0 || 997 target >= self.elems.length) 998 { i = -i; continue; } // fail 999 Layout.Element ce = self.elems[target]; 1000 assert(ce.kind == EK_CBLE); 1001 e.value = target; 1002 e.body = new Layout.Element[]{ ce }; 1003 // Is it a (recursive) backward call? 1004 if (offset <= 0) { 1005 // Yes. Mark both caller and callee backward. 1006 e.flags |= EF_BACK; 1007 ce.flags |= EF_BACK; 1008 } 1009 break; 1010 case 'K': // reference_type: constant_ref 1011 kind = EK_REF; 1012 switch (layout.charAt(i++)) { 1013 case 'I': e.refKind = CONSTANT_Integer; break; 1014 case 'J': e.refKind = CONSTANT_Long; break; 1015 case 'F': e.refKind = CONSTANT_Float; break; 1016 case 'D': e.refKind = CONSTANT_Double; break; 1017 case 'S': e.refKind = CONSTANT_String; break; 1018 case 'Q': e.refKind = CONSTANT_FieldSpecific; break; 1019 1020 // new in 1.7: 1021 case 'M': e.refKind = CONSTANT_MethodHandle; break; 1022 case 'T': e.refKind = CONSTANT_MethodType; break; 1023 case 'L': e.refKind = CONSTANT_LoadableValue; break; 1024 default: { i = -i; continue; } // fail 1025 } 1026 break; 1027 case 'R': // schema_ref 1028 kind = EK_REF; 1029 switch (layout.charAt(i++)) { 1030 case 'C': e.refKind = CONSTANT_Class; break; 1031 case 'S': e.refKind = CONSTANT_Signature; break; 1032 case 'D': e.refKind = CONSTANT_NameandType; break; 1033 case 'F': e.refKind = CONSTANT_Fieldref; break; 1034 case 'M': e.refKind = CONSTANT_Methodref; break; 1035 case 'I': e.refKind = CONSTANT_InterfaceMethodref; break; 1036 1037 case 'U': e.refKind = CONSTANT_Utf8; break; //utf8_ref 1038 case 'Q': e.refKind = CONSTANT_All; break; //untyped_ref 1039 1040 // new in 1.7: 1041 case 'Y': e.refKind = CONSTANT_InvokeDynamic; break; 1042 case 'B': e.refKind = CONSTANT_BootstrapMethod; break; 1043 case 'N': e.refKind = CONSTANT_AnyMember; break; 1044 1045 default: { i = -i; continue; } // fail 1046 } 1047 break; 1048 default: { i = -i; continue; } // fail 1049 } 1050 1051 // further parsing of refs 1052 if (kind == EK_REF) { 1053 // reference: reference_type -><- ( 'N' )? tokenizeUInt 1054 if (layout.charAt(i++) == 'N') { 1055 e.flags |= EF_NULL; 1056 i++; // move forward 1057 } 1058 --i; // reparse 1059 i = tokenizeUInt(e, layout, i); 1060 self.hasRefs = true; 1061 } 1062 1063 prevBCI = (kind == EK_BCI); 1064 1065 // store the new element 1066 e.kind = kind; 1067 e.layout = layout.substring(start, i); 1068 col.add(e); 1069 } 1070 } 1071 static //private 1072 String[] splitBodies(String layout) { 1073 List<String> bodies = new ArrayList<>(); 1074 // Parse several independent layout bodies: "[foo][bar]...[baz]" 1075 for (int i = 0; i < layout.length(); i++) { 1076 if (layout.charAt(i++) != '[') 1077 layout.charAt(-i); // throw error 1078 int body; 1079 i = skipBody(layout, body = i); 1080 bodies.add(layout.substring(body, i)); 1081 } 1082 String[] res = new String[bodies.size()]; 1083 bodies.toArray(res); 1084 return res; 1085 } 1086 static private 1087 int skipBody(String layout, int i) { 1088 assert(layout.charAt(i-1) == '['); 1089 if (layout.charAt(i) == ']') 1090 // No empty bodies, please. 1091 return -i; 1092 // skip balanced [...[...]...] 1093 for (int depth = 1; depth > 0; ) { 1094 switch (layout.charAt(i++)) { 1095 case '[': depth++; break; 1096 case ']': depth--; break; 1097 } 1098 } 1099 --i; // get before bracket 1100 assert(layout.charAt(i) == ']'); 1101 return i; // return closing bracket 1102 } 1103 static private 1104 int tokenizeUInt(Layout.Element e, String layout, int i) { 1105 switch (layout.charAt(i++)) { 1106 case 'V': e.len = 0; break; 1107 case 'B': e.len = 1; break; 1108 case 'H': e.len = 2; break; 1109 case 'I': e.len = 4; break; 1110 default: return -i; 1111 } 1112 return i; 1113 } 1114 static private 1115 int tokenizeSInt(Layout.Element e, String layout, int i) { 1116 if (layout.charAt(i) == 'S') { 1117 e.flags |= EF_SIGN; 1118 ++i; 1119 } 1120 return tokenizeUInt(e, layout, i); 1121 } 1122 1123 static private 1124 boolean isDigit(char c) { 1125 return c >= '0' && c <= '9'; 1126 } 1127 1128 /** Find an occurrence of hyphen '-' between two numerals. */ 1129 static //private 1130 int findCaseDash(String layout, int fromIndex) { 1131 if (fromIndex <= 0) fromIndex = 1; // minimum dash pos 1132 int lastDash = layout.length() - 2; // maximum dash pos 1133 for (;;) { 1134 int dash = layout.indexOf('-', fromIndex); 1135 if (dash < 0 || dash > lastDash) return -1; 1136 if (isDigit(layout.charAt(dash-1))) { 1137 char afterDash = layout.charAt(dash+1); 1138 if (afterDash == '-' && dash+2 < layout.length()) 1139 afterDash = layout.charAt(dash+2); 1140 if (isDigit(afterDash)) { 1141 // matched /[0-9]--?[0-9]/; return position of dash 1142 return dash; 1143 } 1144 } 1145 fromIndex = dash+1; 1146 } 1147 } 1148 static 1149 int parseIntBefore(String layout, int dash) { 1150 int end = dash; 1151 int beg = end; 1152 while (beg > 0 && isDigit(layout.charAt(beg-1))) { 1153 --beg; 1154 } 1155 if (beg == end) return Integer.parseInt("empty"); 1156 // skip backward over a sign 1157 if (beg >= 1 && layout.charAt(beg-1) == '-') --beg; 1158 assert(beg == 0 || !isDigit(layout.charAt(beg-1))); 1159 return Integer.parseInt(layout.substring(beg, end)); 1160 } 1161 static 1162 int parseIntAfter(String layout, int dash) { 1163 int beg = dash+1; 1164 int end = beg; 1165 int limit = layout.length(); 1166 if (end < limit && layout.charAt(end) == '-') ++end; 1167 while (end < limit && isDigit(layout.charAt(end))) { 1168 ++end; 1169 } 1170 if (beg == end) return Integer.parseInt("empty"); 1171 return Integer.parseInt(layout.substring(beg, end)); 1172 } 1173 /** For compatibility with 1.5 pack, expand 1-5 into 1,2,3,4,5. */ 1174 static 1175 String expandCaseDashNotation(String layout) { 1176 int dash = findCaseDash(layout, 0); 1177 if (dash < 0) return layout; // no dashes (the common case) 1178 StringBuilder result = new StringBuilder(layout.length() * 3); 1179 int sofar = 0; // how far have we processed the layout? 1180 for (;;) { 1181 // for each dash, collect everything up to the dash 1182 result.append(layout.substring(sofar, dash)); 1183 sofar = dash+1; // skip the dash 1184 // then collect intermediate values 1185 int value0 = parseIntBefore(layout, dash); 1186 int value1 = parseIntAfter(layout, dash); 1187 assert(value0 < value1); 1188 result.append(","); // close off value0 numeral 1189 for (int i = value0+1; i < value1; i++) { 1190 result.append(i); 1191 result.append(","); // close off i numeral 1192 } 1193 dash = findCaseDash(layout, sofar); 1194 if (dash < 0) break; 1195 } 1196 result.append(layout.substring(sofar)); // collect the rest 1197 return result.toString(); 1198 } 1199 static { 1200 assert(expandCaseDashNotation("1-5").equals("1,2,3,4,5")); 1201 assert(expandCaseDashNotation("-2--1").equals("-2,-1")); 1202 assert(expandCaseDashNotation("-2-1").equals("-2,-1,0,1")); 1203 assert(expandCaseDashNotation("-1-0").equals("-1,0")); 1204 } 1205 1206 // Parse attribute bytes, putting values into bands. Returns new pos. 1207 // Used when reading a class file (local refs resolved with local cpMap). 1208 // Also used for ad hoc scanning. 1209 static 1210 int parseUsing(Layout.Element[] elems, Holder holder, 1211 byte[] bytes, int pos, int len, ValueStream out) { 1212 int prevBCI = 0; 1213 int prevRBCI = 0; 1214 int end = pos + len; 1215 int[] buf = { 0 }; // for calls to parseInt, holds 2nd result 1216 for (int i = 0; i < elems.length; i++) { 1217 Layout.Element e = elems[i]; 1218 int bandIndex = e.bandIndex; 1219 int value; 1220 int BCI, RBCI; 1221 switch (e.kind) { 1222 case EK_INT: 1223 pos = parseInt(e, bytes, pos, buf); 1224 value = buf[0]; 1225 out.putInt(bandIndex, value); 1226 break; 1227 case EK_BCI: // PH, POH 1228 pos = parseInt(e, bytes, pos, buf); 1229 BCI = buf[0]; 1230 RBCI = out.encodeBCI(BCI); 1231 if (!e.flagTest(EF_DELTA)) { 1232 // PH: transmit R(bci), store bci 1233 value = RBCI; 1234 } else { 1235 // POH: transmit D(R(bci)), store bci 1236 value = RBCI - prevRBCI; 1237 } 1238 prevBCI = BCI; 1239 prevRBCI = RBCI; 1240 out.putInt(bandIndex, value); 1241 break; 1242 case EK_BCO: // OH 1243 assert(e.flagTest(EF_DELTA)); 1244 // OH: transmit D(R(bci)), store D(bci) 1245 pos = parseInt(e, bytes, pos, buf); 1246 BCI = prevBCI + buf[0]; 1247 RBCI = out.encodeBCI(BCI); 1248 value = RBCI - prevRBCI; 1249 prevBCI = BCI; 1250 prevRBCI = RBCI; 1251 out.putInt(bandIndex, value); 1252 break; 1253 case EK_FLAG: 1254 pos = parseInt(e, bytes, pos, buf); 1255 value = buf[0]; 1256 out.putInt(bandIndex, value); 1257 break; 1258 case EK_REPL: 1259 pos = parseInt(e, bytes, pos, buf); 1260 value = buf[0]; 1261 out.putInt(bandIndex, value); 1262 for (int j = 0; j < value; j++) { 1263 pos = parseUsing(e.body, holder, bytes, pos, end-pos, out); 1264 } 1265 break; // already transmitted the scalar value 1266 case EK_UN: 1267 pos = parseInt(e, bytes, pos, buf); 1268 value = buf[0]; 1269 out.putInt(bandIndex, value); 1270 Layout.Element ce = matchCase(e, value); 1271 pos = parseUsing(ce.body, holder, bytes, pos, end-pos, out); 1272 1273 break; // already transmitted the scalar value 1274 case EK_CALL: 1275 // Adjust band offset if it is a backward call. 1276 assert(e.body.length == 1); 1277 assert(e.body[0].kind == EK_CBLE); 1278 if (e.flagTest(EF_BACK)) 1279 out.noteBackCall(e.value); 1280 pos = parseUsing(e.body[0].body, holder, bytes, pos, end-pos, out); 1281 break; // no additional scalar value to transmit 1282 case EK_REF: 1283 pos = parseInt(e, bytes, pos, buf); 1284 int localRef = buf[0]; 1285 Entry globalRef; 1286 if (localRef == 0) { 1287 globalRef = null; // N.B. global null reference is -1 1288 } else { 1289 globalRef = holder.getCPMap()[localRef]; 1290 if (e.refKind == CONSTANT_Signature 1291 && globalRef.getTag() == CONSTANT_Utf8) { 1292 // Cf. ClassReader.readSignatureRef. 1293 String typeName = globalRef.stringValue(); 1294 globalRef = ConstantPool.getSignatureEntry(typeName); 1295 } else if (e.refKind == CONSTANT_FieldSpecific) { 1296 assert(globalRef.getTag() >= CONSTANT_Integer); 1297 assert(globalRef.getTag() <= CONSTANT_String || 1298 globalRef.getTag() >= CONSTANT_MethodHandle); 1299 assert(globalRef.getTag() <= CONSTANT_MethodType); 1300 } else if (e.refKind < CONSTANT_GroupFirst) { 1301 assert(e.refKind == globalRef.getTag()); 1302 } 1303 } 1304 out.putRef(bandIndex, globalRef); 1305 break; 1306 default: assert(false); continue; 1307 } 1308 } 1309 return pos; 1310 } 1311 1312 static 1313 Layout.Element matchCase(Layout.Element e, int value) { 1314 assert(e.kind == EK_UN); 1315 int lastj = e.body.length-1; 1316 for (int j = 0; j < lastj; j++) { 1317 Layout.Element ce = e.body[j]; 1318 assert(ce.kind == EK_CASE); 1319 if (value == ce.value) 1320 return ce; 1321 } 1322 return e.body[lastj]; 1323 } 1324 1325 static private 1326 int parseInt(Layout.Element e, byte[] bytes, int pos, int[] buf) { 1327 int value = 0; 1328 int loBits = e.len * 8; 1329 // Read in big-endian order: 1330 for (int bitPos = loBits; (bitPos -= 8) >= 0; ) { 1331 value += (bytes[pos++] & 0xFF) << bitPos; 1332 } 1333 if (loBits < 32 && e.flagTest(EF_SIGN)) { 1334 // sign-extend subword value 1335 int hiBits = 32 - loBits; 1336 value = (value << hiBits) >> hiBits; 1337 } 1338 buf[0] = value; 1339 return pos; 1340 } 1341 1342 // Format attribute bytes, drawing values from bands. 1343 // Used when emptying attribute bands into a package model. 1344 // (At that point CP refs. are not yet assigned indexes.) 1345 static 1346 void unparseUsing(Layout.Element[] elems, Object[] fixups, 1347 ValueStream in, ByteArrayOutputStream out) { 1348 int prevBCI = 0; 1349 int prevRBCI = 0; 1350 for (int i = 0; i < elems.length; i++) { 1351 Layout.Element e = elems[i]; 1352 int bandIndex = e.bandIndex; 1353 int value; 1354 int BCI, RBCI; // "RBCI" is R(BCI), BCI's coded representation 1355 switch (e.kind) { 1356 case EK_INT: 1357 value = in.getInt(bandIndex); 1358 unparseInt(e, value, out); 1359 break; 1360 case EK_BCI: // PH, POH 1361 value = in.getInt(bandIndex); 1362 if (!e.flagTest(EF_DELTA)) { 1363 // PH: transmit R(bci), store bci 1364 RBCI = value; 1365 } else { 1366 // POH: transmit D(R(bci)), store bci 1367 RBCI = prevRBCI + value; 1368 } 1369 assert(prevBCI == in.decodeBCI(prevRBCI)); 1370 BCI = in.decodeBCI(RBCI); 1371 unparseInt(e, BCI, out); 1372 prevBCI = BCI; 1373 prevRBCI = RBCI; 1374 break; 1375 case EK_BCO: // OH 1376 value = in.getInt(bandIndex); 1377 assert(e.flagTest(EF_DELTA)); 1378 // OH: transmit D(R(bci)), store D(bci) 1379 assert(prevBCI == in.decodeBCI(prevRBCI)); 1380 RBCI = prevRBCI + value; 1381 BCI = in.decodeBCI(RBCI); 1382 unparseInt(e, BCI - prevBCI, out); 1383 prevBCI = BCI; 1384 prevRBCI = RBCI; 1385 break; 1386 case EK_FLAG: 1387 value = in.getInt(bandIndex); 1388 unparseInt(e, value, out); 1389 break; 1390 case EK_REPL: 1391 value = in.getInt(bandIndex); 1392 unparseInt(e, value, out); 1393 for (int j = 0; j < value; j++) { 1394 unparseUsing(e.body, fixups, in, out); 1395 } 1396 break; 1397 case EK_UN: 1398 value = in.getInt(bandIndex); 1399 unparseInt(e, value, out); 1400 Layout.Element ce = matchCase(e, value); 1401 unparseUsing(ce.body, fixups, in, out); 1402 break; 1403 case EK_CALL: 1404 assert(e.body.length == 1); 1405 assert(e.body[0].kind == EK_CBLE); 1406 unparseUsing(e.body[0].body, fixups, in, out); 1407 break; 1408 case EK_REF: 1409 Entry globalRef = in.getRef(bandIndex); 1410 int localRef; 1411 if (globalRef != null) { 1412 // It's a one-element array, really an lvalue. 1413 fixups[0] = Fixups.add(fixups[0], null, out.size(), 1414 Fixups.U2_FORMAT, globalRef); 1415 localRef = 0; // placeholder for fixups 1416 } else { 1417 localRef = 0; // fixed null value 1418 } 1419 unparseInt(e, localRef, out); 1420 break; 1421 default: assert(false); continue; 1422 } 1423 } 1424 } 1425 1426 static private 1427 void unparseInt(Layout.Element e, int value, ByteArrayOutputStream out) { 1428 int loBits = e.len * 8; 1429 if (loBits == 0) { 1430 // It is not stored at all ('V' layout). 1431 return; 1432 } 1433 if (loBits < 32) { 1434 int hiBits = 32 - loBits; 1435 int codedValue; 1436 if (e.flagTest(EF_SIGN)) 1437 codedValue = (value << hiBits) >> hiBits; 1438 else 1439 codedValue = (value << hiBits) >>> hiBits; 1440 if (codedValue != value) 1441 throw new InternalError("cannot code in "+e.len+" bytes: "+value); 1442 } 1443 // Write in big-endian order: 1444 for (int bitPos = loBits; (bitPos -= 8) >= 0; ) { 1445 out.write((byte)(value >>> bitPos)); 1446 } 1447 } 1448 1449 /* 1450 /// Testing. 1451 public static void main(String av[]) { 1452 int maxVal = 12; 1453 int iters = 0; 1454 boolean verbose; 1455 int ap = 0; 1456 while (ap < av.length) { 1457 if (!av[ap].startsWith("-")) break; 1458 if (av[ap].startsWith("-m")) 1459 maxVal = Integer.parseInt(av[ap].substring(2)); 1460 else if (av[ap].startsWith("-i")) 1461 iters = Integer.parseInt(av[ap].substring(2)); 1462 else 1463 throw new RuntimeException("Bad option: "+av[ap]); 1464 ap++; 1465 } 1466 verbose = (iters == 0); 1467 if (iters <= 0) iters = 1; 1468 if (ap == av.length) { 1469 av = new String[] { 1470 "HH", // ClassFile.version 1471 "RUH", // SourceFile 1472 "RCHRDNH", // EnclosingMethod 1473 "KQH", // ConstantValue 1474 "NH[RCH]", // Exceptions 1475 "NH[PHH]", // LineNumberTable 1476 "NH[PHOHRUHRSHH]", // LocalVariableTable 1477 "NH[PHPOHIIH]", // CharacterRangeTable 1478 "NH[PHHII]", // CoverageTable 1479 "NH[RCHRCNHRUNHFH]", // InnerClasses 1480 "NH[RMHNH[KLH]]", // BootstrapMethods 1481 "HHNI[B]NH[PHPOHPOHRCNH]NH[RUHNI[B]]", // Code 1482 "=AnnotationDefault", 1483 // Like metadata, but with a compact tag set: 1484 "[NH[(1)]]" 1485 +"[NH[(1)]]" 1486 +"[RSHNH[RUH(1)]]" 1487 +"[TB(0,1,3)[KIH](2)[KDH](5)[KFH](4)[KJH](7)[RSH](8)[RSHRUH](9)[RUH](10)[(-1)](6)[NH[(0)]]()[]]", 1488 "" 1489 }; 1490 ap = 0; 1491 } 1492 Utils.currentInstance.set(new PackerImpl()); 1493 final int[][] counts = new int[2][3]; // int bci ref 1494 final Entry[] cpMap = new Entry[maxVal+1]; 1495 for (int i = 0; i < cpMap.length; i++) { 1496 if (i == 0) continue; // 0 => null 1497 cpMap[i] = ConstantPool.getLiteralEntry(new Integer(i)); 1498 } 1499 Package.Class cls = new Package().new Class(""); 1500 cls.cpMap = cpMap; 1501 class TestValueStream extends ValueStream { 1502 java.util.Random rand = new java.util.Random(0); 1503 ArrayList history = new ArrayList(); 1504 int ckidx = 0; 1505 int maxVal; 1506 boolean verbose; 1507 void reset() { history.clear(); ckidx = 0; } 1508 public int getInt(int bandIndex) { 1509 counts[0][0]++; 1510 int value = rand.nextInt(maxVal+1); 1511 history.add(new Integer(bandIndex)); 1512 history.add(new Integer(value)); 1513 return value; 1514 } 1515 public void putInt(int bandIndex, int token) { 1516 counts[1][0]++; 1517 if (verbose) 1518 System.out.print(" "+bandIndex+":"+token); 1519 // Make sure this put parallels a previous get: 1520 int check0 = ((Integer)history.get(ckidx+0)).intValue(); 1521 int check1 = ((Integer)history.get(ckidx+1)).intValue(); 1522 if (check0 != bandIndex || check1 != token) { 1523 if (!verbose) 1524 System.out.println(history.subList(0, ckidx)); 1525 System.out.println(" *** Should be "+check0+":"+check1); 1526 throw new RuntimeException("Failed test!"); 1527 } 1528 ckidx += 2; 1529 } 1530 public Entry getRef(int bandIndex) { 1531 counts[0][2]++; 1532 int value = getInt(bandIndex); 1533 if (value < 0 || value > maxVal) { 1534 System.out.println(" *** Unexpected ref code "+value); 1535 return ConstantPool.getLiteralEntry(new Integer(value)); 1536 } 1537 return cpMap[value]; 1538 } 1539 public void putRef(int bandIndex, Entry ref) { 1540 counts[1][2]++; 1541 if (ref == null) { 1542 putInt(bandIndex, 0); 1543 return; 1544 } 1545 Number refValue = null; 1546 if (ref instanceof ConstantPool.NumberEntry) 1547 refValue = ((ConstantPool.NumberEntry)ref).numberValue(); 1548 int value; 1549 if (!(refValue instanceof Integer)) { 1550 System.out.println(" *** Unexpected ref "+ref); 1551 value = -1; 1552 } else { 1553 value = ((Integer)refValue).intValue(); 1554 } 1555 putInt(bandIndex, value); 1556 } 1557 public int encodeBCI(int bci) { 1558 counts[1][1]++; 1559 // move LSB to MSB of low byte 1560 int code = (bci >> 8) << 8; // keep high bits 1561 code += (bci & 0xFE) >> 1; 1562 code += (bci & 0x01) << 7; 1563 return code ^ (8<<8); // mark it clearly as coded 1564 } 1565 public int decodeBCI(int bciCode) { 1566 counts[0][1]++; 1567 bciCode ^= (8<<8); // remove extra mark 1568 int bci = (bciCode >> 8) << 8; // keep high bits 1569 bci += (bciCode & 0x7F) << 1; 1570 bci += (bciCode & 0x80) >> 7; 1571 return bci; 1572 } 1573 } 1574 TestValueStream tts = new TestValueStream(); 1575 tts.maxVal = maxVal; 1576 tts.verbose = verbose; 1577 ByteArrayOutputStream buf = new ByteArrayOutputStream(); 1578 for (int i = 0; i < (1 << 30); i = (i + 1) * 5) { 1579 int ei = tts.encodeBCI(i); 1580 int di = tts.decodeBCI(ei); 1581 if (di != i) System.out.println("i="+Integer.toHexString(i)+ 1582 " ei="+Integer.toHexString(ei)+ 1583 " di="+Integer.toHexString(di)); 1584 } 1585 while (iters-- > 0) { 1586 for (int i = ap; i < av.length; i++) { 1587 String layout = av[i]; 1588 if (layout.startsWith("=")) { 1589 String name = layout.substring(1); 1590 for (Attribute a : standardDefs.values()) { 1591 if (a.name().equals(name)) { 1592 layout = a.layout().layout(); 1593 break; 1594 } 1595 } 1596 if (layout.startsWith("=")) { 1597 System.out.println("Could not find "+name+" in "+standardDefs.values()); 1598 } 1599 } 1600 Layout self = new Layout(0, "Foo", layout); 1601 if (verbose) { 1602 System.out.print("/"+layout+"/ => "); 1603 System.out.println(Arrays.asList(self.elems)); 1604 } 1605 buf.reset(); 1606 tts.reset(); 1607 Object fixups = self.unparse(tts, buf); 1608 byte[] bytes = buf.toByteArray(); 1609 // Attach the references to the byte array. 1610 Fixups.setBytes(fixups, bytes); 1611 // Patch the references to their frozen values. 1612 Fixups.finishRefs(fixups, bytes, new Index("test", cpMap)); 1613 if (verbose) { 1614 System.out.print(" bytes: {"); 1615 for (int j = 0; j < bytes.length; j++) { 1616 System.out.print(" "+bytes[j]); 1617 } 1618 System.out.println("}"); 1619 } 1620 if (verbose) { 1621 System.out.print(" parse: {"); 1622 } 1623 self.parse(cls, bytes, 0, bytes.length, tts); 1624 if (verbose) { 1625 System.out.println("}"); 1626 } 1627 } 1628 } 1629 for (int j = 0; j <= 1; j++) { 1630 System.out.print("values "+(j==0?"read":"written")+": {"); 1631 for (int k = 0; k < counts[j].length; k++) { 1632 System.out.print(" "+counts[j][k]); 1633 } 1634 System.out.println(" }"); 1635 } 1636 } 1637 //*/ 1638 }