|
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 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.length() == 0) |
|
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 } |