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 name()55 public String name() { return def.name(); } layout()56 public Layout layout() { return def; } bytes()57 public byte[] bytes() { return bytes; } size()58 public int size() { return bytes.length; } getNameRef()59 public Entry getNameRef() { return def.getNameRef(); } 60 Attribute(Attribute old)61 private Attribute(Attribute old) { 62 this.def = old.def; 63 this.bytes = old.bytes; 64 this.fixups = old.fixups; 65 } 66 Attribute(Layout def, byte[] bytes, Object fixups)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 } Attribute(Layout def, byte[] bytes)73 public Attribute(Layout def, byte[] bytes) { 74 this(def, bytes, null); 75 } 76 addContent(byte[] bytes, Object fixups)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 } addContent(byte[] bytes)87 public Attribute addContent(byte[] bytes) { 88 return addContent(bytes, null); 89 } 90 finishRefs(Index ix)91 public void finishRefs(Index ix) { 92 if (fixups != null) { 93 Fixups.finishRefs(fixups, bytes, ix); 94 fixups = null; 95 } 96 } 97 isCanonical()98 public boolean isCanonical() { 99 return this == def.canon; 100 } 101 102 @Override compareTo(Attribute that)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.) getCanonList(List<Attribute> al)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. find(int ctype, String name, String 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 keyForLookup(int ctype, String name)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. lookup(Map<Layout, Attribute> defs, int ctype, String name)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 define(Map<Layout, Attribute> defs, int ctype, String name, String layout)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; define(sd, ATTR_CONTEXT_CLASS, R, R)164 define(sd, ATTR_CONTEXT_CLASS, "Signature", "RSH"); define(sd, ATTR_CONTEXT_CLASS, R, R)165 define(sd, ATTR_CONTEXT_CLASS, "Synthetic", ""); define(sd, ATTR_CONTEXT_CLASS, R, R)166 define(sd, ATTR_CONTEXT_CLASS, "Deprecated", ""); define(sd, ATTR_CONTEXT_CLASS, R, R)167 define(sd, ATTR_CONTEXT_CLASS, "SourceFile", "RUH"); define(sd, ATTR_CONTEXT_CLASS, R, R)168 define(sd, ATTR_CONTEXT_CLASS, "EnclosingMethod", "RCHRDNH"); define(sd, ATTR_CONTEXT_CLASS, R, R)169 define(sd, ATTR_CONTEXT_CLASS, "InnerClasses", "NH[RCHRCNHRUNHFH]"); define(sd, ATTR_CONTEXT_CLASS, R, R)170 define(sd, ATTR_CONTEXT_CLASS, "BootstrapMethods", "NH[RMHNH[KLH]]"); 171 define(sd, ATTR_CONTEXT_FIELD, R, R)172 define(sd, ATTR_CONTEXT_FIELD, "Signature", "RSH"); define(sd, ATTR_CONTEXT_FIELD, R, R)173 define(sd, ATTR_CONTEXT_FIELD, "Synthetic", ""); define(sd, ATTR_CONTEXT_FIELD, R, R)174 define(sd, ATTR_CONTEXT_FIELD, "Deprecated", ""); define(sd, ATTR_CONTEXT_FIELD, R, R)175 define(sd, ATTR_CONTEXT_FIELD, "ConstantValue", "KQH"); 176 define(sd, ATTR_CONTEXT_METHOD, R, R)177 define(sd, ATTR_CONTEXT_METHOD, "Signature", "RSH"); define(sd, ATTR_CONTEXT_METHOD, R, R)178 define(sd, ATTR_CONTEXT_METHOD, "Synthetic", ""); define(sd, ATTR_CONTEXT_METHOD, R, R)179 define(sd, ATTR_CONTEXT_METHOD, "Deprecated", ""); define(sd, ATTR_CONTEXT_METHOD, R, R)180 define(sd, ATTR_CONTEXT_METHOD, "Exceptions", "NH[RCH]"); define(sd, ATTR_CONTEXT_METHOD, R, R)181 define(sd, ATTR_CONTEXT_METHOD, "MethodParameters", "NB[RUNHFH]"); 182 //define(sd, ATTR_CONTEXT_METHOD, "Code", "HHNI[B]NH[PHPOHPOHRCNH]NH[RUHNI[B]]"); 183 define(sd, ATTR_CONTEXT_CODE, R, (R + R + R + R + R + R + R + R + R + R + R + R + R))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 define(sd, ATTR_CONTEXT_CODE, R, R)199 define(sd, ATTR_CONTEXT_CODE, "LineNumberTable", "NH[PHH]"); define(sd, ATTR_CONTEXT_CODE, R, R)200 define(sd, ATTR_CONTEXT_CODE, "LocalVariableTable", "NH[PHOHRUHRSHH]"); define(sd, ATTR_CONTEXT_CODE, R, R)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) { define(sd, ctype, R, annotationsLayout)309 define(sd, ctype, 310 "RuntimeVisibleAnnotations", annotationsLayout); define(sd, ctype, R, annotationsLayout)311 define(sd, ctype, 312 "RuntimeInvisibleAnnotations", annotationsLayout); 313 314 if (ctype == ATTR_CONTEXT_METHOD) { define(sd, ctype, R, paramsLayout)315 define(sd, ctype, 316 "RuntimeVisibleParameterAnnotations", paramsLayout); define(sd, ctype, R, paramsLayout)317 define(sd, ctype, 318 "RuntimeInvisibleParameterAnnotations", paramsLayout); define(sd, ctype, R, defaultLayout)319 define(sd, ctype, 320 "AnnotationDefault", defaultLayout); 321 } 322 } define(sd, ctype, R, typesLayout)323 define(sd, ctype, 324 "RuntimeVisibleTypeAnnotations", typesLayout); define(sd, ctype, R, typesLayout)325 define(sd, ctype, 326 "RuntimeInvisibleTypeAnnotations", typesLayout); 327 } 328 } 329 contextName(int ctype)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. getCPMap()349 protected abstract Entry[] getCPMap(); 350 351 protected int flags; // defined here for convenience 352 protected List<Attribute> attributes; 353 attributeSize()354 public int attributeSize() { 355 return (attributes == null) ? 0 : attributes.size(); 356 } 357 trimToSize()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 addAttribute(Attribute a)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 removeAttribute(Attribute a)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 getAttribute(int n)405 public Attribute getAttribute(int n) { 406 return attributes.get(n); 407 } 408 visitRefs(int mode, Collection<Entry> refs)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 getAttributes()418 public List<Attribute> getAttributes() { 419 if (attributes == null) 420 return noAttributes; 421 return attributes; 422 } 423 setAttributes(List<Attribute> attrList)424 public void setAttributes(List<Attribute> attrList) { 425 if (attrList.isEmpty()) 426 attributes = null; 427 else 428 attributes = attrList; 429 } 430 getAttribute(String attrName)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 getAttribute(Layout attrDef)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 removeAttribute(String attrName)449 public Attribute removeAttribute(String attrName) { 450 return removeAttribute(getAttribute(attrName)); 451 } 452 removeAttribute(Layout attrDef)453 public Attribute removeAttribute(Layout attrDef) { 454 return removeAttribute(getAttribute(attrDef)); 455 } 456 strip(String attrName)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 { getInt(int bandIndex)466 public int getInt(int bandIndex) { throw undef(); } putInt(int bandIndex, int value)467 public void putInt(int bandIndex, int value) { throw undef(); } getRef(int bandIndex)468 public Entry getRef(int bandIndex) { throw undef(); } putRef(int bandIndex, Entry ref)469 public void putRef(int bandIndex, Entry ref) { throw undef(); } 470 // Note: decodeBCI goes w/ getInt/Ref; encodeBCI goes w/ putInt/Ref decodeBCI(int bciCode)471 public int decodeBCI(int bciCode) { throw undef(); } encodeBCI(int bci)472 public int encodeBCI(int bci) { throw undef(); } noteBackCall(int whichCallable)473 public void noteBackCall(int whichCallable) { /* ignore by default */ } undef()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 ctype()509 public int ctype() { return ctype; } name()510 public String name() { return name; } layout()511 public String layout() { return layout; } canonicalInstance()512 public Attribute canonicalInstance() { return canon; } 513 getNameRef()514 public Entry getNameRef() { 515 return ConstantPool.getUtf8Entry(name()); 516 } 517 isEmpty()518 public boolean isEmpty() { 519 return layout.isEmpty(); 520 } 521 Layout(int ctype, String name, String layout)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 private static final long serialVersionUID = -2542243830788066513L; 720 721 private int ctype; 722 private String name; 723 String layout; 724 public FormatException(String message, 725 int ctype, String name, String layout) { 726 super(ATTR_CONTEXT_NAME[ctype]+ " attribute \"" + name + "\"" + 727 (message == null? "" : (": " + message))); 728 this.ctype = ctype; 729 this.name = name; 730 this.layout = layout; 731 } 732 public FormatException(String message, 733 int ctype, String name) { 734 this(message, ctype, name, null); 735 } 736 } 737 738 void visitRefs(Holder holder, int mode, final Collection<Entry> refs) { 739 if (mode == VRM_CLASSIC) { 740 refs.add(getNameRef()); 741 } 742 // else the name is owned by the layout, and is processed elsewhere 743 if (bytes.length == 0) return; // quick exit 744 if (!def.hasRefs) return; // quick exit 745 if (fixups != null) { 746 Fixups.visitRefs(fixups, refs); 747 return; 748 } 749 // References (to a local cpMap) are embedded in the bytes. 750 def.parse(holder, bytes, 0, bytes.length, 751 new ValueStream() { 752 @Override 753 public void putInt(int bandIndex, int value) { 754 } 755 @Override 756 public void putRef(int bandIndex, Entry ref) { 757 refs.add(ref); 758 } 759 @Override 760 public int encodeBCI(int bci) { 761 return bci; 762 } 763 }); 764 } 765 766 public void parse(Holder holder, byte[] bytes, int pos, int len, ValueStream out) { 767 def.parse(holder, bytes, pos, len, out); 768 } 769 public Object unparse(ValueStream in, ByteArrayOutputStream out) { 770 return def.unparse(in, out); 771 } 772 773 @Override 774 public String toString() { 775 return def 776 +"{"+(bytes == null ? -1 : size())+"}" 777 +(fixups == null? "": fixups.toString()); 778 } 779 780 /** Remove any informal "pretty printing" from the layout string. 781 * Removes blanks and control chars. 782 * Removes '#' comments (to end of line). 783 * Replaces '\c' by the decimal code of the character c. 784 * Replaces '0xNNN' by the decimal code of the hex number NNN. 785 */ 786 public static 787 String normalizeLayoutString(String layout) { 788 StringBuilder buf = new StringBuilder(); 789 for (int i = 0, len = layout.length(); i < len; ) { 790 char ch = layout.charAt(i++); 791 if (ch <= ' ') { 792 // Skip whitespace and control chars 793 continue; 794 } else if (ch == '#') { 795 // Skip to end of line. 796 int end1 = layout.indexOf('\n', i); 797 int end2 = layout.indexOf('\r', i); 798 if (end1 < 0) end1 = len; 799 if (end2 < 0) end2 = len; 800 i = Math.min(end1, end2); 801 } else if (ch == '\\') { 802 // Map a character reference to its decimal code. 803 buf.append((int) layout.charAt(i++)); 804 } else if (ch == '0' && layout.startsWith("0x", i-1)) { 805 // Map a hex numeral to its decimal code. 806 int start = i-1; 807 int end = start+2; 808 while (end < len) { 809 int dig = layout.charAt(end); 810 if ((dig >= '0' && dig <= '9') || 811 (dig >= 'a' && dig <= 'f')) 812 ++end; 813 else 814 break; 815 } 816 if (end > start) { 817 String num = layout.substring(start, end); 818 buf.append(Integer.decode(num)); 819 i = end; 820 } else { 821 buf.append(ch); 822 } 823 } else { 824 buf.append(ch); 825 } 826 } 827 String result = buf.toString(); 828 if (false && !result.equals(layout)) { 829 Utils.log.info("Normalizing layout string"); 830 Utils.log.info(" From: "+layout); 831 Utils.log.info(" To: "+result); 832 } 833 return result; 834 } 835 836 /// Subroutines for parsing and unparsing: 837 838 /** Parse the attribute layout language. 839 <pre> 840 attribute_layout: 841 ( layout_element )* | ( callable )+ 842 layout_element: 843 ( integral | replication | union | call | reference ) 844 845 callable: 846 '[' body ']' 847 body: 848 ( layout_element )+ 849 850 integral: 851 ( unsigned_int | signed_int | bc_index | bc_offset | flag ) 852 unsigned_int: 853 uint_type 854 signed_int: 855 'S' uint_type 856 any_int: 857 ( unsigned_int | signed_int ) 858 bc_index: 859 ( 'P' uint_type | 'PO' uint_type ) 860 bc_offset: 861 'O' any_int 862 flag: 863 'F' uint_type 864 uint_type: 865 ( 'B' | 'H' | 'I' | 'V' ) 866 867 replication: 868 'N' uint_type '[' body ']' 869 870 union: 871 'T' any_int (union_case)* '(' ')' '[' (body)? ']' 872 union_case: 873 '(' union_case_tag (',' union_case_tag)* ')' '[' (body)? ']' 874 union_case_tag: 875 ( numeral | numeral '-' numeral ) 876 call: 877 '(' numeral ')' 878 879 reference: 880 reference_type ( 'N' )? uint_type 881 reference_type: 882 ( constant_ref | schema_ref | utf8_ref | untyped_ref ) 883 constant_ref: 884 ( 'KI' | 'KJ' | 'KF' | 'KD' | 'KS' | 'KQ' | 'KM' | 'KT' | 'KL' ) 885 schema_ref: 886 ( 'RC' | 'RS' | 'RD' | 'RF' | 'RM' | 'RI' | 'RY' | 'RB' | 'RN' ) 887 utf8_ref: 888 'RU' 889 untyped_ref: 890 'RQ' 891 892 numeral: 893 '(' ('-')? (digit)+ ')' 894 digit: 895 ( '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' ) 896 </pre> 897 */ 898 static //private 899 Layout.Element[] tokenizeLayout(Layout self, int curCble, String layout) { 900 List<Layout.Element> col = new ArrayList<>(layout.length()); 901 tokenizeLayout(self, curCble, layout, col); 902 Layout.Element[] res = new Layout.Element[col.size()]; 903 col.toArray(res); 904 return res; 905 } 906 static //private 907 void tokenizeLayout(Layout self, int curCble, String layout, List<Layout.Element> col) { 908 boolean prevBCI = false; 909 for (int len = layout.length(), i = 0; i < len; ) { 910 int start = i; 911 int body; 912 Layout.Element e = self.new Element(); 913 byte kind; 914 //System.out.println("at "+i+": ..."+layout.substring(i)); 915 // strip a prefix 916 switch (layout.charAt(i++)) { 917 /// layout_element: integral 918 case 'B': case 'H': case 'I': case 'V': // unsigned_int 919 kind = EK_INT; 920 --i; // reparse 921 i = tokenizeUInt(e, layout, i); 922 break; 923 case 'S': // signed_int 924 kind = EK_INT; 925 --i; // reparse 926 i = tokenizeSInt(e, layout, i); 927 break; 928 case 'P': // bc_index 929 kind = EK_BCI; 930 if (layout.charAt(i++) == 'O') { 931 // bc_index: 'PO' tokenizeUInt 932 e.flags |= EF_DELTA; 933 // must follow P or PO: 934 if (!prevBCI) 935 { i = -i; continue; } // fail 936 i++; // move forward 937 } 938 --i; // reparse 939 i = tokenizeUInt(e, layout, i); 940 break; 941 case 'O': // bc_offset 942 kind = EK_BCO; 943 e.flags |= EF_DELTA; 944 // must follow P or PO: 945 if (!prevBCI) 946 { i = -i; continue; } // fail 947 i = tokenizeSInt(e, layout, i); 948 break; 949 case 'F': // flag 950 kind = EK_FLAG; 951 i = tokenizeUInt(e, layout, i); 952 break; 953 case 'N': // replication: 'N' uint '[' elem ... ']' 954 kind = EK_REPL; 955 i = tokenizeUInt(e, layout, i); 956 if (layout.charAt(i++) != '[') 957 { i = -i; continue; } // fail 958 i = skipBody(layout, body = i); 959 e.body = tokenizeLayout(self, curCble, 960 layout.substring(body, i++)); 961 break; 962 case 'T': // union: 'T' any_int union_case* '(' ')' '[' body ']' 963 kind = EK_UN; 964 i = tokenizeSInt(e, layout, i); 965 List<Layout.Element> cases = new ArrayList<>(); 966 for (;;) { 967 // Keep parsing cases until we hit the default case. 968 if (layout.charAt(i++) != '(') 969 { i = -i; break; } // fail 970 int beg = i; 971 i = layout.indexOf(')', i); 972 String cstr = layout.substring(beg, i++); 973 int cstrlen = cstr.length(); 974 if (layout.charAt(i++) != '[') 975 { i = -i; break; } // fail 976 // Check for duplication. 977 if (layout.charAt(i) == ']') 978 body = i; // missing body, which is legal here 979 else 980 i = skipBody(layout, body = i); 981 Layout.Element[] cbody 982 = tokenizeLayout(self, curCble, 983 layout.substring(body, i++)); 984 if (cstrlen == 0) { 985 Layout.Element ce = self.new Element(); 986 ce.body = cbody; 987 ce.kind = EK_CASE; 988 ce.removeBand(); 989 cases.add(ce); 990 break; // done with the whole union 991 } else { 992 // Parse a case string. 993 boolean firstCaseNum = true; 994 for (int cp = 0, endp;; cp = endp+1) { 995 // Look for multiple case tags: 996 endp = cstr.indexOf(',', cp); 997 if (endp < 0) endp = cstrlen; 998 String cstr1 = cstr.substring(cp, endp); 999 if (cstr1.isEmpty()) 1000 cstr1 = "empty"; // will fail parse 1001 int value0, value1; 1002 // Check for a case range (new in 1.6). 1003 int dash = findCaseDash(cstr1, 0); 1004 if (dash >= 0) { 1005 value0 = parseIntBefore(cstr1, dash); 1006 value1 = parseIntAfter(cstr1, dash); 1007 if (value0 >= value1) 1008 { i = -i; break; } // fail 1009 } else { 1010 value0 = value1 = Integer.parseInt(cstr1); 1011 } 1012 // Add a case for each value in value0..value1 1013 for (;; value0++) { 1014 Layout.Element ce = self.new Element(); 1015 ce.body = cbody; // all cases share one body 1016 ce.kind = EK_CASE; 1017 ce.removeBand(); 1018 if (!firstCaseNum) 1019 // "backward case" repeats a body 1020 ce.flags |= EF_BACK; 1021 firstCaseNum = false; 1022 ce.value = value0; 1023 cases.add(ce); 1024 if (value0 == value1) break; 1025 } 1026 if (endp == cstrlen) { 1027 break; // done with this case 1028 } 1029 } 1030 } 1031 } 1032 e.body = new Layout.Element[cases.size()]; 1033 cases.toArray(e.body); 1034 e.kind = kind; 1035 for (int j = 0; j < e.body.length-1; j++) { 1036 Layout.Element ce = e.body[j]; 1037 if (matchCase(e, ce.value) != ce) { 1038 // Duplicate tag. 1039 { i = -i; break; } // fail 1040 } 1041 } 1042 break; 1043 case '(': // call: '(' '-'? digit+ ')' 1044 kind = EK_CALL; 1045 e.removeBand(); 1046 i = layout.indexOf(')', i); 1047 String cstr = layout.substring(start+1, i++); 1048 int offset = Integer.parseInt(cstr); 1049 int target = curCble + offset; 1050 if (!(offset+"").equals(cstr) || 1051 self.elems == null || 1052 target < 0 || 1053 target >= self.elems.length) 1054 { i = -i; continue; } // fail 1055 Layout.Element ce = self.elems[target]; 1056 assert(ce.kind == EK_CBLE); 1057 e.value = target; 1058 e.body = new Layout.Element[]{ ce }; 1059 // Is it a (recursive) backward call? 1060 if (offset <= 0) { 1061 // Yes. Mark both caller and callee backward. 1062 e.flags |= EF_BACK; 1063 ce.flags |= EF_BACK; 1064 } 1065 break; 1066 case 'K': // reference_type: constant_ref 1067 kind = EK_REF; 1068 switch (layout.charAt(i++)) { 1069 case 'I': e.refKind = CONSTANT_Integer; break; 1070 case 'J': e.refKind = CONSTANT_Long; break; 1071 case 'F': e.refKind = CONSTANT_Float; break; 1072 case 'D': e.refKind = CONSTANT_Double; break; 1073 case 'S': e.refKind = CONSTANT_String; break; 1074 case 'Q': e.refKind = CONSTANT_FieldSpecific; break; 1075 1076 // new in 1.7: 1077 case 'M': e.refKind = CONSTANT_MethodHandle; break; 1078 case 'T': e.refKind = CONSTANT_MethodType; break; 1079 case 'L': e.refKind = CONSTANT_LoadableValue; break; 1080 default: { i = -i; continue; } // fail 1081 } 1082 break; 1083 case 'R': // schema_ref 1084 kind = EK_REF; 1085 switch (layout.charAt(i++)) { 1086 case 'C': e.refKind = CONSTANT_Class; break; 1087 case 'S': e.refKind = CONSTANT_Signature; break; 1088 case 'D': e.refKind = CONSTANT_NameandType; break; 1089 case 'F': e.refKind = CONSTANT_Fieldref; break; 1090 case 'M': e.refKind = CONSTANT_Methodref; break; 1091 case 'I': e.refKind = CONSTANT_InterfaceMethodref; break; 1092 1093 case 'U': e.refKind = CONSTANT_Utf8; break; //utf8_ref 1094 case 'Q': e.refKind = CONSTANT_All; break; //untyped_ref 1095 1096 // new in 1.7: 1097 case 'Y': e.refKind = CONSTANT_InvokeDynamic; break; 1098 case 'B': e.refKind = CONSTANT_BootstrapMethod; break; 1099 case 'N': e.refKind = CONSTANT_AnyMember; break; 1100 1101 default: { i = -i; continue; } // fail 1102 } 1103 break; 1104 default: { i = -i; continue; } // fail 1105 } 1106 1107 // further parsing of refs 1108 if (kind == EK_REF) { 1109 // reference: reference_type -><- ( 'N' )? tokenizeUInt 1110 if (layout.charAt(i++) == 'N') { 1111 e.flags |= EF_NULL; 1112 i++; // move forward 1113 } 1114 --i; // reparse 1115 i = tokenizeUInt(e, layout, i); 1116 self.hasRefs = true; 1117 } 1118 1119 prevBCI = (kind == EK_BCI); 1120 1121 // store the new element 1122 e.kind = kind; 1123 e.layout = layout.substring(start, i); 1124 col.add(e); 1125 } 1126 } 1127 static //private 1128 String[] splitBodies(String layout) { 1129 List<String> bodies = new ArrayList<>(); 1130 // Parse several independent layout bodies: "[foo][bar]...[baz]" 1131 for (int i = 0; i < layout.length(); i++) { 1132 if (layout.charAt(i++) != '[') 1133 layout.charAt(-i); // throw error 1134 int body; 1135 i = skipBody(layout, body = i); 1136 bodies.add(layout.substring(body, i)); 1137 } 1138 String[] res = new String[bodies.size()]; 1139 bodies.toArray(res); 1140 return res; 1141 } 1142 private static 1143 int skipBody(String layout, int i) { 1144 assert(layout.charAt(i-1) == '['); 1145 if (layout.charAt(i) == ']') 1146 // No empty bodies, please. 1147 return -i; 1148 // skip balanced [...[...]...] 1149 for (int depth = 1; depth > 0; ) { 1150 switch (layout.charAt(i++)) { 1151 case '[': depth++; break; 1152 case ']': depth--; break; 1153 } 1154 } 1155 --i; // get before bracket 1156 assert(layout.charAt(i) == ']'); 1157 return i; // return closing bracket 1158 } 1159 private static 1160 int tokenizeUInt(Layout.Element e, String layout, int i) { 1161 switch (layout.charAt(i++)) { 1162 case 'V': e.len = 0; break; 1163 case 'B': e.len = 1; break; 1164 case 'H': e.len = 2; break; 1165 case 'I': e.len = 4; break; 1166 default: return -i; 1167 } 1168 return i; 1169 } 1170 private static 1171 int tokenizeSInt(Layout.Element e, String layout, int i) { 1172 if (layout.charAt(i) == 'S') { 1173 e.flags |= EF_SIGN; 1174 ++i; 1175 } 1176 return tokenizeUInt(e, layout, i); 1177 } 1178 1179 private static 1180 boolean isDigit(char c) { 1181 return c >= '0' && c <= '9'; 1182 } 1183 1184 /** Find an occurrence of hyphen '-' between two numerals. */ 1185 static //private 1186 int findCaseDash(String layout, int fromIndex) { 1187 if (fromIndex <= 0) fromIndex = 1; // minimum dash pos 1188 int lastDash = layout.length() - 2; // maximum dash pos 1189 for (;;) { 1190 int dash = layout.indexOf('-', fromIndex); 1191 if (dash < 0 || dash > lastDash) return -1; 1192 if (isDigit(layout.charAt(dash-1))) { 1193 char afterDash = layout.charAt(dash+1); 1194 if (afterDash == '-' && dash+2 < layout.length()) 1195 afterDash = layout.charAt(dash+2); 1196 if (isDigit(afterDash)) { 1197 // matched /[0-9]--?[0-9]/; return position of dash 1198 return dash; 1199 } 1200 } 1201 fromIndex = dash+1; 1202 } 1203 } 1204 static 1205 int parseIntBefore(String layout, int dash) { 1206 int end = dash; 1207 int beg = end; 1208 while (beg > 0 && isDigit(layout.charAt(beg-1))) { 1209 --beg; 1210 } 1211 if (beg == end) return Integer.parseInt("empty"); 1212 // skip backward over a sign 1213 if (beg >= 1 && layout.charAt(beg-1) == '-') --beg; 1214 assert(beg == 0 || !isDigit(layout.charAt(beg-1))); 1215 return Integer.parseInt(layout.substring(beg, end)); 1216 } 1217 static 1218 int parseIntAfter(String layout, int dash) { 1219 int beg = dash+1; 1220 int end = beg; 1221 int limit = layout.length(); 1222 if (end < limit && layout.charAt(end) == '-') ++end; 1223 while (end < limit && isDigit(layout.charAt(end))) { 1224 ++end; 1225 } 1226 if (beg == end) return Integer.parseInt("empty"); 1227 return Integer.parseInt(layout.substring(beg, end)); 1228 } 1229 /** For compatibility with 1.5 pack, expand 1-5 into 1,2,3,4,5. */ 1230 static 1231 String expandCaseDashNotation(String layout) { 1232 int dash = findCaseDash(layout, 0); 1233 if (dash < 0) return layout; // no dashes (the common case) 1234 StringBuilder result = new StringBuilder(layout.length() * 3); 1235 int sofar = 0; // how far have we processed the layout? 1236 for (;;) { 1237 // for each dash, collect everything up to the dash 1238 result.append(layout, sofar, dash); 1239 sofar = dash+1; // skip the dash 1240 // then collect intermediate values 1241 int value0 = parseIntBefore(layout, dash); 1242 int value1 = parseIntAfter(layout, dash); 1243 assert(value0 < value1); 1244 result.append(","); // close off value0 numeral 1245 for (int i = value0+1; i < value1; i++) { 1246 result.append(i); 1247 result.append(","); // close off i numeral 1248 } 1249 dash = findCaseDash(layout, sofar); 1250 if (dash < 0) break; 1251 } 1252 result.append(layout, sofar, layout.length()); // collect the rest 1253 return result.toString(); 1254 } 1255 static { 1256 assert(expandCaseDashNotation("1-5").equals("1,2,3,4,5")); 1257 assert(expandCaseDashNotation("-2--1").equals("-2,-1")); 1258 assert(expandCaseDashNotation("-2-1").equals("-2,-1,0,1")); 1259 assert(expandCaseDashNotation("-1-0").equals("-1,0")); 1260 } 1261 1262 // Parse attribute bytes, putting values into bands. Returns new pos. 1263 // Used when reading a class file (local refs resolved with local cpMap). 1264 // Also used for ad hoc scanning. 1265 static 1266 int parseUsing(Layout.Element[] elems, Holder holder, 1267 byte[] bytes, int pos, int len, ValueStream out) { 1268 int prevBCI = 0; 1269 int prevRBCI = 0; 1270 int end = pos + len; 1271 int[] buf = { 0 }; // for calls to parseInt, holds 2nd result 1272 for (int i = 0; i < elems.length; i++) { 1273 Layout.Element e = elems[i]; 1274 int bandIndex = e.bandIndex; 1275 int value; 1276 int BCI, RBCI; 1277 switch (e.kind) { 1278 case EK_INT: 1279 pos = parseInt(e, bytes, pos, buf); 1280 value = buf[0]; 1281 out.putInt(bandIndex, value); 1282 break; 1283 case EK_BCI: // PH, POH 1284 pos = parseInt(e, bytes, pos, buf); 1285 BCI = buf[0]; 1286 RBCI = out.encodeBCI(BCI); 1287 if (!e.flagTest(EF_DELTA)) { 1288 // PH: transmit R(bci), store bci 1289 value = RBCI; 1290 } else { 1291 // POH: transmit D(R(bci)), store bci 1292 value = RBCI - prevRBCI; 1293 } 1294 prevBCI = BCI; 1295 prevRBCI = RBCI; 1296 out.putInt(bandIndex, value); 1297 break; 1298 case EK_BCO: // OH 1299 assert(e.flagTest(EF_DELTA)); 1300 // OH: transmit D(R(bci)), store D(bci) 1301 pos = parseInt(e, bytes, pos, buf); 1302 BCI = prevBCI + buf[0]; 1303 RBCI = out.encodeBCI(BCI); 1304 value = RBCI - prevRBCI; 1305 prevBCI = BCI; 1306 prevRBCI = RBCI; 1307 out.putInt(bandIndex, value); 1308 break; 1309 case EK_FLAG: 1310 pos = parseInt(e, bytes, pos, buf); 1311 value = buf[0]; 1312 out.putInt(bandIndex, value); 1313 break; 1314 case EK_REPL: 1315 pos = parseInt(e, bytes, pos, buf); 1316 value = buf[0]; 1317 out.putInt(bandIndex, value); 1318 for (int j = 0; j < value; j++) { 1319 pos = parseUsing(e.body, holder, bytes, pos, end-pos, out); 1320 } 1321 break; // already transmitted the scalar value 1322 case EK_UN: 1323 pos = parseInt(e, bytes, pos, buf); 1324 value = buf[0]; 1325 out.putInt(bandIndex, value); 1326 Layout.Element ce = matchCase(e, value); 1327 pos = parseUsing(ce.body, holder, bytes, pos, end-pos, out); 1328 1329 break; // already transmitted the scalar value 1330 case EK_CALL: 1331 // Adjust band offset if it is a backward call. 1332 assert(e.body.length == 1); 1333 assert(e.body[0].kind == EK_CBLE); 1334 if (e.flagTest(EF_BACK)) 1335 out.noteBackCall(e.value); 1336 pos = parseUsing(e.body[0].body, holder, bytes, pos, end-pos, out); 1337 break; // no additional scalar value to transmit 1338 case EK_REF: 1339 pos = parseInt(e, bytes, pos, buf); 1340 int localRef = buf[0]; 1341 Entry globalRef; 1342 if (localRef == 0) { 1343 globalRef = null; // N.B. global null reference is -1 1344 } else { 1345 Entry[] cpMap = holder.getCPMap(); 1346 globalRef = (localRef >= 0 && localRef < cpMap.length 1347 ? cpMap[localRef] 1348 : null); 1349 byte tag = e.refKind; 1350 if (globalRef != null && tag == CONSTANT_Signature 1351 && globalRef.getTag() == CONSTANT_Utf8) { 1352 // Cf. ClassReader.readSignatureRef. 1353 String typeName = globalRef.stringValue(); 1354 globalRef = ConstantPool.getSignatureEntry(typeName); 1355 } 1356 String got = (globalRef == null 1357 ? "invalid CP index" 1358 : "type=" + ConstantPool.tagName(globalRef.tag)); 1359 if (globalRef == null || !globalRef.tagMatches(tag)) { 1360 throw new IllegalArgumentException( 1361 "Bad constant, expected type=" + 1362 ConstantPool.tagName(tag) + " got " + got); 1363 } 1364 } 1365 out.putRef(bandIndex, globalRef); 1366 break; 1367 default: assert(false); 1368 } 1369 } 1370 return pos; 1371 } 1372 1373 static 1374 Layout.Element matchCase(Layout.Element e, int value) { 1375 assert(e.kind == EK_UN); 1376 int lastj = e.body.length-1; 1377 for (int j = 0; j < lastj; j++) { 1378 Layout.Element ce = e.body[j]; 1379 assert(ce.kind == EK_CASE); 1380 if (value == ce.value) 1381 return ce; 1382 } 1383 return e.body[lastj]; 1384 } 1385 1386 private static 1387 int parseInt(Layout.Element e, byte[] bytes, int pos, int[] buf) { 1388 int value = 0; 1389 int loBits = e.len * 8; 1390 // Read in big-endian order: 1391 for (int bitPos = loBits; (bitPos -= 8) >= 0; ) { 1392 value += (bytes[pos++] & 0xFF) << bitPos; 1393 } 1394 if (loBits < 32 && e.flagTest(EF_SIGN)) { 1395 // sign-extend subword value 1396 int hiBits = 32 - loBits; 1397 value = (value << hiBits) >> hiBits; 1398 } 1399 buf[0] = value; 1400 return pos; 1401 } 1402 1403 // Format attribute bytes, drawing values from bands. 1404 // Used when emptying attribute bands into a package model. 1405 // (At that point CP refs. are not yet assigned indexes.) 1406 static 1407 void unparseUsing(Layout.Element[] elems, Object[] fixups, 1408 ValueStream in, ByteArrayOutputStream out) { 1409 int prevBCI = 0; 1410 int prevRBCI = 0; 1411 for (int i = 0; i < elems.length; i++) { 1412 Layout.Element e = elems[i]; 1413 int bandIndex = e.bandIndex; 1414 int value; 1415 int BCI, RBCI; // "RBCI" is R(BCI), BCI's coded representation 1416 switch (e.kind) { 1417 case EK_INT: 1418 value = in.getInt(bandIndex); 1419 unparseInt(e, value, out); 1420 break; 1421 case EK_BCI: // PH, POH 1422 value = in.getInt(bandIndex); 1423 if (!e.flagTest(EF_DELTA)) { 1424 // PH: transmit R(bci), store bci 1425 RBCI = value; 1426 } else { 1427 // POH: transmit D(R(bci)), store bci 1428 RBCI = prevRBCI + value; 1429 } 1430 assert(prevBCI == in.decodeBCI(prevRBCI)); 1431 BCI = in.decodeBCI(RBCI); 1432 unparseInt(e, BCI, out); 1433 prevBCI = BCI; 1434 prevRBCI = RBCI; 1435 break; 1436 case EK_BCO: // OH 1437 value = in.getInt(bandIndex); 1438 assert(e.flagTest(EF_DELTA)); 1439 // OH: transmit D(R(bci)), store D(bci) 1440 assert(prevBCI == in.decodeBCI(prevRBCI)); 1441 RBCI = prevRBCI + value; 1442 BCI = in.decodeBCI(RBCI); 1443 unparseInt(e, BCI - prevBCI, out); 1444 prevBCI = BCI; 1445 prevRBCI = RBCI; 1446 break; 1447 case EK_FLAG: 1448 value = in.getInt(bandIndex); 1449 unparseInt(e, value, out); 1450 break; 1451 case EK_REPL: 1452 value = in.getInt(bandIndex); 1453 unparseInt(e, value, out); 1454 for (int j = 0; j < value; j++) { 1455 unparseUsing(e.body, fixups, in, out); 1456 } 1457 break; 1458 case EK_UN: 1459 value = in.getInt(bandIndex); 1460 unparseInt(e, value, out); 1461 Layout.Element ce = matchCase(e, value); 1462 unparseUsing(ce.body, fixups, in, out); 1463 break; 1464 case EK_CALL: 1465 assert(e.body.length == 1); 1466 assert(e.body[0].kind == EK_CBLE); 1467 unparseUsing(e.body[0].body, fixups, in, out); 1468 break; 1469 case EK_REF: 1470 Entry globalRef = in.getRef(bandIndex); 1471 int localRef; 1472 if (globalRef != null) { 1473 // It's a one-element array, really an lvalue. 1474 fixups[0] = Fixups.addRefWithLoc(fixups[0], out.size(), globalRef); 1475 localRef = 0; // placeholder for fixups 1476 } else { 1477 localRef = 0; // fixed null value 1478 } 1479 unparseInt(e, localRef, out); 1480 break; 1481 default: assert(false); continue; 1482 } 1483 } 1484 } 1485 1486 private static 1487 void unparseInt(Layout.Element e, int value, ByteArrayOutputStream out) { 1488 int loBits = e.len * 8; 1489 if (loBits == 0) { 1490 // It is not stored at all ('V' layout). 1491 return; 1492 } 1493 if (loBits < 32) { 1494 int hiBits = 32 - loBits; 1495 int codedValue; 1496 if (e.flagTest(EF_SIGN)) 1497 codedValue = (value << hiBits) >> hiBits; 1498 else 1499 codedValue = (value << hiBits) >>> hiBits; 1500 if (codedValue != value) 1501 throw new InternalError("cannot code in "+e.len+" bytes: "+value); 1502 } 1503 // Write in big-endian order: 1504 for (int bitPos = loBits; (bitPos -= 8) >= 0; ) { 1505 out.write((byte)(value >>> bitPos)); 1506 } 1507 } 1508 1509 /* 1510 /// Testing. 1511 public static void main(String av[]) { 1512 int maxVal = 12; 1513 int iters = 0; 1514 boolean verbose; 1515 int ap = 0; 1516 while (ap < av.length) { 1517 if (!av[ap].startsWith("-")) break; 1518 if (av[ap].startsWith("-m")) 1519 maxVal = Integer.parseInt(av[ap].substring(2)); 1520 else if (av[ap].startsWith("-i")) 1521 iters = Integer.parseInt(av[ap].substring(2)); 1522 else 1523 throw new RuntimeException("Bad option: "+av[ap]); 1524 ap++; 1525 } 1526 verbose = (iters == 0); 1527 if (iters <= 0) iters = 1; 1528 if (ap == av.length) { 1529 av = new String[] { 1530 "HH", // ClassFile.version 1531 "RUH", // SourceFile 1532 "RCHRDNH", // EnclosingMethod 1533 "KQH", // ConstantValue 1534 "NH[RCH]", // Exceptions 1535 "NH[PHH]", // LineNumberTable 1536 "NH[PHOHRUHRSHH]", // LocalVariableTable 1537 "NH[PHPOHIIH]", // CharacterRangeTable 1538 "NH[PHHII]", // CoverageTable 1539 "NH[RCHRCNHRUNHFH]", // InnerClasses 1540 "NH[RMHNH[KLH]]", // BootstrapMethods 1541 "HHNI[B]NH[PHPOHPOHRCNH]NH[RUHNI[B]]", // Code 1542 "=AnnotationDefault", 1543 // Like metadata, but with a compact tag set: 1544 "[NH[(1)]]" 1545 +"[NH[(1)]]" 1546 +"[RSHNH[RUH(1)]]" 1547 +"[TB(0,1,3)[KIH](2)[KDH](5)[KFH](4)[KJH](7)[RSH](8)[RSHRUH](9)[RUH](10)[(-1)](6)[NH[(0)]]()[]]", 1548 "" 1549 }; 1550 ap = 0; 1551 } 1552 Utils.currentInstance.set(new PackerImpl()); 1553 final int[][] counts = new int[2][3]; // int bci ref 1554 final Entry[] cpMap = new Entry[maxVal+1]; 1555 for (int i = 0; i < cpMap.length; i++) { 1556 if (i == 0) continue; // 0 => null 1557 cpMap[i] = ConstantPool.getLiteralEntry(new Integer(i)); 1558 } 1559 Package.Class cls = new Package().new Class(""); 1560 cls.cpMap = cpMap; 1561 class TestValueStream extends ValueStream { 1562 java.util.Random rand = new java.util.Random(0); 1563 ArrayList history = new ArrayList(); 1564 int ckidx = 0; 1565 int maxVal; 1566 boolean verbose; 1567 void reset() { history.clear(); ckidx = 0; } 1568 public int getInt(int bandIndex) { 1569 counts[0][0]++; 1570 int value = rand.nextInt(maxVal+1); 1571 history.add(new Integer(bandIndex)); 1572 history.add(new Integer(value)); 1573 return value; 1574 } 1575 public void putInt(int bandIndex, int token) { 1576 counts[1][0]++; 1577 if (verbose) 1578 System.out.print(" "+bandIndex+":"+token); 1579 // Make sure this put parallels a previous get: 1580 int check0 = ((Integer)history.get(ckidx+0)).intValue(); 1581 int check1 = ((Integer)history.get(ckidx+1)).intValue(); 1582 if (check0 != bandIndex || check1 != token) { 1583 if (!verbose) 1584 System.out.println(history.subList(0, ckidx)); 1585 System.out.println(" *** Should be "+check0+":"+check1); 1586 throw new RuntimeException("Failed test!"); 1587 } 1588 ckidx += 2; 1589 } 1590 public Entry getRef(int bandIndex) { 1591 counts[0][2]++; 1592 int value = getInt(bandIndex); 1593 if (value < 0 || value > maxVal) { 1594 System.out.println(" *** Unexpected ref code "+value); 1595 return ConstantPool.getLiteralEntry(new Integer(value)); 1596 } 1597 return cpMap[value]; 1598 } 1599 public void putRef(int bandIndex, Entry ref) { 1600 counts[1][2]++; 1601 if (ref == null) { 1602 putInt(bandIndex, 0); 1603 return; 1604 } 1605 Number refValue = null; 1606 if (ref instanceof ConstantPool.NumberEntry) 1607 refValue = ((ConstantPool.NumberEntry)ref).numberValue(); 1608 int value; 1609 if (!(refValue instanceof Integer)) { 1610 System.out.println(" *** Unexpected ref "+ref); 1611 value = -1; 1612 } else { 1613 value = ((Integer)refValue).intValue(); 1614 } 1615 putInt(bandIndex, value); 1616 } 1617 public int encodeBCI(int bci) { 1618 counts[1][1]++; 1619 // move LSB to MSB of low byte 1620 int code = (bci >> 8) << 8; // keep high bits 1621 code += (bci & 0xFE) >> 1; 1622 code += (bci & 0x01) << 7; 1623 return code ^ (8<<8); // mark it clearly as coded 1624 } 1625 public int decodeBCI(int bciCode) { 1626 counts[0][1]++; 1627 bciCode ^= (8<<8); // remove extra mark 1628 int bci = (bciCode >> 8) << 8; // keep high bits 1629 bci += (bciCode & 0x7F) << 1; 1630 bci += (bciCode & 0x80) >> 7; 1631 return bci; 1632 } 1633 } 1634 TestValueStream tts = new TestValueStream(); 1635 tts.maxVal = maxVal; 1636 tts.verbose = verbose; 1637 ByteArrayOutputStream buf = new ByteArrayOutputStream(); 1638 for (int i = 0; i < (1 << 30); i = (i + 1) * 5) { 1639 int ei = tts.encodeBCI(i); 1640 int di = tts.decodeBCI(ei); 1641 if (di != i) System.out.println("i="+Integer.toHexString(i)+ 1642 " ei="+Integer.toHexString(ei)+ 1643 " di="+Integer.toHexString(di)); 1644 } 1645 while (iters-- > 0) { 1646 for (int i = ap; i < av.length; i++) { 1647 String layout = av[i]; 1648 if (layout.startsWith("=")) { 1649 String name = layout.substring(1); 1650 for (Attribute a : standardDefs.values()) { 1651 if (a.name().equals(name)) { 1652 layout = a.layout().layout(); 1653 break; 1654 } 1655 } 1656 if (layout.startsWith("=")) { 1657 System.out.println("Could not find "+name+" in "+standardDefs.values()); 1658 } 1659 } 1660 Layout self = new Layout(0, "Foo", layout); 1661 if (verbose) { 1662 System.out.print("/"+layout+"/ => "); 1663 System.out.println(Arrays.asList(self.elems)); 1664 } 1665 buf.reset(); 1666 tts.reset(); 1667 Object fixups = self.unparse(tts, buf); 1668 byte[] bytes = buf.toByteArray(); 1669 // Attach the references to the byte array. 1670 Fixups.setBytes(fixups, bytes); 1671 // Patch the references to their frozen values. 1672 Fixups.finishRefs(fixups, bytes, new Index("test", cpMap)); 1673 if (verbose) { 1674 System.out.print(" bytes: {"); 1675 for (int j = 0; j < bytes.length; j++) { 1676 System.out.print(" "+bytes[j]); 1677 } 1678 System.out.println("}"); 1679 } 1680 if (verbose) { 1681 System.out.print(" parse: {"); 1682 } 1683 self.parse(cls, bytes, 0, bytes.length, tts); 1684 if (verbose) { 1685 System.out.println("}"); 1686 } 1687 } 1688 } 1689 for (int j = 0; j <= 1; j++) { 1690 System.out.print("values "+(j==0?"read":"written")+": {"); 1691 for (int k = 0; k < counts[j].length; k++) { 1692 System.out.print(" "+counts[j][k]); 1693 } 1694 System.out.println(" }"); 1695 } 1696 } 1697 //*/ 1698 } 1699