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