1 | /* |
2 | * Copyright 1999-2006 Sun Microsystems, Inc. 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. Sun designates this |
8 | * particular file as subject to the "Classpath" exception as provided |
9 | * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, |
22 | * CA 95054 USA or visit www.sun.com if you need additional information or |
23 | * have any questions. |
24 | */ |
25 | |
26 | package com.sun.tools.javac.jvm; |
27 | |
28 | import java.io.*; |
29 | import java.util.*; |
30 | import java.util.Set; |
31 | import java.util.HashSet; |
32 | |
33 | import javax.tools.JavaFileManager; |
34 | import javax.tools.FileObject; |
35 | import javax.tools.JavaFileObject; |
36 | |
37 | import com.sun.tools.javac.code.*; |
38 | import com.sun.tools.javac.code.Symbol.*; |
39 | import com.sun.tools.javac.code.Type.*; |
40 | import com.sun.tools.javac.util.*; |
41 | import com.sun.tools.javac.util.List; |
42 | |
43 | import static com.sun.tools.javac.code.BoundKind.*; |
44 | import static com.sun.tools.javac.code.Flags.*; |
45 | import static com.sun.tools.javac.code.Kinds.*; |
46 | import static com.sun.tools.javac.code.TypeTags.*; |
47 | import static com.sun.tools.javac.jvm.UninitializedType.*; |
48 | import static javax.tools.StandardLocation.CLASS_OUTPUT; |
49 | |
50 | /** This class provides operations to map an internal symbol table graph |
51 | * rooted in a ClassSymbol into a classfile. |
52 | * |
53 | * <p><b>This is NOT part of any API supported by Sun Microsystems. If |
54 | * you write code that depends on this, you do so at your own risk. |
55 | * This code and its internal interfaces are subject to change or |
56 | * deletion without notice.</b> |
57 | */ |
58 | public class ClassWriter extends ClassFile { |
59 | protected static final Context.Key<ClassWriter> classWriterKey = |
60 | new Context.Key<ClassWriter>(); |
61 | |
62 | private final Symtab syms; |
63 | |
64 | private final Options options; |
65 | |
66 | /** Switch: verbose output. |
67 | */ |
68 | private boolean verbose; |
69 | |
70 | /** Switch: scrable private names. |
71 | */ |
72 | private boolean scramble; |
73 | |
74 | /** Switch: scrable private names. |
75 | */ |
76 | private boolean scrambleAll; |
77 | |
78 | /** Switch: retrofit mode. |
79 | */ |
80 | private boolean retrofit; |
81 | |
82 | /** Switch: emit source file attribute. |
83 | */ |
84 | private boolean emitSourceFile; |
85 | |
86 | /** Switch: generate CharacterRangeTable attribute. |
87 | */ |
88 | private boolean genCrt; |
89 | |
90 | /** Switch: describe the generated stackmap |
91 | */ |
92 | boolean debugstackmap; |
93 | |
94 | /** |
95 | * Target class version. |
96 | */ |
97 | private Target target; |
98 | |
99 | /** |
100 | * Source language version. |
101 | */ |
102 | private Source source; |
103 | |
104 | /** Type utilities. */ |
105 | private Types types; |
106 | |
107 | /** The initial sizes of the data and constant pool buffers. |
108 | * sizes are increased when buffers get full. |
109 | */ |
110 | static final int DATA_BUF_SIZE = 0x0fff0; |
111 | static final int POOL_BUF_SIZE = 0x1fff0; |
112 | |
113 | /** An output buffer for member info. |
114 | */ |
115 | ByteBuffer databuf = new ByteBuffer(DATA_BUF_SIZE); |
116 | |
117 | /** An output buffer for the constant pool. |
118 | */ |
119 | ByteBuffer poolbuf = new ByteBuffer(POOL_BUF_SIZE); |
120 | |
121 | /** An output buffer for type signatures. |
122 | */ |
123 | ByteBuffer sigbuf = new ByteBuffer(); |
124 | |
125 | /** The constant pool. |
126 | */ |
127 | Pool pool; |
128 | |
129 | /** The inner classes to be written, as a set. |
130 | */ |
131 | Set<ClassSymbol> innerClasses; |
132 | |
133 | /** The inner classes to be written, as a queue where |
134 | * enclosing classes come first. |
135 | */ |
136 | ListBuffer<ClassSymbol> innerClassesQueue; |
137 | |
138 | /** The log to use for verbose output. |
139 | */ |
140 | private final Log log; |
141 | |
142 | /** The name table. */ |
143 | private final Name.Table names; |
144 | |
145 | /** Access to files. */ |
146 | private final JavaFileManager fileManager; |
147 | |
148 | /** The tags and constants used in compressed stackmap. */ |
149 | static final int SAME_FRAME_SIZE = 64; |
150 | static final int SAME_LOCALS_1_STACK_ITEM_EXTENDED = 247; |
151 | static final int SAME_FRAME_EXTENDED = 251; |
152 | static final int FULL_FRAME = 255; |
153 | static final int MAX_LOCAL_LENGTH_DIFF = 4; |
154 | |
155 | /** Get the ClassWriter instance for this context. */ |
156 | public static ClassWriter instance(Context context) { |
157 | ClassWriter instance = context.get(classWriterKey); |
158 | if (instance == null) |
159 | instance = new ClassWriter(context); |
160 | return instance; |
161 | } |
162 | |
163 | /** Construct a class writer, given an options table. |
164 | */ |
165 | private ClassWriter(Context context) { |
166 | context.put(classWriterKey, this); |
167 | |
168 | log = Log.instance(context); |
169 | names = Name.Table.instance(context); |
170 | syms = Symtab.instance(context); |
171 | options = Options.instance(context); |
172 | target = Target.instance(context); |
173 | source = Source.instance(context); |
174 | types = Types.instance(context); |
175 | fileManager = context.get(JavaFileManager.class); |
176 | |
177 | verbose = options.get("-verbose") != null; |
178 | scramble = options.get("-scramble") != null; |
179 | scrambleAll = options.get("-scrambleAll") != null; |
180 | retrofit = options.get("-retrofit") != null; |
181 | genCrt = options.get("-Xjcov") != null; |
182 | debugstackmap = options.get("debugstackmap") != null; |
183 | |
184 | emitSourceFile = options.get("-g:")==null || options.get("-g:source")!=null; |
185 | |
186 | String dumpModFlags = options.get("dumpmodifiers"); |
187 | dumpClassModifiers = |
188 | (dumpModFlags != null && dumpModFlags.indexOf('c') != -1); |
189 | dumpFieldModifiers = |
190 | (dumpModFlags != null && dumpModFlags.indexOf('f') != -1); |
191 | dumpInnerClassModifiers = |
192 | (dumpModFlags != null && dumpModFlags.indexOf('i') != -1); |
193 | dumpMethodModifiers = |
194 | (dumpModFlags != null && dumpModFlags.indexOf('m') != -1); |
195 | } |
196 | |
197 | /****************************************************************** |
198 | * Diagnostics: dump generated class names and modifiers |
199 | ******************************************************************/ |
200 | |
201 | /** Value of option 'dumpmodifiers' is a string |
202 | * indicating which modifiers should be dumped for debugging: |
203 | * 'c' -- classes |
204 | * 'f' -- fields |
205 | * 'i' -- innerclass attributes |
206 | * 'm' -- methods |
207 | * For example, to dump everything: |
208 | * javac -XDdumpmodifiers=cifm MyProg.java |
209 | */ |
210 | private final boolean dumpClassModifiers; // -XDdumpmodifiers=c |
211 | private final boolean dumpFieldModifiers; // -XDdumpmodifiers=f |
212 | private final boolean dumpInnerClassModifiers; // -XDdumpmodifiers=i |
213 | private final boolean dumpMethodModifiers; // -XDdumpmodifiers=m |
214 | |
215 | |
216 | /** Return flags as a string, separated by " ". |
217 | */ |
218 | public static String flagNames(long flags) { |
219 | StringBuffer sbuf = new StringBuffer(); |
220 | int i = 0; |
221 | long f = flags & StandardFlags; |
222 | while (f != 0) { |
223 | if ((f & 1) != 0) sbuf.append(" " + flagName[i]); |
224 | f = f >> 1; |
225 | i++; |
226 | } |
227 | return sbuf.toString(); |
228 | } |
229 | //where |
230 | private final static String[] flagName = { |
231 | "PUBLIC", "PRIVATE", "PROTECTED", "STATIC", "FINAL", |
232 | "SUPER", "VOLATILE", "TRANSIENT", "NATIVE", "INTERFACE", |
233 | "ABSTRACT", "STRICTFP"}; |
234 | |
235 | /****************************************************************** |
236 | * Output routines |
237 | ******************************************************************/ |
238 | |
239 | /** Write a character into given byte buffer; |
240 | * byte buffer will not be grown. |
241 | */ |
242 | void putChar(ByteBuffer buf, int op, int x) { |
243 | buf.elems[op ] = (byte)((x >> 8) & 0xFF); |
244 | buf.elems[op+1] = (byte)((x ) & 0xFF); |
245 | } |
246 | |
247 | /** Write an integer into given byte buffer; |
248 | * byte buffer will not be grown. |
249 | */ |
250 | void putInt(ByteBuffer buf, int adr, int x) { |
251 | buf.elems[adr ] = (byte)((x >> 24) & 0xFF); |
252 | buf.elems[adr+1] = (byte)((x >> 16) & 0xFF); |
253 | buf.elems[adr+2] = (byte)((x >> 8) & 0xFF); |
254 | buf.elems[adr+3] = (byte)((x ) & 0xFF); |
255 | } |
256 | |
257 | /****************************************************************** |
258 | * Signature Generation |
259 | ******************************************************************/ |
260 | |
261 | /** Assemble signature of given type in string buffer. |
262 | */ |
263 | void assembleSig(Type type) { |
264 | switch (type.tag) { |
265 | case BYTE: |
266 | sigbuf.appendByte('B'); |
267 | break; |
268 | case SHORT: |
269 | sigbuf.appendByte('S'); |
270 | break; |
271 | case CHAR: |
272 | sigbuf.appendByte('C'); |
273 | break; |
274 | case INT: |
275 | sigbuf.appendByte('I'); |
276 | break; |
277 | case LONG: |
278 | sigbuf.appendByte('J'); |
279 | break; |
280 | case FLOAT: |
281 | sigbuf.appendByte('F'); |
282 | break; |
283 | case DOUBLE: |
284 | sigbuf.appendByte('D'); |
285 | break; |
286 | case BOOLEAN: |
287 | sigbuf.appendByte('Z'); |
288 | break; |
289 | case VOID: |
290 | sigbuf.appendByte('V'); |
291 | break; |
292 | case CLASS: |
293 | sigbuf.appendByte('L'); |
294 | assembleClassSig(type); |
295 | sigbuf.appendByte(';'); |
296 | break; |
297 | case ARRAY: |
298 | ArrayType at = (ArrayType)type; |
299 | sigbuf.appendByte('['); |
300 | assembleSig(at.elemtype); |
301 | break; |
302 | case METHOD: |
303 | MethodType mt = (MethodType)type; |
304 | sigbuf.appendByte('('); |
305 | assembleSig(mt.argtypes); |
306 | sigbuf.appendByte(')'); |
307 | assembleSig(mt.restype); |
308 | if (hasTypeVar(mt.thrown)) { |
309 | for (List<Type> l = mt.thrown; l.nonEmpty(); l = l.tail) { |
310 | sigbuf.appendByte('^'); |
311 | assembleSig(l.head); |
312 | } |
313 | } |
314 | break; |
315 | case WILDCARD: { |
316 | WildcardType ta = (WildcardType) type; |
317 | switch (ta.kind) { |
318 | case SUPER: |
319 | sigbuf.appendByte('-'); |
320 | assembleSig(ta.type); |
321 | break; |
322 | case EXTENDS: |
323 | sigbuf.appendByte('+'); |
324 | assembleSig(ta.type); |
325 | break; |
326 | case UNBOUND: |
327 | sigbuf.appendByte('*'); |
328 | break; |
329 | default: |
330 | throw new AssertionError(ta.kind); |
331 | } |
332 | break; |
333 | } |
334 | case TYPEVAR: |
335 | sigbuf.appendByte('T'); |
336 | sigbuf.appendName(type.tsym.name); |
337 | sigbuf.appendByte(';'); |
338 | break; |
339 | case FORALL: |
340 | ForAll ft = (ForAll)type; |
341 | assembleParamsSig(ft.tvars); |
342 | assembleSig(ft.qtype); |
343 | break; |
344 | case UNINITIALIZED_THIS: |
345 | case UNINITIALIZED_OBJECT: |
346 | // we don't yet have a spec for uninitialized types in the |
347 | // local variable table |
348 | assembleSig(types.erasure(((UninitializedType)type).qtype)); |
349 | break; |
350 | default: |
351 | throw new AssertionError("typeSig " + type.tag); |
352 | } |
353 | } |
354 | |
355 | boolean hasTypeVar(List<Type> l) { |
356 | while (l.nonEmpty()) { |
357 | if (l.head.tag == TypeTags.TYPEVAR) return true; |
358 | l = l.tail; |
359 | } |
360 | return false; |
361 | } |
362 | |
363 | void assembleClassSig(Type type) { |
364 | ClassType ct = (ClassType)type; |
365 | ClassSymbol c = (ClassSymbol)ct.tsym; |
366 | enterInner(c); |
367 | Type outer = ct.getEnclosingType(); |
368 | if (outer.allparams().nonEmpty()) { |
369 | boolean rawOuter = |
370 | c.owner.kind == MTH || // either a local class |
371 | c.name == names.empty; // or anonymous |
372 | assembleClassSig(rawOuter |
373 | ? types.erasure(outer) |
374 | : outer); |
375 | sigbuf.appendByte('.'); |
376 | assert c.flatname.startsWith(c.owner.enclClass().flatname); |
377 | sigbuf.appendName(rawOuter |
378 | ? c.flatname.subName(c.owner.enclClass() |
379 | .flatname.len+1, |
380 | c.flatname.len) |
381 | : c.name); |
382 | } else { |
383 | sigbuf.appendBytes(externalize(c.flatname)); |
384 | } |
385 | if (ct.getTypeArguments().nonEmpty()) { |
386 | sigbuf.appendByte('<'); |
387 | assembleSig(ct.getTypeArguments()); |
388 | sigbuf.appendByte('>'); |
389 | } |
390 | } |
391 | |
392 | |
393 | void assembleSig(List<Type> types) { |
394 | for (List<Type> ts = types; ts.nonEmpty(); ts = ts.tail) |
395 | assembleSig(ts.head); |
396 | } |
397 | |
398 | void assembleParamsSig(List<Type> typarams) { |
399 | sigbuf.appendByte('<'); |
400 | for (List<Type> ts = typarams; ts.nonEmpty(); ts = ts.tail) { |
401 | TypeVar tvar = (TypeVar)ts.head; |
402 | sigbuf.appendName(tvar.tsym.name); |
403 | List<Type> bounds = types.getBounds(tvar); |
404 | if ((bounds.head.tsym.flags() & INTERFACE) != 0) { |
405 | sigbuf.appendByte(':'); |
406 | } |
407 | for (List<Type> l = bounds; l.nonEmpty(); l = l.tail) { |
408 | sigbuf.appendByte(':'); |
409 | assembleSig(l.head); |
410 | } |
411 | } |
412 | sigbuf.appendByte('>'); |
413 | } |
414 | |
415 | /** Return signature of given type |
416 | */ |
417 | Name typeSig(Type type) { |
418 | assert sigbuf.length == 0; |
419 | //- System.out.println(" ? " + type); |
420 | assembleSig(type); |
421 | Name n = sigbuf.toName(names); |
422 | sigbuf.reset(); |
423 | //- System.out.println(" " + n); |
424 | return n; |
425 | } |
426 | |
427 | /** Given a type t, return the extended class name of its erasure in |
428 | * external representation. |
429 | */ |
430 | public Name xClassName(Type t) { |
431 | if (t.tag == CLASS) { |
432 | return names.fromUtf(externalize(t.tsym.flatName())); |
433 | } else if (t.tag == ARRAY) { |
434 | return typeSig(types.erasure(t)); |
435 | } else { |
436 | throw new AssertionError("xClassName"); |
437 | } |
438 | } |
439 | |
440 | /****************************************************************** |
441 | * Writing the Constant Pool |
442 | ******************************************************************/ |
443 | |
444 | /** Thrown when the constant pool is over full. |
445 | */ |
446 | public static class PoolOverflow extends Exception { |
447 | private static final long serialVersionUID = 0; |
448 | public PoolOverflow() {} |
449 | } |
450 | public static class StringOverflow extends Exception { |
451 | private static final long serialVersionUID = 0; |
452 | public final String value; |
453 | public StringOverflow(String s) { |
454 | value = s; |
455 | } |
456 | } |
457 | |
458 | /** Write constant pool to pool buffer. |
459 | * Note: during writing, constant pool |
460 | * might grow since some parts of constants still need to be entered. |
461 | */ |
462 | void writePool(Pool pool) throws PoolOverflow, StringOverflow { |
463 | int poolCountIdx = poolbuf.length; |
464 | poolbuf.appendChar(0); |
465 | int i = 1; |
466 | while (i < pool.pp) { |
467 | Object value = pool.pool[i]; |
468 | assert value != null; |
469 | if (value instanceof Pool.Method) |
470 | value = ((Pool.Method)value).m; |
471 | else if (value instanceof Pool.Variable) |
472 | value = ((Pool.Variable)value).v; |
473 | |
474 | if (value instanceof MethodSymbol) { |
475 | MethodSymbol m = (MethodSymbol)value; |
476 | poolbuf.appendByte((m.owner.flags() & INTERFACE) != 0 |
477 | ? CONSTANT_InterfaceMethodref |
478 | : CONSTANT_Methodref); |
479 | poolbuf.appendChar(pool.put(m.owner)); |
480 | poolbuf.appendChar(pool.put(nameType(m))); |
481 | } else if (value instanceof VarSymbol) { |
482 | VarSymbol v = (VarSymbol)value; |
483 | poolbuf.appendByte(CONSTANT_Fieldref); |
484 | poolbuf.appendChar(pool.put(v.owner)); |
485 | poolbuf.appendChar(pool.put(nameType(v))); |
486 | } else if (value instanceof Name) { |
487 | poolbuf.appendByte(CONSTANT_Utf8); |
488 | byte[] bs = ((Name)value).toUtf(); |
489 | poolbuf.appendChar(bs.length); |
490 | poolbuf.appendBytes(bs, 0, bs.length); |
491 | if (bs.length > Pool.MAX_STRING_LENGTH) |
492 | throw new StringOverflow(value.toString()); |
493 | } else if (value instanceof ClassSymbol) { |
494 | ClassSymbol c = (ClassSymbol)value; |
495 | if (c.owner.kind == TYP) pool.put(c.owner); |
496 | poolbuf.appendByte(CONSTANT_Class); |
497 | if (c.type.tag == ARRAY) { |
498 | poolbuf.appendChar(pool.put(typeSig(c.type))); |
499 | } else { |
500 | poolbuf.appendChar(pool.put(names.fromUtf(externalize(c.flatname)))); |
501 | enterInner(c); |
502 | } |
503 | } else if (value instanceof NameAndType) { |
504 | NameAndType nt = (NameAndType)value; |
505 | poolbuf.appendByte(CONSTANT_NameandType); |
506 | poolbuf.appendChar(pool.put(nt.name)); |
507 | poolbuf.appendChar(pool.put(typeSig(nt.type))); |
508 | } else if (value instanceof Integer) { |
509 | poolbuf.appendByte(CONSTANT_Integer); |
510 | poolbuf.appendInt(((Integer)value).intValue()); |
511 | } else if (value instanceof Long) { |
512 | poolbuf.appendByte(CONSTANT_Long); |
513 | poolbuf.appendLong(((Long)value).longValue()); |
514 | i++; |
515 | } else if (value instanceof Float) { |
516 | poolbuf.appendByte(CONSTANT_Float); |
517 | poolbuf.appendFloat(((Float)value).floatValue()); |
518 | } else if (value instanceof Double) { |
519 | poolbuf.appendByte(CONSTANT_Double); |
520 | poolbuf.appendDouble(((Double)value).doubleValue()); |
521 | i++; |
522 | } else if (value instanceof String) { |
523 | poolbuf.appendByte(CONSTANT_String); |
524 | poolbuf.appendChar(pool.put(names.fromString((String)value))); |
525 | } else if (value instanceof Type) { |
526 | Type type = (Type)value; |
527 | if (type.tag == CLASS) enterInner((ClassSymbol)type.tsym); |
528 | poolbuf.appendByte(CONSTANT_Class); |
529 | poolbuf.appendChar(pool.put(xClassName(type))); |
530 | } else { |
531 | assert false : "writePool " + value; |
532 | } |
533 | i++; |
534 | } |
535 | if (pool.pp > Pool.MAX_ENTRIES) |
536 | throw new PoolOverflow(); |
537 | putChar(poolbuf, poolCountIdx, pool.pp); |
538 | } |
539 | |
540 | /** Given a field, return its name. |
541 | */ |
542 | Name fieldName(Symbol sym) { |
543 | if (scramble && (sym.flags() & PRIVATE) != 0 || |
544 | scrambleAll && (sym.flags() & (PROTECTED | PUBLIC)) == 0) |
545 | return names.fromString("_$" + sym.name.index); |
546 | else |
547 | return sym.name; |
548 | } |
549 | |
550 | /** Given a symbol, return its name-and-type. |
551 | */ |
552 | NameAndType nameType(Symbol sym) { |
553 | return new NameAndType(fieldName(sym), |
554 | retrofit |
555 | ? sym.erasure(types) |
556 | : sym.externalType(types)); |
557 | // if we retrofit, then the NameAndType has been read in as is |
558 | // and no change is necessary. If we compile normally, the |
559 | // NameAndType is generated from a symbol reference, and the |
560 | // adjustment of adding an additional this$n parameter needs to be made. |
561 | } |
562 | |
563 | /****************************************************************** |
564 | * Writing Attributes |
565 | ******************************************************************/ |
566 | |
567 | /** Write header for an attribute to data buffer and return |
568 | * position past attribute length index. |
569 | */ |
570 | int writeAttr(Name attrName) { |
571 | databuf.appendChar(pool.put(attrName)); |
572 | databuf.appendInt(0); |
573 | return databuf.length; |
574 | } |
575 | |
576 | /** Fill in attribute length. |
577 | */ |
578 | void endAttr(int index) { |
579 | putInt(databuf, index - 4, databuf.length - index); |
580 | } |
581 | |
582 | /** Leave space for attribute count and return index for |
583 | * number of attributes field. |
584 | */ |
585 | int beginAttrs() { |
586 | databuf.appendChar(0); |
587 | return databuf.length; |
588 | } |
589 | |
590 | /** Fill in number of attributes. |
591 | */ |
592 | void endAttrs(int index, int count) { |
593 | putChar(databuf, index - 2, count); |
594 | } |
595 | |
596 | /** Write the EnclosingMethod attribute if needed. |
597 | * Returns the number of attributes written (0 or 1). |
598 | */ |
599 | int writeEnclosingMethodAttribute(ClassSymbol c) { |
600 | if (!target.hasEnclosingMethodAttribute() || |
601 | c.owner.kind != MTH && // neither a local class |
602 | c.name != names.empty) // nor anonymous |
603 | return 0; |
604 | |
605 | int alenIdx = writeAttr(names.EnclosingMethod); |
606 | ClassSymbol enclClass = c.owner.enclClass(); |
607 | MethodSymbol enclMethod = |
608 | (c.owner.type == null // local to init block |
609 | || c.owner.kind != MTH) // or member init |
610 | ? null |
611 | : (MethodSymbol)c.owner; |
612 | databuf.appendChar(pool.put(enclClass)); |
613 | databuf.appendChar(enclMethod == null ? 0 : pool.put(nameType(c.owner))); |
614 | endAttr(alenIdx); |
615 | return 1; |
616 | } |
617 | |
618 | /** Write flag attributes; return number of attributes written. |
619 | */ |
620 | int writeFlagAttrs(long flags) { |
621 | int acount = 0; |
622 | if ((flags & DEPRECATED) != 0) { |
623 | int alenIdx = writeAttr(names.Deprecated); |
624 | endAttr(alenIdx); |
625 | acount++; |
626 | } |
627 | if ((flags & ENUM) != 0 && !target.useEnumFlag()) { |
628 | int alenIdx = writeAttr(names.Enum); |
629 | endAttr(alenIdx); |
630 | acount++; |
631 | } |
632 | if ((flags & SYNTHETIC) != 0 && !target.useSyntheticFlag()) { |
633 | int alenIdx = writeAttr(names.Synthetic); |
634 | endAttr(alenIdx); |
635 | acount++; |
636 | } |
637 | if ((flags & BRIDGE) != 0 && !target.useBridgeFlag()) { |
638 | int alenIdx = writeAttr(names.Bridge); |
639 | endAttr(alenIdx); |
640 | acount++; |
641 | } |
642 | if ((flags & VARARGS) != 0 && !target.useVarargsFlag()) { |
643 | int alenIdx = writeAttr(names.Varargs); |
644 | endAttr(alenIdx); |
645 | acount++; |
646 | } |
647 | if ((flags & ANNOTATION) != 0 && !target.useAnnotationFlag()) { |
648 | int alenIdx = writeAttr(names.Annotation); |
649 | endAttr(alenIdx); |
650 | acount++; |
651 | } |
652 | return acount; |
653 | } |
654 | |
655 | /** Write member (field or method) attributes; |
656 | * return number of attributes written. |
657 | */ |
658 | int writeMemberAttrs(Symbol sym) { |
659 | int acount = writeFlagAttrs(sym.flags()); |
660 | long flags = sym.flags(); |
661 | if (source.allowGenerics() && |
662 | (flags & (SYNTHETIC|BRIDGE)) != SYNTHETIC && |
663 | (flags & ANONCONSTR) == 0 && |
664 | (!types.isSameType(sym.type, sym.erasure(types)) || |
665 | hasTypeVar(sym.type.getThrownTypes()))) { |
666 | // note that a local class with captured variables |
667 | // will get a signature attribute |
668 | int alenIdx = writeAttr(names.Signature); |
669 | databuf.appendChar(pool.put(typeSig(sym.type))); |
670 | endAttr(alenIdx); |
671 | acount++; |
672 | } |
673 | acount += writeJavaAnnotations(sym.getAnnotationMirrors()); |
674 | return acount; |
675 | } |
676 | |
677 | /** Write method parameter annotations; |
678 | * return number of attributes written. |
679 | */ |
680 | int writeParameterAttrs(MethodSymbol m) { |
681 | boolean hasVisible = false; |
682 | boolean hasInvisible = false; |
683 | if (m.params != null) for (VarSymbol s : m.params) { |
684 | for (Attribute.Compound a : s.getAnnotationMirrors()) { |
685 | switch (getRetention(a.type.tsym)) { |
686 | case SOURCE: break; |
687 | case CLASS: hasInvisible = true; break; |
688 | case RUNTIME: hasVisible = true; break; |
689 | default: ;// /* fail soft */ throw new AssertionError(vis); |
690 | } |
691 | } |
692 | } |
693 | |
694 | int attrCount = 0; |
695 | if (hasVisible) { |
696 | int attrIndex = writeAttr(names.RuntimeVisibleParameterAnnotations); |
697 | databuf.appendByte(m.params.length()); |
698 | for (VarSymbol s : m.params) { |
699 | ListBuffer<Attribute.Compound> buf = new ListBuffer<Attribute.Compound>(); |
700 | for (Attribute.Compound a : s.getAnnotationMirrors()) |
701 | if (getRetention(a.type.tsym) == RetentionPolicy.RUNTIME) |
702 | buf.append(a); |
703 | databuf.appendChar(buf.length()); |
704 | for (Attribute.Compound a : buf) |
705 | writeCompoundAttribute(a); |
706 | } |
707 | endAttr(attrIndex); |
708 | attrCount++; |
709 | } |
710 | if (hasInvisible) { |
711 | int attrIndex = writeAttr(names.RuntimeInvisibleParameterAnnotations); |
712 | databuf.appendByte(m.params.length()); |
713 | for (VarSymbol s : m.params) { |
714 | ListBuffer<Attribute.Compound> buf = new ListBuffer<Attribute.Compound>(); |
715 | for (Attribute.Compound a : s.getAnnotationMirrors()) |
716 | if (getRetention(a.type.tsym) == RetentionPolicy.CLASS) |
717 | buf.append(a); |
718 | databuf.appendChar(buf.length()); |
719 | for (Attribute.Compound a : buf) |
720 | writeCompoundAttribute(a); |
721 | } |
722 | endAttr(attrIndex); |
723 | attrCount++; |
724 | } |
725 | return attrCount; |
726 | } |
727 | |
728 | /********************************************************************** |
729 | * Writing Java-language annotations (aka metadata, attributes) |
730 | **********************************************************************/ |
731 | |
732 | /** Write Java-language annotations; return number of JVM |
733 | * attributes written (zero or one). |
734 | */ |
735 | int writeJavaAnnotations(List<Attribute.Compound> attrs) { |
736 | if (attrs.isEmpty()) return 0; |
737 | ListBuffer<Attribute.Compound> visibles = new ListBuffer<Attribute.Compound>(); |
738 | ListBuffer<Attribute.Compound> invisibles = new ListBuffer<Attribute.Compound>(); |
739 | for (Attribute.Compound a : attrs) { |
740 | switch (getRetention(a.type.tsym)) { |
741 | case SOURCE: break; |
742 | case CLASS: invisibles.append(a); break; |
743 | case RUNTIME: visibles.append(a); break; |
744 | default: ;// /* fail soft */ throw new AssertionError(vis); |
745 | } |
746 | } |
747 | |
748 | int attrCount = 0; |
749 | if (visibles.length() != 0) { |
750 | int attrIndex = writeAttr(names.RuntimeVisibleAnnotations); |
751 | databuf.appendChar(visibles.length()); |
752 | for (Attribute.Compound a : visibles) |
753 | writeCompoundAttribute(a); |
754 | endAttr(attrIndex); |
755 | attrCount++; |
756 | } |
757 | if (invisibles.length() != 0) { |
758 | int attrIndex = writeAttr(names.RuntimeInvisibleAnnotations); |
759 | databuf.appendChar(invisibles.length()); |
760 | for (Attribute.Compound a : invisibles) |
761 | writeCompoundAttribute(a); |
762 | endAttr(attrIndex); |
763 | attrCount++; |
764 | } |
765 | return attrCount; |
766 | } |
767 | |
768 | /** A mirror of java.lang.annotation.RetentionPolicy. */ |
769 | enum RetentionPolicy { |
770 | SOURCE, |
771 | CLASS, |
772 | RUNTIME |
773 | } |
774 | |
775 | RetentionPolicy getRetention(TypeSymbol annotationType) { |
776 | RetentionPolicy vis = RetentionPolicy.CLASS; // the default |
777 | Attribute.Compound c = annotationType.attribute(syms.retentionType.tsym); |
778 | if (c != null) { |
779 | Attribute value = c.member(names.value); |
780 | if (value != null && value instanceof Attribute.Enum) { |
781 | Name levelName = ((Attribute.Enum)value).value.name; |
782 | if (levelName == names.SOURCE) vis = RetentionPolicy.SOURCE; |
783 | else if (levelName == names.CLASS) vis = RetentionPolicy.CLASS; |
784 | else if (levelName == names.RUNTIME) vis = RetentionPolicy.RUNTIME; |
785 | else ;// /* fail soft */ throw new AssertionError(levelName); |
786 | } |
787 | } |
788 | return vis; |
789 | } |
790 | |
791 | /** A visitor to write an attribute including its leading |
792 | * single-character marker. |
793 | */ |
794 | class AttributeWriter implements Attribute.Visitor { |
795 | public void visitConstant(Attribute.Constant _value) { |
796 | Object value = _value.value; |
797 | switch (_value.type.tag) { |
798 | case BYTE: |
799 | databuf.appendByte('B'); |
800 | break; |
801 | case CHAR: |
802 | databuf.appendByte('C'); |
803 | break; |
804 | case SHORT: |
805 | databuf.appendByte('S'); |
806 | break; |
807 | case INT: |
808 | databuf.appendByte('I'); |
809 | break; |
810 | case LONG: |
811 | databuf.appendByte('J'); |
812 | break; |
813 | case FLOAT: |
814 | databuf.appendByte('F'); |
815 | break; |
816 | case DOUBLE: |
817 | databuf.appendByte('D'); |
818 | break; |
819 | case BOOLEAN: |
820 | databuf.appendByte('Z'); |
821 | break; |
822 | case CLASS: |
823 | assert value instanceof String; |
824 | databuf.appendByte('s'); |
825 | value = names.fromString(value.toString()); // CONSTANT_Utf8 |
826 | break; |
827 | default: |
828 | throw new AssertionError(_value.type); |
829 | } |
830 | databuf.appendChar(pool.put(value)); |
831 | } |
832 | public void visitEnum(Attribute.Enum e) { |
833 | databuf.appendByte('e'); |
834 | databuf.appendChar(pool.put(typeSig(e.value.type))); |
835 | databuf.appendChar(pool.put(e.value.name)); |
836 | } |
837 | public void visitClass(Attribute.Class clazz) { |
838 | databuf.appendByte('c'); |
839 | databuf.appendChar(pool.put(typeSig(clazz.type))); |
840 | } |
841 | public void visitCompound(Attribute.Compound compound) { |
842 | databuf.appendByte('@'); |
843 | writeCompoundAttribute(compound); |
844 | } |
845 | public void visitError(Attribute.Error x) { |
846 | throw new AssertionError(x); |
847 | } |
848 | public void visitArray(Attribute.Array array) { |
849 | databuf.appendByte('['); |
850 | databuf.appendChar(array.values.length); |
851 | for (Attribute a : array.values) { |
852 | a.accept(this); |
853 | } |
854 | } |
855 | } |
856 | AttributeWriter awriter = new AttributeWriter(); |
857 | |
858 | /** Write a compound attribute excluding the '@' marker. */ |
859 | void writeCompoundAttribute(Attribute.Compound c) { |
860 | databuf.appendChar(pool.put(typeSig(c.type))); |
861 | databuf.appendChar(c.values.length()); |
862 | for (Pair<Symbol.MethodSymbol,Attribute> p : c.values) { |
863 | databuf.appendChar(pool.put(p.fst.name)); |
864 | p.snd.accept(awriter); |
865 | } |
866 | } |
867 | |
868 | /********************************************************************** |
869 | * Writing Objects |
870 | **********************************************************************/ |
871 | |
872 | /** Enter an inner class into the `innerClasses' set/queue. |
873 | */ |
874 | void enterInner(ClassSymbol c) { |
875 | assert !c.type.isCompound(); |
876 | try { |
877 | c.complete(); |
878 | } catch (CompletionFailure ex) { |
879 | System.err.println("error: " + c + ": " + ex.getMessage()); |
880 | throw ex; |
881 | } |
882 | if (c.type.tag != CLASS) return; // arrays |
883 | if (pool != null && // pool might be null if called from xClassName |
884 | c.owner.kind != PCK && |
885 | (innerClasses == null || !innerClasses.contains(c))) { |
886 | // log.errWriter.println("enter inner " + c);//DEBUG |
887 | if (c.owner.kind == TYP) enterInner((ClassSymbol)c.owner); |
888 | pool.put(c); |
889 | pool.put(c.name); |
890 | if (innerClasses == null) { |
891 | innerClasses = new HashSet<ClassSymbol>(); |
892 | innerClassesQueue = new ListBuffer<ClassSymbol>(); |
893 | pool.put(names.InnerClasses); |
894 | } |
895 | innerClasses.add(c); |
896 | innerClassesQueue.append(c); |
897 | } |
898 | } |
899 | |
900 | /** Write "inner classes" attribute. |
901 | */ |
902 | void writeInnerClasses() { |
903 | int alenIdx = writeAttr(names.InnerClasses); |
904 | databuf.appendChar(innerClassesQueue.length()); |
905 | for (List<ClassSymbol> l = innerClassesQueue.toList(); |
906 | l.nonEmpty(); |
907 | l = l.tail) { |
908 | ClassSymbol inner = l.head; |
909 | char flags = (char) adjustFlags(inner.flags_field); |
910 | if ((flags & INTERFACE) != 0) flags |= ABSTRACT; // Interfaces are always ABSTRACT |
911 | if (inner.name.isEmpty()) flags &= ~FINAL; // Anonymous class: unset FINAL flag |
912 | if (dumpInnerClassModifiers) { |
913 | log.errWriter.println("INNERCLASS " + inner.name); |
914 | log.errWriter.println("---" + flagNames(flags)); |
915 | } |
916 | databuf.appendChar(pool.get(inner)); |
917 | databuf.appendChar( |
918 | inner.owner.kind == TYP ? pool.get(inner.owner) : 0); |
919 | databuf.appendChar( |
920 | inner.name.len != 0 ? pool.get(inner.name) : 0); |
921 | databuf.appendChar(flags); |
922 | } |
923 | endAttr(alenIdx); |
924 | } |
925 | |
926 | /** Write field symbol, entering all references into constant pool. |
927 | */ |
928 | void writeField(VarSymbol v) { |
929 | int flags = adjustFlags(v.flags()); |
930 | databuf.appendChar(flags); |
931 | if (dumpFieldModifiers) { |
932 | log.errWriter.println("FIELD " + fieldName(v)); |
933 | log.errWriter.println("---" + flagNames(v.flags())); |
934 | } |
935 | databuf.appendChar(pool.put(fieldName(v))); |
936 | databuf.appendChar(pool.put(typeSig(v.erasure(types)))); |
937 | int acountIdx = beginAttrs(); |
938 | int acount = 0; |
939 | if (v.getConstValue() != null) { |
940 | int alenIdx = writeAttr(names.ConstantValue); |
941 | databuf.appendChar(pool.put(v.getConstValue())); |
942 | endAttr(alenIdx); |
943 | acount++; |
944 | } |
945 | acount += writeMemberAttrs(v); |
946 | endAttrs(acountIdx, acount); |
947 | } |
948 | |
949 | /** Write method symbol, entering all references into constant pool. |
950 | */ |
951 | void writeMethod(MethodSymbol m) { |
952 | int flags = adjustFlags(m.flags()); |
953 | databuf.appendChar(flags); |
954 | if (dumpMethodModifiers) { |
955 | log.errWriter.println("METHOD " + fieldName(m)); |
956 | log.errWriter.println("---" + flagNames(m.flags())); |
957 | } |
958 | databuf.appendChar(pool.put(fieldName(m))); |
959 | databuf.appendChar(pool.put(typeSig(m.externalType(types)))); |
960 | int acountIdx = beginAttrs(); |
961 | int acount = 0; |
962 | if (m.code != null) { |
963 | int alenIdx = writeAttr(names.Code); |
964 | writeCode(m.code); |
965 | m.code = null; // to conserve space |
966 | endAttr(alenIdx); |
967 | acount++; |
968 | } |
969 | List<Type> thrown = m.erasure(types).getThrownTypes(); |
970 | if (thrown.nonEmpty()) { |
971 | int alenIdx = writeAttr(names.Exceptions); |
972 | databuf.appendChar(thrown.length()); |
973 | for (List<Type> l = thrown; l.nonEmpty(); l = l.tail) |
974 | databuf.appendChar(pool.put(l.head.tsym)); |
975 | endAttr(alenIdx); |
976 | acount++; |
977 | } |
978 | if (m.defaultValue != null) { |
979 | int alenIdx = writeAttr(names.AnnotationDefault); |
980 | m.defaultValue.accept(awriter); |
981 | endAttr(alenIdx); |
982 | acount++; |
983 | } |
984 | acount += writeMemberAttrs(m); |
985 | acount += writeParameterAttrs(m); |
986 | endAttrs(acountIdx, acount); |
987 | } |
988 | |
989 | /** Write code attribute of method. |
990 | */ |
991 | void writeCode(Code code) { |
992 | databuf.appendChar(code.max_stack); |
993 | databuf.appendChar(code.max_locals); |
994 | databuf.appendInt(code.cp); |
995 | databuf.appendBytes(code.code, 0, code.cp); |
996 | databuf.appendChar(code.catchInfo.length()); |
997 | for (List<char[]> l = code.catchInfo.toList(); |
998 | l.nonEmpty(); |
999 | l = l.tail) { |
1000 | for (int i = 0; i < l.head.length; i++) |
1001 | databuf.appendChar(l.head[i]); |
1002 | } |
1003 | int acountIdx = beginAttrs(); |
1004 | int acount = 0; |
1005 | |
1006 | if (code.lineInfo.nonEmpty()) { |
1007 | int alenIdx = writeAttr(names.LineNumberTable); |
1008 | databuf.appendChar(code.lineInfo.length()); |
1009 | for (List<char[]> l = code.lineInfo.reverse(); |
1010 | l.nonEmpty(); |
1011 | l = l.tail) |
1012 | for (int i = 0; i < l.head.length; i++) |
1013 | databuf.appendChar(l.head[i]); |
1014 | endAttr(alenIdx); |
1015 | acount++; |
1016 | } |
1017 | |
1018 | if (genCrt && (code.crt != null)) { |
1019 | CRTable crt = code.crt; |
1020 | int alenIdx = writeAttr(names.CharacterRangeTable); |
1021 | int crtIdx = beginAttrs(); |
1022 | int crtEntries = crt.writeCRT(databuf, code.lineMap, log); |
1023 | endAttrs(crtIdx, crtEntries); |
1024 | endAttr(alenIdx); |
1025 | acount++; |
1026 | } |
1027 | |
1028 | // counter for number of generic local variables |
1029 | int nGenericVars = 0; |
1030 | |
1031 | if (code.varBufferSize > 0) { |
1032 | int alenIdx = writeAttr(names.LocalVariableTable); |
1033 | databuf.appendChar(code.varBufferSize); |
1034 | |
1035 | for (int i=0; i<code.varBufferSize; i++) { |
1036 | Code.LocalVar var = code.varBuffer[i]; |
1037 | |
1038 | // write variable info |
1039 | assert var.start_pc >= 0; |
1040 | assert var.start_pc <= code.cp; |
1041 | databuf.appendChar(var.start_pc); |
1042 | assert var.length >= 0; |
1043 | assert (var.start_pc + var.length) <= code.cp; |
1044 | databuf.appendChar(var.length); |
1045 | VarSymbol sym = var.sym; |
1046 | databuf.appendChar(pool.put(sym.name)); |
1047 | Type vartype = sym.erasure(types); |
1048 | if (!types.isSameType(sym.type, vartype)) |
1049 | nGenericVars++; |
1050 | databuf.appendChar(pool.put(typeSig(vartype))); |
1051 | databuf.appendChar(var.reg); |
1052 | } |
1053 | endAttr(alenIdx); |
1054 | acount++; |
1055 | } |
1056 | |
1057 | if (nGenericVars > 0) { |
1058 | int alenIdx = writeAttr(names.LocalVariableTypeTable); |
1059 | databuf.appendChar(nGenericVars); |
1060 | int count = 0; |
1061 | |
1062 | for (int i=0; i<code.varBufferSize; i++) { |
1063 | Code.LocalVar var = code.varBuffer[i]; |
1064 | VarSymbol sym = var.sym; |
1065 | if (types.isSameType(sym.type, sym.erasure(types))) |
1066 | continue; |
1067 | count++; |
1068 | // write variable info |
1069 | databuf.appendChar(var.start_pc); |
1070 | databuf.appendChar(var.length); |
1071 | databuf.appendChar(pool.put(sym.name)); |
1072 | databuf.appendChar(pool.put(typeSig(sym.type))); |
1073 | databuf.appendChar(var.reg); |
1074 | } |
1075 | assert count == nGenericVars; |
1076 | endAttr(alenIdx); |
1077 | acount++; |
1078 | } |
1079 | |
1080 | if (code.stackMapBufferSize > 0) { |
1081 | if (debugstackmap) System.out.println("Stack map for " + code.meth); |
1082 | int alenIdx = writeAttr(code.stackMap.getAttributeName(names)); |
1083 | writeStackMap(code); |
1084 | endAttr(alenIdx); |
1085 | acount++; |
1086 | } |
1087 | endAttrs(acountIdx, acount); |
1088 | } |
1089 | |
1090 | void writeStackMap(Code code) { |
1091 | int nframes = code.stackMapBufferSize; |
1092 | if (debugstackmap) System.out.println(" nframes = " + nframes); |
1093 | databuf.appendChar(nframes); |
1094 | |
1095 | switch (code.stackMap) { |
1096 | case CLDC: |
1097 | for (int i=0; i<nframes; i++) { |
1098 | if (debugstackmap) System.out.print(" " + i + ":"); |
1099 | Code.StackMapFrame frame = code.stackMapBuffer[i]; |
1100 | |
1101 | // output PC |
1102 | if (debugstackmap) System.out.print(" pc=" + frame.pc); |
1103 | databuf.appendChar(frame.pc); |
1104 | |
1105 | // output locals |
1106 | int localCount = 0; |
1107 | for (int j=0; j<frame.locals.length; |
1108 | j += (target.generateEmptyAfterBig() ? 1 : Code.width(frame.locals[j]))) { |
1109 | localCount++; |
1110 | } |
1111 | if (debugstackmap) System.out.print(" nlocals=" + |
1112 | localCount); |
1113 | databuf.appendChar(localCount); |
1114 | for (int j=0; j<frame.locals.length; |
1115 | j += (target.generateEmptyAfterBig() ? 1 : Code.width(frame.locals[j]))) { |
1116 | if (debugstackmap) System.out.print(" local[" + j + "]="); |
1117 | writeStackMapType(frame.locals[j]); |
1118 | } |
1119 | |
1120 | // output stack |
1121 | int stackCount = 0; |
1122 | for (int j=0; j<frame.stack.length; |
1123 | j += (target.generateEmptyAfterBig() ? 1 : Code.width(frame.stack[j]))) { |
1124 | stackCount++; |
1125 | } |
1126 | if (debugstackmap) System.out.print(" nstack=" + |
1127 | stackCount); |
1128 | databuf.appendChar(stackCount); |
1129 | for (int j=0; j<frame.stack.length; |
1130 | j += (target.generateEmptyAfterBig() ? 1 : Code.width(frame.stack[j]))) { |
1131 | if (debugstackmap) System.out.print(" stack[" + j + "]="); |
1132 | writeStackMapType(frame.stack[j]); |
1133 | } |
1134 | if (debugstackmap) System.out.println(); |
1135 | } |
1136 | break; |
1137 | case JSR202: { |
1138 | assert code.stackMapBuffer == null; |
1139 | for (int i=0; i<nframes; i++) { |
1140 | if (debugstackmap) System.out.print(" " + i + ":"); |
1141 | StackMapTableFrame frame = code.stackMapTableBuffer[i]; |
1142 | frame.write(this); |
1143 | if (debugstackmap) System.out.println(); |
1144 | } |
1145 | break; |
1146 | } |
1147 | default: |
1148 | throw new AssertionError("Unexpected stackmap format value"); |
1149 | } |
1150 | } |
1151 | |
1152 | //where |
1153 | void writeStackMapType(Type t) { |
1154 | if (t == null) { |
1155 | if (debugstackmap) System.out.print("empty"); |
1156 | databuf.appendByte(0); |
1157 | } |
1158 | else switch(t.tag) { |
1159 | case BYTE: |
1160 | case CHAR: |
1161 | case SHORT: |
1162 | case INT: |
1163 | case BOOLEAN: |
1164 | if (debugstackmap) System.out.print("int"); |
1165 | databuf.appendByte(1); |
1166 | break; |
1167 | case FLOAT: |
1168 | if (debugstackmap) System.out.print("float"); |
1169 | databuf.appendByte(2); |
1170 | break; |
1171 | case DOUBLE: |
1172 | if (debugstackmap) System.out.print("double"); |
1173 | databuf.appendByte(3); |
1174 | break; |
1175 | case LONG: |
1176 | if (debugstackmap) System.out.print("long"); |
1177 | databuf.appendByte(4); |
1178 | break; |
1179 | case BOT: // null |
1180 | if (debugstackmap) System.out.print("null"); |
1181 | databuf.appendByte(5); |
1182 | break; |
1183 | case CLASS: |
1184 | case ARRAY: |
1185 | if (debugstackmap) System.out.print("object(" + t + ")"); |
1186 | databuf.appendByte(7); |
1187 | databuf.appendChar(pool.put(t)); |
1188 | break; |
1189 | case TYPEVAR: |
1190 | if (debugstackmap) System.out.print("object(" + types.erasure(t).tsym + ")"); |
1191 | databuf.appendByte(7); |
1192 | databuf.appendChar(pool.put(types.erasure(t).tsym)); |
1193 | break; |
1194 | case UNINITIALIZED_THIS: |
1195 | if (debugstackmap) System.out.print("uninit_this"); |
1196 | databuf.appendByte(6); |
1197 | break; |
1198 | case UNINITIALIZED_OBJECT: |
1199 | { UninitializedType uninitType = (UninitializedType)t; |
1200 | databuf.appendByte(8); |
1201 | if (debugstackmap) System.out.print("uninit_object@" + uninitType.offset); |
1202 | databuf.appendChar(uninitType.offset); |
1203 | } |
1204 | break; |
1205 | default: |
1206 | throw new AssertionError(); |
1207 | } |
1208 | } |
1209 | |
1210 | /** An entry in the JSR202 StackMapTable */ |
1211 | abstract static class StackMapTableFrame { |
1212 | abstract int getFrameType(); |
1213 | |
1214 | void write(ClassWriter writer) { |
1215 | int frameType = getFrameType(); |
1216 | writer.databuf.appendByte(frameType); |
1217 | if (writer.debugstackmap) System.out.print(" frame_type=" + frameType); |
1218 | } |
1219 | |
1220 | static class SameFrame extends StackMapTableFrame { |
1221 | final int offsetDelta; |
1222 | SameFrame(int offsetDelta) { |
1223 | this.offsetDelta = offsetDelta; |
1224 | } |
1225 | int getFrameType() { |
1226 | return (offsetDelta < SAME_FRAME_SIZE) ? offsetDelta : SAME_FRAME_EXTENDED; |
1227 | } |
1228 | @Override |
1229 | void write(ClassWriter writer) { |
1230 | super.write(writer); |
1231 | if (getFrameType() == SAME_FRAME_EXTENDED) { |
1232 | writer.databuf.appendChar(offsetDelta); |
1233 | if (writer.debugstackmap){ |
1234 | System.out.print(" offset_delta=" + offsetDelta); |
1235 | } |
1236 | } |
1237 | } |
1238 | } |
1239 | |
1240 | static class SameLocals1StackItemFrame extends StackMapTableFrame { |
1241 | final int offsetDelta; |
1242 | final Type stack; |
1243 | SameLocals1StackItemFrame(int offsetDelta, Type stack) { |
1244 | this.offsetDelta = offsetDelta; |
1245 | this.stack = stack; |
1246 | } |
1247 | int getFrameType() { |
1248 | return (offsetDelta < SAME_FRAME_SIZE) ? |
1249 | (SAME_FRAME_SIZE + offsetDelta) : |
1250 | SAME_LOCALS_1_STACK_ITEM_EXTENDED; |
1251 | } |
1252 | @Override |
1253 | void write(ClassWriter writer) { |
1254 | super.write(writer); |
1255 | if (getFrameType() == SAME_LOCALS_1_STACK_ITEM_EXTENDED) { |
1256 | writer.databuf.appendChar(offsetDelta); |
1257 | if (writer.debugstackmap) { |
1258 | System.out.print(" offset_delta=" + offsetDelta); |
1259 | } |
1260 | } |
1261 | if (writer.debugstackmap) { |
1262 | System.out.print(" stack[" + 0 + "]="); |
1263 | } |
1264 | writer.writeStackMapType(stack); |
1265 | } |
1266 | } |
1267 | |
1268 | static class ChopFrame extends StackMapTableFrame { |
1269 | final int frameType; |
1270 | final int offsetDelta; |
1271 | ChopFrame(int frameType, int offsetDelta) { |
1272 | this.frameType = frameType; |
1273 | this.offsetDelta = offsetDelta; |
1274 | } |
1275 | int getFrameType() { return frameType; } |
1276 | @Override |
1277 | void write(ClassWriter writer) { |
1278 | super.write(writer); |
1279 | writer.databuf.appendChar(offsetDelta); |
1280 | if (writer.debugstackmap) { |
1281 | System.out.print(" offset_delta=" + offsetDelta); |
1282 | } |
1283 | } |
1284 | } |
1285 | |
1286 | static class AppendFrame extends StackMapTableFrame { |
1287 | final int frameType; |
1288 | final int offsetDelta; |
1289 | final Type[] locals; |
1290 | AppendFrame(int frameType, int offsetDelta, Type[] locals) { |
1291 | this.frameType = frameType; |
1292 | this.offsetDelta = offsetDelta; |
1293 | this.locals = locals; |
1294 | } |
1295 | int getFrameType() { return frameType; } |
1296 | @Override |
1297 | void write(ClassWriter writer) { |
1298 | super.write(writer); |
1299 | writer.databuf.appendChar(offsetDelta); |
1300 | if (writer.debugstackmap) { |
1301 | System.out.print(" offset_delta=" + offsetDelta); |
1302 | } |
1303 | for (int i=0; i<locals.length; i++) { |
1304 | if (writer.debugstackmap) System.out.print(" locals[" + i + "]="); |
1305 | writer.writeStackMapType(locals[i]); |
1306 | } |
1307 | } |
1308 | } |
1309 | |
1310 | static class FullFrame extends StackMapTableFrame { |
1311 | final int offsetDelta; |
1312 | final Type[] locals; |
1313 | final Type[] stack; |
1314 | FullFrame(int offsetDelta, Type[] locals, Type[] stack) { |
1315 | this.offsetDelta = offsetDelta; |
1316 | this.locals = locals; |
1317 | this.stack = stack; |
1318 | } |
1319 | int getFrameType() { return FULL_FRAME; } |
1320 | @Override |
1321 | void write(ClassWriter writer) { |
1322 | super.write(writer); |
1323 | writer.databuf.appendChar(offsetDelta); |
1324 | writer.databuf.appendChar(locals.length); |
1325 | if (writer.debugstackmap) { |
1326 | System.out.print(" offset_delta=" + offsetDelta); |
1327 | System.out.print(" nlocals=" + locals.length); |
1328 | } |
1329 | for (int i=0; i<locals.length; i++) { |
1330 | if (writer.debugstackmap) System.out.print(" locals[" + i + "]="); |
1331 | writer.writeStackMapType(locals[i]); |
1332 | } |
1333 | |
1334 | writer.databuf.appendChar(stack.length); |
1335 | if (writer.debugstackmap) { System.out.print(" nstack=" + stack.length); } |
1336 | for (int i=0; i<stack.length; i++) { |
1337 | if (writer.debugstackmap) System.out.print(" stack[" + i + "]="); |
1338 | writer.writeStackMapType(stack[i]); |
1339 | } |
1340 | } |
1341 | } |
1342 | |
1343 | /** Compare this frame with the previous frame and produce |
1344 | * an entry of compressed stack map frame. */ |
1345 | static StackMapTableFrame getInstance(Code.StackMapFrame this_frame, |
1346 | int prev_pc, |
1347 | Type[] prev_locals, |
1348 | Types types) { |
1349 | Type[] locals = this_frame.locals; |
1350 | Type[] stack = this_frame.stack; |
1351 | int offset_delta = this_frame.pc - prev_pc - 1; |
1352 | if (stack.length == 1) { |
1353 | if (locals.length == prev_locals.length |
1354 | && compare(prev_locals, locals, types) == 0) { |
1355 | return new SameLocals1StackItemFrame(offset_delta, stack[0]); |
1356 | } |
1357 | } else if (stack.length == 0) { |
1358 | int diff_length = compare(prev_locals, locals, types); |
1359 | if (diff_length == 0) { |
1360 | return new SameFrame(offset_delta); |
1361 | } else if (-MAX_LOCAL_LENGTH_DIFF < diff_length && diff_length < 0) { |
1362 | // APPEND |
1363 | Type[] local_diff = new Type[-diff_length]; |
1364 | for (int i=prev_locals.length, j=0; i<locals.length; i++,j++) { |
1365 | local_diff[j] = locals[i]; |
1366 | } |
1367 | return new AppendFrame(SAME_FRAME_EXTENDED - diff_length, |
1368 | offset_delta, |
1369 | local_diff); |
1370 | } else if (0 < diff_length && diff_length < MAX_LOCAL_LENGTH_DIFF) { |
1371 | // CHOP |
1372 | return new ChopFrame(SAME_FRAME_EXTENDED - diff_length, |
1373 | offset_delta); |
1374 | } |
1375 | } |
1376 | // FULL_FRAME |
1377 | return new FullFrame(offset_delta, locals, stack); |
1378 | } |
1379 | |
1380 | static boolean isInt(Type t) { |
1381 | return (t.tag < TypeTags.INT || t.tag == TypeTags.BOOLEAN); |
1382 | } |
1383 | |
1384 | static boolean isSameType(Type t1, Type t2, Types types) { |
1385 | if (t1 == null) { return t2 == null; } |
1386 | if (t2 == null) { return false; } |
1387 | |
1388 | if (isInt(t1) && isInt(t2)) { return true; } |
1389 | |
1390 | if (t1.tag == UNINITIALIZED_THIS) { |
1391 | return t2.tag == UNINITIALIZED_THIS; |
1392 | } else if (t1.tag == UNINITIALIZED_OBJECT) { |
1393 | if (t2.tag == UNINITIALIZED_OBJECT) { |
1394 | return ((UninitializedType)t1).offset == ((UninitializedType)t2).offset; |
1395 | } else { |
1396 | return false; |
1397 | } |
1398 | } else if (t2.tag == UNINITIALIZED_THIS || t2.tag == UNINITIALIZED_OBJECT) { |
1399 | return false; |
1400 | } |
1401 | |
1402 | return types.isSameType(t1, t2); |
1403 | } |
1404 | |
1405 | static int compare(Type[] arr1, Type[] arr2, Types types) { |
1406 | int diff_length = arr1.length - arr2.length; |
1407 | if (diff_length > MAX_LOCAL_LENGTH_DIFF || diff_length < -MAX_LOCAL_LENGTH_DIFF) { |
1408 | return Integer.MAX_VALUE; |
1409 | } |
1410 | int len = (diff_length > 0) ? arr2.length : arr1.length; |
1411 | for (int i=0; i<len; i++) { |
1412 | if (!isSameType(arr1[i], arr2[i], types)) { |
1413 | return Integer.MAX_VALUE; |
1414 | } |
1415 | } |
1416 | return diff_length; |
1417 | } |
1418 | } |
1419 | |
1420 | void writeFields(Scope.Entry e) { |
1421 | // process them in reverse sibling order; |
1422 | // i.e., process them in declaration order. |
1423 | List<VarSymbol> vars = List.nil(); |
1424 | for (Scope.Entry i = e; i != null; i = i.sibling) { |
1425 | if (i.sym.kind == VAR) vars = vars.prepend((VarSymbol)i.sym); |
1426 | } |
1427 | while (vars.nonEmpty()) { |
1428 | writeField(vars.head); |
1429 | vars = vars.tail; |
1430 | } |
1431 | } |
1432 | |
1433 | void writeMethods(Scope.Entry e) { |
1434 | List<MethodSymbol> methods = List.nil(); |
1435 | for (Scope.Entry i = e; i != null; i = i.sibling) { |
1436 | if (i.sym.kind == MTH && (i.sym.flags() & HYPOTHETICAL) == 0) |
1437 | methods = methods.prepend((MethodSymbol)i.sym); |
1438 | } |
1439 | while (methods.nonEmpty()) { |
1440 | writeMethod(methods.head); |
1441 | methods = methods.tail; |
1442 | } |
1443 | } |
1444 | |
1445 | /** Emit a class file for a given class. |
1446 | * @param c The class from which a class file is generated. |
1447 | */ |
1448 | public JavaFileObject writeClass(ClassSymbol c) |
1449 | throws IOException, PoolOverflow, StringOverflow |
1450 | { |
1451 | JavaFileObject outFile |
1452 | = fileManager.getJavaFileForOutput(CLASS_OUTPUT, |
1453 | c.flatname.toString(), |
1454 | JavaFileObject.Kind.CLASS, |
1455 | c.sourcefile); |
1456 | OutputStream out = outFile.openOutputStream(); |
1457 | try { |
1458 | writeClassFile(out, c); |
1459 | if (verbose) |
1460 | log.errWriter.println(log.getLocalizedString("verbose.wrote.file", outFile)); |
1461 | out.close(); |
1462 | out = null; |
1463 | } finally { |
1464 | if (out != null) { |
1465 | // if we are propogating an exception, delete the file |
1466 | out.close(); |
1467 | outFile.delete(); |
1468 | outFile = null; |
1469 | } |
1470 | } |
1471 | return outFile; // may be null if write failed |
1472 | } |
1473 | |
1474 | /** Write class `c' to outstream `out'. |
1475 | */ |
1476 | public void writeClassFile(OutputStream out, ClassSymbol c) |
1477 | throws IOException, PoolOverflow, StringOverflow { |
1478 | assert (c.flags() & COMPOUND) == 0; |
1479 | databuf.reset(); |
1480 | poolbuf.reset(); |
1481 | sigbuf.reset(); |
1482 | pool = c.pool; |
1483 | innerClasses = null; |
1484 | innerClassesQueue = null; |
1485 | |
1486 | Type supertype = types.supertype(c.type); |
1487 | List<Type> interfaces = types.interfaces(c.type); |
1488 | List<Type> typarams = c.type.getTypeArguments(); |
1489 | |
1490 | int flags = adjustFlags(c.flags()); |
1491 | if ((flags & PROTECTED) != 0) flags |= PUBLIC; |
1492 | flags = flags & ClassFlags & ~STRICTFP; |
1493 | if ((flags & INTERFACE) == 0) flags |= ACC_SUPER; |
1494 | if (c.isInner() && c.name.isEmpty()) flags &= ~FINAL; |
1495 | if (dumpClassModifiers) { |
1496 | log.errWriter.println(); |
1497 | log.errWriter.println("CLASSFILE " + c.getQualifiedName()); |
1498 | log.errWriter.println("---" + flagNames(flags)); |
1499 | } |
1500 | databuf.appendChar(flags); |
1501 | |
1502 | databuf.appendChar(pool.put(c)); |
1503 | databuf.appendChar(supertype.tag == CLASS ? pool.put(supertype.tsym) : 0); |
1504 | databuf.appendChar(interfaces.length()); |
1505 | for (List<Type> l = interfaces; l.nonEmpty(); l = l.tail) |
1506 | databuf.appendChar(pool.put(l.head.tsym)); |
1507 | int fieldsCount = 0; |
1508 | int methodsCount = 0; |
1509 | for (Scope.Entry e = c.members().elems; e != null; e = e.sibling) { |
1510 | switch (e.sym.kind) { |
1511 | case VAR: fieldsCount++; break; |
1512 | case MTH: if ((e.sym.flags() & HYPOTHETICAL) == 0) methodsCount++; |
1513 | break; |
1514 | case TYP: enterInner((ClassSymbol)e.sym); break; |
1515 | default : assert false; |
1516 | } |
1517 | } |
1518 | databuf.appendChar(fieldsCount); |
1519 | writeFields(c.members().elems); |
1520 | databuf.appendChar(methodsCount); |
1521 | writeMethods(c.members().elems); |
1522 | |
1523 | int acountIdx = beginAttrs(); |
1524 | int acount = 0; |
1525 | |
1526 | boolean sigReq = |
1527 | typarams.length() != 0 || supertype.getTypeArguments().length() != 0; |
1528 | for (List<Type> l = interfaces; !sigReq && l.nonEmpty(); l = l.tail) |
1529 | sigReq = l.head.getTypeArguments().length() != 0; |
1530 | if (sigReq) { |
1531 | assert source.allowGenerics(); |
1532 | int alenIdx = writeAttr(names.Signature); |
1533 | if (typarams.length() != 0) assembleParamsSig(typarams); |
1534 | assembleSig(supertype); |
1535 | for (List<Type> l = interfaces; l.nonEmpty(); l = l.tail) |
1536 | assembleSig(l.head); |
1537 | databuf.appendChar(pool.put(sigbuf.toName(names))); |
1538 | sigbuf.reset(); |
1539 | endAttr(alenIdx); |
1540 | acount++; |
1541 | } |
1542 | |
1543 | if (c.sourcefile != null && emitSourceFile) { |
1544 | int alenIdx = writeAttr(names.SourceFile); |
1545 | // WHM 6/29/1999: Strip file path prefix. We do it here at |
1546 | // the last possible moment because the sourcefile may be used |
1547 | // elsewhere in error diagnostics. Fixes 4241573. |
1548 | //databuf.appendChar(c.pool.put(c.sourcefile)); |
1549 | String filename = c.sourcefile.toString(); |
1550 | int sepIdx = filename.lastIndexOf(File.separatorChar); |
1551 | // Allow '/' as separator on all platforms, e.g., on Win32. |
1552 | int slashIdx = filename.lastIndexOf('/'); |
1553 | if (slashIdx > sepIdx) sepIdx = slashIdx; |
1554 | if (sepIdx >= 0) filename = filename.substring(sepIdx + 1); |
1555 | databuf.appendChar(c.pool.put(names.fromString(filename))); |
1556 | endAttr(alenIdx); |
1557 | acount++; |
1558 | } |
1559 | |
1560 | if (genCrt) { |
1561 | // Append SourceID attribute |
1562 | int alenIdx = writeAttr(names.SourceID); |
1563 | databuf.appendChar(c.pool.put(names.fromString(Long.toString(getLastModified(c.sourcefile))))); |
1564 | endAttr(alenIdx); |
1565 | acount++; |
1566 | // Append CompilationID attribute |
1567 | alenIdx = writeAttr(names.CompilationID); |
1568 | databuf.appendChar(c.pool.put(names.fromString(Long.toString(System.currentTimeMillis())))); |
1569 | endAttr(alenIdx); |
1570 | acount++; |
1571 | } |
1572 | |
1573 | acount += writeFlagAttrs(c.flags()); |
1574 | acount += writeJavaAnnotations(c.getAnnotationMirrors()); |
1575 | acount += writeEnclosingMethodAttribute(c); |
1576 | |
1577 | poolbuf.appendInt(JAVA_MAGIC); |
1578 | poolbuf.appendChar(target.minorVersion); |
1579 | poolbuf.appendChar(target.majorVersion); |
1580 | |
1581 | writePool(c.pool); |
1582 | |
1583 | if (innerClasses != null) { |
1584 | writeInnerClasses(); |
1585 | acount++; |
1586 | } |
1587 | endAttrs(acountIdx, acount); |
1588 | |
1589 | poolbuf.appendBytes(databuf.elems, 0, databuf.length); |
1590 | out.write(poolbuf.elems, 0, poolbuf.length); |
1591 | |
1592 | pool = c.pool = null; // to conserve space |
1593 | } |
1594 | |
1595 | int adjustFlags(final long flags) { |
1596 | int result = (int)flags; |
1597 | if ((flags & SYNTHETIC) != 0 && !target.useSyntheticFlag()) |
1598 | result &= ~SYNTHETIC; |
1599 | if ((flags & ENUM) != 0 && !target.useEnumFlag()) |
1600 | result &= ~ENUM; |
1601 | if ((flags & ANNOTATION) != 0 && !target.useAnnotationFlag()) |
1602 | result &= ~ANNOTATION; |
1603 | |
1604 | if ((flags & BRIDGE) != 0 && target.useBridgeFlag()) |
1605 | result |= ACC_BRIDGE; |
1606 | if ((flags & VARARGS) != 0 && target.useVarargsFlag()) |
1607 | result |= ACC_VARARGS; |
1608 | return result; |
1609 | } |
1610 | |
1611 | long getLastModified(FileObject filename) { |
1612 | long mod = 0; |
1613 | try { |
1614 | mod = filename.getLastModified(); |
1615 | } catch (SecurityException e) { |
1616 | throw new AssertionError("CRT: couldn't get source file modification date: " + e.getMessage()); |
1617 | } |
1618 | return mod; |
1619 | } |
1620 | } |