1 | /* |
2 | * Copyright 1999-2007 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 | import java.util.*; |
28 | |
29 | import com.sun.tools.javac.util.*; |
30 | import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; |
31 | import com.sun.tools.javac.util.List; |
32 | import com.sun.tools.javac.code.*; |
33 | import com.sun.tools.javac.comp.*; |
34 | import com.sun.tools.javac.tree.*; |
35 | |
36 | import com.sun.tools.javac.code.Symbol.*; |
37 | import com.sun.tools.javac.code.Type.*; |
38 | import com.sun.tools.javac.jvm.Code.*; |
39 | import com.sun.tools.javac.jvm.Items.*; |
40 | import com.sun.tools.javac.tree.JCTree.*; |
41 | |
42 | import static com.sun.tools.javac.code.Flags.*; |
43 | import static com.sun.tools.javac.code.Kinds.*; |
44 | import static com.sun.tools.javac.code.TypeTags.*; |
45 | import static com.sun.tools.javac.jvm.ByteCodes.*; |
46 | import static com.sun.tools.javac.jvm.CRTFlags.*; |
47 | |
48 | /** This pass maps flat Java (i.e. without inner classes) to bytecodes. |
49 | * |
50 | * <p><b>This is NOT part of any API supported by Sun Microsystems. If |
51 | * you write code that depends on this, you do so at your own risk. |
52 | * This code and its internal interfaces are subject to change or |
53 | * deletion without notice.</b> |
54 | */ |
55 | public class Gen extends JCTree.Visitor { |
56 | protected static final Context.Key<Gen> genKey = |
57 | new Context.Key<Gen>(); |
58 | |
59 | private final Log log; |
60 | private final Symtab syms; |
61 | private final Check chk; |
62 | private final Resolve rs; |
63 | private final TreeMaker make; |
64 | private final Name.Table names; |
65 | private final Target target; |
66 | private final Type stringBufferType; |
67 | private final Map<Type,Symbol> stringBufferAppend; |
68 | private Name accessDollar; |
69 | private final Types types; |
70 | |
71 | /** Switch: GJ mode? |
72 | */ |
73 | private final boolean allowGenerics; |
74 | |
75 | /** Set when Miranda method stubs are to be generated. */ |
76 | private final boolean generateIproxies; |
77 | |
78 | /** Format of stackmap tables to be generated. */ |
79 | private final Code.StackMapFormat stackMap; |
80 | |
81 | /** A type that serves as the expected type for all method expressions. |
82 | */ |
83 | private final Type methodType; |
84 | |
85 | public static Gen instance(Context context) { |
86 | Gen instance = context.get(genKey); |
87 | if (instance == null) |
88 | instance = new Gen(context); |
89 | return instance; |
90 | } |
91 | |
92 | protected Gen(Context context) { |
93 | context.put(genKey, this); |
94 | |
95 | names = Name.Table.instance(context); |
96 | log = Log.instance(context); |
97 | syms = Symtab.instance(context); |
98 | chk = Check.instance(context); |
99 | rs = Resolve.instance(context); |
100 | make = TreeMaker.instance(context); |
101 | target = Target.instance(context); |
102 | types = Types.instance(context); |
103 | methodType = new MethodType(null, null, null, syms.methodClass); |
104 | allowGenerics = Source.instance(context).allowGenerics(); |
105 | stringBufferType = target.useStringBuilder() |
106 | ? syms.stringBuilderType |
107 | : syms.stringBufferType; |
108 | stringBufferAppend = new HashMap<Type,Symbol>(); |
109 | accessDollar = names. |
110 | fromString("access" + target.syntheticNameChar()); |
111 | |
112 | Options options = Options.instance(context); |
113 | lineDebugInfo = |
114 | options.get("-g:") == null || |
115 | options.get("-g:lines") != null; |
116 | varDebugInfo = |
117 | options.get("-g:") == null |
118 | ? options.get("-g") != null |
119 | : options.get("-g:vars") != null; |
120 | genCrt = options.get("-Xjcov") != null; |
121 | debugCode = options.get("debugcode") != null; |
122 | |
123 | generateIproxies = |
124 | target.requiresIproxy() || |
125 | options.get("miranda") != null; |
126 | |
127 | if (target.generateStackMapTable()) { |
128 | // ignore cldc because we cannot have both stackmap formats |
129 | this.stackMap = StackMapFormat.JSR202; |
130 | } else { |
131 | if (target.generateCLDCStackmap()) { |
132 | this.stackMap = StackMapFormat.CLDC; |
133 | } else { |
134 | this.stackMap = StackMapFormat.NONE; |
135 | } |
136 | } |
137 | |
138 | // by default, avoid jsr's for simple finalizers |
139 | int setjsrlimit = 50; |
140 | String jsrlimitString = options.get("jsrlimit"); |
141 | if (jsrlimitString != null) { |
142 | try { |
143 | setjsrlimit = Integer.parseInt(jsrlimitString); |
144 | } catch (NumberFormatException ex) { |
145 | // ignore ill-formed numbers for jsrlimit |
146 | } |
147 | } |
148 | this.jsrlimit = setjsrlimit; |
149 | this.useJsrLocally = false; // reset in visitTry |
150 | } |
151 | |
152 | /** Switches |
153 | */ |
154 | private final boolean lineDebugInfo; |
155 | private final boolean varDebugInfo; |
156 | private final boolean genCrt; |
157 | private final boolean debugCode; |
158 | |
159 | /** Default limit of (approximate) size of finalizer to inline. |
160 | * Zero means always use jsr. 100 or greater means never use |
161 | * jsr. |
162 | */ |
163 | private final int jsrlimit; |
164 | |
165 | /** True if jsr is used. |
166 | */ |
167 | private boolean useJsrLocally; |
168 | |
169 | /* Constant pool, reset by genClass. |
170 | */ |
171 | private Pool pool = new Pool(); |
172 | |
173 | /** Code buffer, set by genMethod. |
174 | */ |
175 | private Code code; |
176 | |
177 | /** Items structure, set by genMethod. |
178 | */ |
179 | private Items items; |
180 | |
181 | /** Environment for symbol lookup, set by genClass |
182 | */ |
183 | private Env<AttrContext> attrEnv; |
184 | |
185 | /** The top level tree. |
186 | */ |
187 | private JCCompilationUnit toplevel; |
188 | |
189 | /** The number of code-gen errors in this class. |
190 | */ |
191 | private int nerrs = 0; |
192 | |
193 | /** A hash table mapping syntax trees to their ending source positions. |
194 | */ |
195 | private Map<JCTree, Integer> endPositions; |
196 | |
197 | /** Generate code to load an integer constant. |
198 | * @param n The integer to be loaded. |
199 | */ |
200 | void loadIntConst(int n) { |
201 | items.makeImmediateItem(syms.intType, n).load(); |
202 | } |
203 | |
204 | /** The opcode that loads a zero constant of a given type code. |
205 | * @param tc The given type code (@see ByteCode). |
206 | */ |
207 | public static int zero(int tc) { |
208 | switch(tc) { |
209 | case INTcode: case BYTEcode: case SHORTcode: case CHARcode: |
210 | return iconst_0; |
211 | case LONGcode: |
212 | return lconst_0; |
213 | case FLOATcode: |
214 | return fconst_0; |
215 | case DOUBLEcode: |
216 | return dconst_0; |
217 | default: |
218 | throw new AssertionError("zero"); |
219 | } |
220 | } |
221 | |
222 | /** The opcode that loads a one constant of a given type code. |
223 | * @param tc The given type code (@see ByteCode). |
224 | */ |
225 | public static int one(int tc) { |
226 | return zero(tc) + 1; |
227 | } |
228 | |
229 | /** Generate code to load -1 of the given type code (either int or long). |
230 | * @param tc The given type code (@see ByteCode). |
231 | */ |
232 | void emitMinusOne(int tc) { |
233 | if (tc == LONGcode) { |
234 | items.makeImmediateItem(syms.longType, new Long(-1)).load(); |
235 | } else { |
236 | code.emitop0(iconst_m1); |
237 | } |
238 | } |
239 | |
240 | /** Construct a symbol to reflect the qualifying type that should |
241 | * appear in the byte code as per JLS 13.1. |
242 | * |
243 | * For target >= 1.2: Clone a method with the qualifier as owner (except |
244 | * for those cases where we need to work around VM bugs). |
245 | * |
246 | * For target <= 1.1: If qualified variable or method is defined in a |
247 | * non-accessible class, clone it with the qualifier class as owner. |
248 | * |
249 | * @param sym The accessed symbol |
250 | * @param site The qualifier's type. |
251 | */ |
252 | Symbol binaryQualifier(Symbol sym, Type site) { |
253 | |
254 | if (site.tag == ARRAY) { |
255 | if (sym == syms.lengthVar || |
256 | sym.owner != syms.arrayClass) |
257 | return sym; |
258 | // array clone can be qualified by the array type in later targets |
259 | Symbol qualifier = target.arrayBinaryCompatibility() |
260 | ? new ClassSymbol(Flags.PUBLIC, site.tsym.name, |
261 | site, syms.noSymbol) |
262 | : syms.objectType.tsym; |
263 | return sym.clone(qualifier); |
264 | } |
265 | |
266 | if (sym.owner == site.tsym || |
267 | (sym.flags() & (STATIC | SYNTHETIC)) == (STATIC | SYNTHETIC)) { |
268 | return sym; |
269 | } |
270 | if (!target.obeyBinaryCompatibility()) |
271 | return rs.isAccessible(attrEnv, (TypeSymbol)sym.owner) |
272 | ? sym |
273 | : sym.clone(site.tsym); |
274 | |
275 | if (!target.interfaceFieldsBinaryCompatibility()) { |
276 | if ((sym.owner.flags() & INTERFACE) != 0 && sym.kind == VAR) |
277 | return sym; |
278 | } |
279 | |
280 | // leave alone methods inherited from Object |
281 | // JLS2 13.1. |
282 | if (sym.owner == syms.objectType.tsym) |
283 | return sym; |
284 | |
285 | if (!target.interfaceObjectOverridesBinaryCompatibility()) { |
286 | if ((sym.owner.flags() & INTERFACE) != 0 && |
287 | syms.objectType.tsym.members().lookup(sym.name).scope != null) |
288 | return sym; |
289 | } |
290 | |
291 | return sym.clone(site.tsym); |
292 | } |
293 | |
294 | /** Insert a reference to given type in the constant pool, |
295 | * checking for an array with too many dimensions; |
296 | * return the reference's index. |
297 | * @param type The type for which a reference is inserted. |
298 | */ |
299 | int makeRef(DiagnosticPosition pos, Type type) { |
300 | checkDimension(pos, type); |
301 | return pool.put(type.tag == CLASS ? (Object)type.tsym : (Object)type); |
302 | } |
303 | |
304 | /** Check if the given type is an array with too many dimensions. |
305 | */ |
306 | private void checkDimension(DiagnosticPosition pos, Type t) { |
307 | switch (t.tag) { |
308 | case METHOD: |
309 | checkDimension(pos, t.getReturnType()); |
310 | for (List<Type> args = t.getParameterTypes(); args.nonEmpty(); args = args.tail) |
311 | checkDimension(pos, args.head); |
312 | break; |
313 | case ARRAY: |
314 | if (types.dimensions(t) > ClassFile.MAX_DIMENSIONS) { |
315 | log.error(pos, "limit.dimensions"); |
316 | nerrs++; |
317 | } |
318 | break; |
319 | default: |
320 | break; |
321 | } |
322 | } |
323 | |
324 | /** Create a tempory variable. |
325 | * @param type The variable's type. |
326 | */ |
327 | LocalItem makeTemp(Type type) { |
328 | VarSymbol v = new VarSymbol(Flags.SYNTHETIC, |
329 | names.empty, |
330 | type, |
331 | env.enclMethod.sym); |
332 | code.newLocal(v); |
333 | return items.makeLocalItem(v); |
334 | } |
335 | |
336 | /** Generate code to call a non-private method or constructor. |
337 | * @param pos Position to be used for error reporting. |
338 | * @param site The type of which the method is a member. |
339 | * @param name The method's name. |
340 | * @param argtypes The method's argument types. |
341 | * @param isStatic A flag that indicates whether we call a |
342 | * static or instance method. |
343 | */ |
344 | void callMethod(DiagnosticPosition pos, |
345 | Type site, Name name, List<Type> argtypes, |
346 | boolean isStatic) { |
347 | Symbol msym = rs. |
348 | resolveInternalMethod(pos, attrEnv, site, name, argtypes, null); |
349 | if (isStatic) items.makeStaticItem(msym).invoke(); |
350 | else items.makeMemberItem(msym, name == names.init).invoke(); |
351 | } |
352 | |
353 | /** Is the given method definition an access method |
354 | * resulting from a qualified super? This is signified by an odd |
355 | * access code. |
356 | */ |
357 | private boolean isAccessSuper(JCMethodDecl enclMethod) { |
358 | return |
359 | (enclMethod.mods.flags & SYNTHETIC) != 0 && |
360 | isOddAccessName(enclMethod.name); |
361 | } |
362 | |
363 | /** Does given name start with "access$" and end in an odd digit? |
364 | */ |
365 | private boolean isOddAccessName(Name name) { |
366 | return |
367 | name.startsWith(accessDollar) && |
368 | (name.byteAt(name.len - 1) & 1) == 1; |
369 | } |
370 | |
371 | /* ************************************************************************ |
372 | * Non-local exits |
373 | *************************************************************************/ |
374 | |
375 | /** Generate code to invoke the finalizer associated with given |
376 | * environment. |
377 | * Any calls to finalizers are appended to the environments `cont' chain. |
378 | * Mark beginning of gap in catch all range for finalizer. |
379 | */ |
380 | void genFinalizer(Env<GenContext> env) { |
381 | if (code.isAlive() && env.info.finalize != null) |
382 | env.info.finalize.gen(); |
383 | } |
384 | |
385 | /** Generate code to call all finalizers of structures aborted by |
386 | * a non-local |
387 | * exit. Return target environment of the non-local exit. |
388 | * @param target The tree representing the structure that's aborted |
389 | * @param env The environment current at the non-local exit. |
390 | */ |
391 | Env<GenContext> unwind(JCTree target, Env<GenContext> env) { |
392 | Env<GenContext> env1 = env; |
393 | while (true) { |
394 | genFinalizer(env1); |
395 | if (env1.tree == target) break; |
396 | env1 = env1.next; |
397 | } |
398 | return env1; |
399 | } |
400 | |
401 | /** Mark end of gap in catch-all range for finalizer. |
402 | * @param env the environment which might contain the finalizer |
403 | * (if it does, env.info.gaps != null). |
404 | */ |
405 | void endFinalizerGap(Env<GenContext> env) { |
406 | if (env.info.gaps != null && env.info.gaps.length() % 2 == 1) |
407 | env.info.gaps.append(code.curPc()); |
408 | } |
409 | |
410 | /** Mark end of all gaps in catch-all ranges for finalizers of environments |
411 | * lying between, and including to two environments. |
412 | * @param from the most deeply nested environment to mark |
413 | * @param to the least deeply nested environment to mark |
414 | */ |
415 | void endFinalizerGaps(Env<GenContext> from, Env<GenContext> to) { |
416 | Env<GenContext> last = null; |
417 | while (last != to) { |
418 | endFinalizerGap(from); |
419 | last = from; |
420 | from = from.next; |
421 | } |
422 | } |
423 | |
424 | /** Do any of the structures aborted by a non-local exit have |
425 | * finalizers that require an empty stack? |
426 | * @param target The tree representing the structure that's aborted |
427 | * @param env The environment current at the non-local exit. |
428 | */ |
429 | boolean hasFinally(JCTree target, Env<GenContext> env) { |
430 | while (env.tree != target) { |
431 | if (env.tree.getTag() == JCTree.TRY && env.info.finalize.hasFinalizer()) |
432 | return true; |
433 | env = env.next; |
434 | } |
435 | return false; |
436 | } |
437 | |
438 | /* ************************************************************************ |
439 | * Normalizing class-members. |
440 | *************************************************************************/ |
441 | |
442 | /** Distribute member initializer code into constructors and <clinit> |
443 | * method. |
444 | * @param defs The list of class member declarations. |
445 | * @param c The enclosing class. |
446 | */ |
447 | List<JCTree> normalizeDefs(List<JCTree> defs, ClassSymbol c) { |
448 | ListBuffer<JCStatement> initCode = new ListBuffer<JCStatement>(); |
449 | ListBuffer<JCStatement> clinitCode = new ListBuffer<JCStatement>(); |
450 | ListBuffer<JCTree> methodDefs = new ListBuffer<JCTree>(); |
451 | // Sort definitions into three listbuffers: |
452 | // - initCode for instance initializers |
453 | // - clinitCode for class initializers |
454 | // - methodDefs for method definitions |
455 | for (List<JCTree> l = defs; l.nonEmpty(); l = l.tail) { |
456 | JCTree def = l.head; |
457 | switch (def.getTag()) { |
458 | case JCTree.BLOCK: |
459 | JCBlock block = (JCBlock)def; |
460 | if ((block.flags & STATIC) != 0) |
461 | clinitCode.append(block); |
462 | else |
463 | initCode.append(block); |
464 | break; |
465 | case JCTree.METHODDEF: |
466 | methodDefs.append(def); |
467 | break; |
468 | case JCTree.VARDEF: |
469 | JCVariableDecl vdef = (JCVariableDecl) def; |
470 | VarSymbol sym = vdef.sym; |
471 | checkDimension(vdef.pos(), sym.type); |
472 | if (vdef.init != null) { |
473 | if ((sym.flags() & STATIC) == 0) { |
474 | // Always initialize instance variables. |
475 | JCStatement init = make.at(vdef.pos()). |
476 | Assignment(sym, vdef.init); |
477 | initCode.append(init); |
478 | if (endPositions != null) { |
479 | Integer endPos = endPositions.remove(vdef); |
480 | if (endPos != null) endPositions.put(init, endPos); |
481 | } |
482 | } else if (sym.getConstValue() == null) { |
483 | // Initialize class (static) variables only if |
484 | // they are not compile-time constants. |
485 | JCStatement init = make.at(vdef.pos). |
486 | Assignment(sym, vdef.init); |
487 | clinitCode.append(init); |
488 | if (endPositions != null) { |
489 | Integer endPos = endPositions.remove(vdef); |
490 | if (endPos != null) endPositions.put(init, endPos); |
491 | } |
492 | } else { |
493 | checkStringConstant(vdef.init.pos(), sym.getConstValue()); |
494 | } |
495 | } |
496 | break; |
497 | default: |
498 | assert false; |
499 | } |
500 | } |
501 | // Insert any instance initializers into all constructors. |
502 | if (initCode.length() != 0) { |
503 | List<JCStatement> inits = initCode.toList(); |
504 | for (JCTree t : methodDefs) { |
505 | normalizeMethod((JCMethodDecl)t, inits); |
506 | } |
507 | } |
508 | // If there are class initializers, create a <clinit> method |
509 | // that contains them as its body. |
510 | if (clinitCode.length() != 0) { |
511 | MethodSymbol clinit = new MethodSymbol( |
512 | STATIC, names.clinit, |
513 | new MethodType( |
514 | List.<Type>nil(), syms.voidType, |
515 | List.<Type>nil(), syms.methodClass), |
516 | c); |
517 | c.members().enter(clinit); |
518 | List<JCStatement> clinitStats = clinitCode.toList(); |
519 | JCBlock block = make.at(clinitStats.head.pos()).Block(0, clinitStats); |
520 | block.endpos = TreeInfo.endPos(clinitStats.last()); |
521 | methodDefs.append(make.MethodDef(clinit, block)); |
522 | } |
523 | // Return all method definitions. |
524 | return methodDefs.toList(); |
525 | } |
526 | |
527 | /** Check a constant value and report if it is a string that is |
528 | * too large. |
529 | */ |
530 | private void checkStringConstant(DiagnosticPosition pos, Object constValue) { |
531 | if (nerrs != 0 || // only complain about a long string once |
532 | constValue == null || |
533 | !(constValue instanceof String) || |
534 | ((String)constValue).length() < Pool.MAX_STRING_LENGTH) |
535 | return; |
536 | log.error(pos, "limit.string"); |
537 | nerrs++; |
538 | } |
539 | |
540 | /** Insert instance initializer code into initial constructor. |
541 | * @param md The tree potentially representing a |
542 | * constructor's definition. |
543 | * @param initCode The list of instance initializer statements. |
544 | */ |
545 | void normalizeMethod(JCMethodDecl md, List<JCStatement> initCode) { |
546 | if (md.name == names.init && TreeInfo.isInitialConstructor(md)) { |
547 | // We are seeing a constructor that does not call another |
548 | // constructor of the same class. |
549 | List<JCStatement> stats = md.body.stats; |
550 | ListBuffer<JCStatement> newstats = new ListBuffer<JCStatement>(); |
551 | |
552 | if (stats.nonEmpty()) { |
553 | // Copy initializers of synthetic variables generated in |
554 | // the translation of inner classes. |
555 | while (TreeInfo.isSyntheticInit(stats.head)) { |
556 | newstats.append(stats.head); |
557 | stats = stats.tail; |
558 | } |
559 | // Copy superclass constructor call |
560 | newstats.append(stats.head); |
561 | stats = stats.tail; |
562 | // Copy remaining synthetic initializers. |
563 | while (stats.nonEmpty() && |
564 | TreeInfo.isSyntheticInit(stats.head)) { |
565 | newstats.append(stats.head); |
566 | stats = stats.tail; |
567 | } |
568 | // Now insert the initializer code. |
569 | newstats.appendList(initCode); |
570 | // And copy all remaining statements. |
571 | while (stats.nonEmpty()) { |
572 | newstats.append(stats.head); |
573 | stats = stats.tail; |
574 | } |
575 | } |
576 | md.body.stats = newstats.toList(); |
577 | if (md.body.endpos == Position.NOPOS) |
578 | md.body.endpos = TreeInfo.endPos(md.body.stats.last()); |
579 | } |
580 | } |
581 | |
582 | /* ******************************************************************** |
583 | * Adding miranda methods |
584 | *********************************************************************/ |
585 | |
586 | /** Add abstract methods for all methods defined in one of |
587 | * the interfaces of a given class, |
588 | * provided they are not already implemented in the class. |
589 | * |
590 | * @param c The class whose interfaces are searched for methods |
591 | * for which Miranda methods should be added. |
592 | */ |
593 | void implementInterfaceMethods(ClassSymbol c) { |
594 | implementInterfaceMethods(c, c); |
595 | } |
596 | |
597 | /** Add abstract methods for all methods defined in one of |
598 | * the interfaces of a given class, |
599 | * provided they are not already implemented in the class. |
600 | * |
601 | * @param c The class whose interfaces are searched for methods |
602 | * for which Miranda methods should be added. |
603 | * @param site The class in which a definition may be needed. |
604 | */ |
605 | void implementInterfaceMethods(ClassSymbol c, ClassSymbol site) { |
606 | for (List<Type> l = types.interfaces(c.type); l.nonEmpty(); l = l.tail) { |
607 | ClassSymbol i = (ClassSymbol)l.head.tsym; |
608 | for (Scope.Entry e = i.members().elems; |
609 | e != null; |
610 | e = e.sibling) |
611 | { |
612 | if (e.sym.kind == MTH && (e.sym.flags() & STATIC) == 0) |
613 | { |
614 | MethodSymbol absMeth = (MethodSymbol)e.sym; |
615 | MethodSymbol implMeth = absMeth.binaryImplementation(site, types); |
616 | if (implMeth == null) |
617 | addAbstractMethod(site, absMeth); |
618 | else if ((implMeth.flags() & IPROXY) != 0) |
619 | adjustAbstractMethod(site, implMeth, absMeth); |
620 | } |
621 | } |
622 | implementInterfaceMethods(i, site); |
623 | } |
624 | } |
625 | |
626 | /** Add an abstract methods to a class |
627 | * which implicitly implements a method defined in some interface |
628 | * implemented by the class. These methods are called "Miranda methods". |
629 | * Enter the newly created method into its enclosing class scope. |
630 | * Note that it is not entered into the class tree, as the emitter |
631 | * doesn't need to see it there to emit an abstract method. |
632 | * |
633 | * @param c The class to which the Miranda method is added. |
634 | * @param m The interface method symbol for which a Miranda method |
635 | * is added. |
636 | */ |
637 | private void addAbstractMethod(ClassSymbol c, |
638 | MethodSymbol m) { |
639 | MethodSymbol absMeth = new MethodSymbol( |
640 | m.flags() | IPROXY | SYNTHETIC, m.name, |
641 | m.type, // was c.type.memberType(m), but now only !generics supported |
642 | c); |
643 | c.members().enter(absMeth); // add to symbol table |
644 | } |
645 | |
646 | private void adjustAbstractMethod(ClassSymbol c, |
647 | MethodSymbol pm, |
648 | MethodSymbol im) { |
649 | MethodType pmt = (MethodType)pm.type; |
650 | Type imt = types.memberType(c.type, im); |
651 | pmt.thrown = chk.intersect(pmt.getThrownTypes(), imt.getThrownTypes()); |
652 | } |
653 | |
654 | /* ************************************************************************ |
655 | * Traversal methods |
656 | *************************************************************************/ |
657 | |
658 | /** Visitor argument: The current environment. |
659 | */ |
660 | Env<GenContext> env; |
661 | |
662 | /** Visitor argument: The expected type (prototype). |
663 | */ |
664 | Type pt; |
665 | |
666 | /** Visitor result: The item representing the computed value. |
667 | */ |
668 | Item result; |
669 | |
670 | /** Visitor method: generate code for a definition, catching and reporting |
671 | * any completion failures. |
672 | * @param tree The definition to be visited. |
673 | * @param env The environment current at the definition. |
674 | */ |
675 | public void genDef(JCTree tree, Env<GenContext> env) { |
676 | Env<GenContext> prevEnv = this.env; |
677 | try { |
678 | this.env = env; |
679 | tree.accept(this); |
680 | } catch (CompletionFailure ex) { |
681 | chk.completionError(tree.pos(), ex); |
682 | } finally { |
683 | this.env = prevEnv; |
684 | } |
685 | } |
686 | |
687 | /** Derived visitor method: check whether CharacterRangeTable |
688 | * should be emitted, if so, put a new entry into CRTable |
689 | * and call method to generate bytecode. |
690 | * If not, just call method to generate bytecode. |
691 | * @see #genStat(Tree, Env) |
692 | * |
693 | * @param tree The tree to be visited. |
694 | * @param env The environment to use. |
695 | * @param crtFlags The CharacterRangeTable flags |
696 | * indicating type of the entry. |
697 | */ |
698 | public void genStat(JCTree tree, Env<GenContext> env, int crtFlags) { |
699 | if (!genCrt) { |
700 | genStat(tree, env); |
701 | return; |
702 | } |
703 | int startpc = code.curPc(); |
704 | genStat(tree, env); |
705 | if (tree.getTag() == JCTree.BLOCK) crtFlags |= CRT_BLOCK; |
706 | code.crt.put(tree, crtFlags, startpc, code.curPc()); |
707 | } |
708 | |
709 | /** Derived visitor method: generate code for a statement. |
710 | */ |
711 | public void genStat(JCTree tree, Env<GenContext> env) { |
712 | if (code.isAlive()) { |
713 | code.statBegin(tree.pos); |
714 | genDef(tree, env); |
715 | } else if (env.info.isSwitch && tree.getTag() == JCTree.VARDEF) { |
716 | // variables whose declarations are in a switch |
717 | // can be used even if the decl is unreachable. |
718 | code.newLocal(((JCVariableDecl) tree).sym); |
719 | } |
720 | } |
721 | |
722 | /** Derived visitor method: check whether CharacterRangeTable |
723 | * should be emitted, if so, put a new entry into CRTable |
724 | * and call method to generate bytecode. |
725 | * If not, just call method to generate bytecode. |
726 | * @see #genStats(List, Env) |
727 | * |
728 | * @param trees The list of trees to be visited. |
729 | * @param env The environment to use. |
730 | * @param crtFlags The CharacterRangeTable flags |
731 | * indicating type of the entry. |
732 | */ |
733 | public void genStats(List<JCStatement> trees, Env<GenContext> env, int crtFlags) { |
734 | if (!genCrt) { |
735 | genStats(trees, env); |
736 | return; |
737 | } |
738 | if (trees.length() == 1) { // mark one statement with the flags |
739 | genStat(trees.head, env, crtFlags | CRT_STATEMENT); |
740 | } else { |
741 | int startpc = code.curPc(); |
742 | genStats(trees, env); |
743 | code.crt.put(trees, crtFlags, startpc, code.curPc()); |
744 | } |
745 | } |
746 | |
747 | /** Derived visitor method: generate code for a list of statements. |
748 | */ |
749 | public void genStats(List<? extends JCTree> trees, Env<GenContext> env) { |
750 | for (List<? extends JCTree> l = trees; l.nonEmpty(); l = l.tail) |
751 | genStat(l.head, env, CRT_STATEMENT); |
752 | } |
753 | |
754 | /** Derived visitor method: check whether CharacterRangeTable |
755 | * should be emitted, if so, put a new entry into CRTable |
756 | * and call method to generate bytecode. |
757 | * If not, just call method to generate bytecode. |
758 | * @see #genCond(Tree,boolean) |
759 | * |
760 | * @param tree The tree to be visited. |
761 | * @param crtFlags The CharacterRangeTable flags |
762 | * indicating type of the entry. |
763 | */ |
764 | public CondItem genCond(JCTree tree, int crtFlags) { |
765 | if (!genCrt) return genCond(tree, false); |
766 | int startpc = code.curPc(); |
767 | CondItem item = genCond(tree, (crtFlags & CRT_FLOW_CONTROLLER) != 0); |
768 | code.crt.put(tree, crtFlags, startpc, code.curPc()); |
769 | return item; |
770 | } |
771 | |
772 | /** Derived visitor method: generate code for a boolean |
773 | * expression in a control-flow context. |
774 | * @param _tree The expression to be visited. |
775 | * @param markBranches The flag to indicate that the condition is |
776 | * a flow controller so produced conditions |
777 | * should contain a proper tree to generate |
778 | * CharacterRangeTable branches for them. |
779 | */ |
780 | public CondItem genCond(JCTree _tree, boolean markBranches) { |
781 | JCTree inner_tree = TreeInfo.skipParens(_tree); |
782 | if (inner_tree.getTag() == JCTree.CONDEXPR) { |
783 | JCConditional tree = (JCConditional)inner_tree; |
784 | CondItem cond = genCond(tree.cond, CRT_FLOW_CONTROLLER); |
785 | if (cond.isTrue()) { |
786 | code.resolve(cond.trueJumps); |
787 | CondItem result = genCond(tree.truepart, CRT_FLOW_TARGET); |
788 | if (markBranches) result.tree = tree.truepart; |
789 | return result; |
790 | } |
791 | if (cond.isFalse()) { |
792 | code.resolve(cond.falseJumps); |
793 | CondItem result = genCond(tree.falsepart, CRT_FLOW_TARGET); |
794 | if (markBranches) result.tree = tree.falsepart; |
795 | return result; |
796 | } |
797 | Chain secondJumps = cond.jumpFalse(); |
798 | code.resolve(cond.trueJumps); |
799 | CondItem first = genCond(tree.truepart, CRT_FLOW_TARGET); |
800 | if (markBranches) first.tree = tree.truepart; |
801 | Chain falseJumps = first.jumpFalse(); |
802 | code.resolve(first.trueJumps); |
803 | Chain trueJumps = code.branch(goto_); |
804 | code.resolve(secondJumps); |
805 | CondItem second = genCond(tree.falsepart, CRT_FLOW_TARGET); |
806 | CondItem result = items.makeCondItem(second.opcode, |
807 | code.mergeChains(trueJumps, second.trueJumps), |
808 | code.mergeChains(falseJumps, second.falseJumps)); |
809 | if (markBranches) result.tree = tree.falsepart; |
810 | return result; |
811 | } else { |
812 | CondItem result = genExpr(_tree, syms.booleanType).mkCond(); |
813 | if (markBranches) result.tree = _tree; |
814 | return result; |
815 | } |
816 | } |
817 | |
818 | /** Visitor method: generate code for an expression, catching and reporting |
819 | * any completion failures. |
820 | * @param tree The expression to be visited. |
821 | * @param pt The expression's expected type (proto-type). |
822 | */ |
823 | public Item genExpr(JCTree tree, Type pt) { |
824 | Type prevPt = this.pt; |
825 | try { |
826 | if (tree.type.constValue() != null) { |
827 | // Short circuit any expressions which are constants |
828 | checkStringConstant(tree.pos(), tree.type.constValue()); |
829 | result = items.makeImmediateItem(tree.type, tree.type.constValue()); |
830 | } else { |
831 | this.pt = pt; |
832 | tree.accept(this); |
833 | } |
834 | return result.coerce(pt); |
835 | } catch (CompletionFailure ex) { |
836 | chk.completionError(tree.pos(), ex); |
837 | code.state.stacksize = 1; |
838 | return items.makeStackItem(pt); |
839 | } finally { |
840 | this.pt = prevPt; |
841 | } |
842 | } |
843 | |
844 | /** Derived visitor method: generate code for a list of method arguments. |
845 | * @param trees The argument expressions to be visited. |
846 | * @param pts The expression's expected types (i.e. the formal parameter |
847 | * types of the invoked method). |
848 | */ |
849 | public void genArgs(List<JCExpression> trees, List<Type> pts) { |
850 | for (List<JCExpression> l = trees; l.nonEmpty(); l = l.tail) { |
851 | genExpr(l.head, pts.head).load(); |
852 | pts = pts.tail; |
853 | } |
854 | // require lists be of same length |
855 | assert pts.isEmpty(); |
856 | } |
857 | |
858 | /* ************************************************************************ |
859 | * Visitor methods for statements and definitions |
860 | *************************************************************************/ |
861 | |
862 | /** Thrown when the byte code size exceeds limit. |
863 | */ |
864 | public static class CodeSizeOverflow extends RuntimeException { |
865 | private static final long serialVersionUID = 0; |
866 | public CodeSizeOverflow() {} |
867 | } |
868 | |
869 | public void visitMethodDef(JCMethodDecl tree) { |
870 | // Create a new local environment that points pack at method |
871 | // definition. |
872 | Env<GenContext> localEnv = env.dup(tree); |
873 | localEnv.enclMethod = tree; |
874 | |
875 | // The expected type of every return statement in this method |
876 | // is the method's return type. |
877 | this.pt = tree.sym.erasure(types).getReturnType(); |
878 | |
879 | checkDimension(tree.pos(), tree.sym.erasure(types)); |
880 | genMethod(tree, localEnv, false); |
881 | } |
882 | //where |
883 | /** Generate code for a method. |
884 | * @param tree The tree representing the method definition. |
885 | * @param env The environment current for the method body. |
886 | * @param fatcode A flag that indicates whether all jumps are |
887 | * within 32K. We first invoke this method under |
888 | * the assumption that fatcode == false, i.e. all |
889 | * jumps are within 32K. If this fails, fatcode |
890 | * is set to true and we try again. |
891 | */ |
892 | void genMethod(JCMethodDecl tree, Env<GenContext> env, boolean fatcode) { |
893 | MethodSymbol meth = tree.sym; |
894 | // System.err.println("Generating " + meth + " in " + meth.owner); //DEBUG |
895 | if (Code.width(types.erasure(env.enclMethod.sym.type).getParameterTypes()) + |
896 | (((tree.mods.flags & STATIC) == 0 || meth.isConstructor()) ? 1 : 0) > |
897 | ClassFile.MAX_PARAMETERS) { |
898 | log.error(tree.pos(), "limit.parameters"); |
899 | nerrs++; |
900 | } |
901 | |
902 | else if (tree.body != null) { |
903 | // Create a new code structure and initialize it. |
904 | int startpcCrt = initCode(tree, env, fatcode); |
905 | |
906 | try { |
907 | genStat(tree.body, env); |
908 | } catch (CodeSizeOverflow e) { |
909 | // Failed due to code limit, try again with jsr/ret |
910 | startpcCrt = initCode(tree, env, fatcode); |
911 | genStat(tree.body, env); |
912 | } |
913 | |
914 | if (code.state.stacksize != 0) { |
915 | log.error(tree.body.pos(), "stack.sim.error", tree); |
916 | throw new AssertionError(); |
917 | } |
918 | |
919 | // If last statement could complete normally, insert a |
920 | // return at the end. |
921 | if (code.isAlive()) { |
922 | code.statBegin(TreeInfo.endPos(tree.body)); |
923 | if (env.enclMethod == null || |
924 | env.enclMethod.sym.type.getReturnType().tag == VOID) { |
925 | code.emitop0(return_); |
926 | } else { |
927 | // sometime dead code seems alive (4415991); |
928 | // generate a small loop instead |
929 | int startpc = code.entryPoint(); |
930 | CondItem c = items.makeCondItem(goto_); |
931 | code.resolve(c.jumpTrue(), startpc); |
932 | } |
933 | } |
934 | if (genCrt) |
935 | code.crt.put(tree.body, |
936 | CRT_BLOCK, |
937 | startpcCrt, |
938 | code.curPc()); |
939 | |
940 | // End the scope of all local variables in variable info. |
941 | code.endScopes(0); |
942 | |
943 | // If we exceeded limits, panic |
944 | if (code.checkLimits(tree.pos(), log)) { |
945 | nerrs++; |
946 | return; |
947 | } |
948 | |
949 | // If we generated short code but got a long jump, do it again |
950 | // with fatCode = true. |
951 | if (!fatcode && code.fatcode) genMethod(tree, env, true); |
952 | |
953 | // Clean up |
954 | if(stackMap == StackMapFormat.JSR202) { |
955 | code.lastFrame = null; |
956 | code.frameBeforeLast = null; |
957 | } |
958 | } |
959 | } |
960 | |
961 | private int initCode(JCMethodDecl tree, Env<GenContext> env, boolean fatcode) { |
962 | MethodSymbol meth = tree.sym; |
963 | |
964 | // Create a new code structure. |
965 | meth.code = code = new Code(meth, |
966 | fatcode, |
967 | lineDebugInfo ? toplevel.lineMap : null, |
968 | varDebugInfo, |
969 | stackMap, |
970 | debugCode, |
971 | genCrt ? new CRTable(tree, env.toplevel.endPositions) |
972 | : null, |
973 | syms, |
974 | types, |
975 | pool); |
976 | items = new Items(pool, code, syms, types); |
977 | if (code.debugCode) |
978 | System.err.println(meth + " for body " + tree); |
979 | |
980 | // If method is not static, create a new local variable address |
981 | // for `this'. |
982 | if ((tree.mods.flags & STATIC) == 0) { |
983 | Type selfType = meth.owner.type; |
984 | if (meth.isConstructor() && selfType != syms.objectType) |
985 | selfType = UninitializedType.uninitializedThis(selfType); |
986 | code.setDefined( |
987 | code.newLocal( |
988 | new VarSymbol(FINAL, names._this, selfType, meth.owner))); |
989 | } |
990 | |
991 | // Mark all parameters as defined from the beginning of |
992 | // the method. |
993 | for (List<JCVariableDecl> l = tree.params; l.nonEmpty(); l = l.tail) { |
994 | checkDimension(l.head.pos(), l.head.sym.type); |
995 | code.setDefined(code.newLocal(l.head.sym)); |
996 | } |
997 | |
998 | // Get ready to generate code for method body. |
999 | int startpcCrt = genCrt ? code.curPc() : 0; |
1000 | code.entryPoint(); |
1001 | |
1002 | // Suppress initial stackmap |
1003 | code.pendingStackMap = false; |
1004 | |
1005 | return startpcCrt; |
1006 | } |
1007 | |
1008 | public void visitVarDef(JCVariableDecl tree) { |
1009 | VarSymbol v = tree.sym; |
1010 | code.newLocal(v); |
1011 | if (tree.init != null) { |
1012 | checkStringConstant(tree.init.pos(), v.getConstValue()); |
1013 | if (v.getConstValue() == null || varDebugInfo) { |
1014 | genExpr(tree.init, v.erasure(types)).load(); |
1015 | items.makeLocalItem(v).store(); |
1016 | } |
1017 | } |
1018 | checkDimension(tree.pos(), v.type); |
1019 | } |
1020 | |
1021 | public void visitSkip(JCSkip tree) { |
1022 | } |
1023 | |
1024 | public void visitBlock(JCBlock tree) { |
1025 | int limit = code.nextreg; |
1026 | Env<GenContext> localEnv = env.dup(tree, new GenContext()); |
1027 | genStats(tree.stats, localEnv); |
1028 | // End the scope of all block-local variables in variable info. |
1029 | if (env.tree.getTag() != JCTree.METHODDEF) { |
1030 | code.statBegin(tree.endpos); |
1031 | code.endScopes(limit); |
1032 | code.pendingStatPos = Position.NOPOS; |
1033 | } |
1034 | } |
1035 | |
1036 | public void visitDoLoop(JCDoWhileLoop tree) { |
1037 | genLoop(tree, tree.body, tree.cond, List.<JCExpressionStatement>nil(), false); |
1038 | } |
1039 | |
1040 | public void visitWhileLoop(JCWhileLoop tree) { |
1041 | genLoop(tree, tree.body, tree.cond, List.<JCExpressionStatement>nil(), true); |
1042 | } |
1043 | |
1044 | public void visitForLoop(JCForLoop tree) { |
1045 | int limit = code.nextreg; |
1046 | genStats(tree.init, env); |
1047 | genLoop(tree, tree.body, tree.cond, tree.step, true); |
1048 | code.endScopes(limit); |
1049 | } |
1050 | //where |
1051 | /** Generate code for a loop. |
1052 | * @param loop The tree representing the loop. |
1053 | * @param body The loop's body. |
1054 | * @param cond The loop's controling condition. |
1055 | * @param step "Step" statements to be inserted at end of |
1056 | * each iteration. |
1057 | * @param testFirst True if the loop test belongs before the body. |
1058 | */ |
1059 | private void genLoop(JCStatement loop, |
1060 | JCStatement body, |
1061 | JCExpression cond, |
1062 | List<JCExpressionStatement> step, |
1063 | boolean testFirst) { |
1064 | Env<GenContext> loopEnv = env.dup(loop, new GenContext()); |
1065 | int startpc = code.entryPoint(); |
1066 | if (testFirst) { |
1067 | CondItem c; |
1068 | if (cond != null) { |
1069 | code.statBegin(cond.pos); |
1070 | c = genCond(TreeInfo.skipParens(cond), CRT_FLOW_CONTROLLER); |
1071 | } else { |
1072 | c = items.makeCondItem(goto_); |
1073 | } |
1074 | Chain loopDone = c.jumpFalse(); |
1075 | code.resolve(c.trueJumps); |
1076 | genStat(body, loopEnv, CRT_STATEMENT | CRT_FLOW_TARGET); |
1077 | code.resolve(loopEnv.info.cont); |
1078 | genStats(step, loopEnv); |
1079 | code.resolve(code.branch(goto_), startpc); |
1080 | code.resolve(loopDone); |
1081 | } else { |
1082 | genStat(body, loopEnv, CRT_STATEMENT | CRT_FLOW_TARGET); |
1083 | code.resolve(loopEnv.info.cont); |
1084 | genStats(step, loopEnv); |
1085 | CondItem c; |
1086 | if (cond != null) { |
1087 | code.statBegin(cond.pos); |
1088 | c = genCond(TreeInfo.skipParens(cond), CRT_FLOW_CONTROLLER); |
1089 | } else { |
1090 | c = items.makeCondItem(goto_); |
1091 | } |
1092 | code.resolve(c.jumpTrue(), startpc); |
1093 | code.resolve(c.falseJumps); |
1094 | } |
1095 | code.resolve(loopEnv.info.exit); |
1096 | } |
1097 | |
1098 | public void visitForeachLoop(JCEnhancedForLoop tree) { |
1099 | throw new AssertionError(); // should have been removed by Lower. |
1100 | } |
1101 | |
1102 | public void visitLabelled(JCLabeledStatement tree) { |
1103 | Env<GenContext> localEnv = env.dup(tree, new GenContext()); |
1104 | genStat(tree.body, localEnv, CRT_STATEMENT); |
1105 | code.resolve(localEnv.info.exit); |
1106 | } |
1107 | |
1108 | public void visitSwitch(JCSwitch tree) { |
1109 | int limit = code.nextreg; |
1110 | assert tree.selector.type.tag != CLASS; |
1111 | int startpcCrt = genCrt ? code.curPc() : 0; |
1112 | Item sel = genExpr(tree.selector, syms.intType); |
1113 | List<JCCase> cases = tree.cases; |
1114 | if (cases.isEmpty()) { |
1115 | // We are seeing: switch <sel> {} |
1116 | sel.load().drop(); |
1117 | if (genCrt) |
1118 | code.crt.put(TreeInfo.skipParens(tree.selector), |
1119 | CRT_FLOW_CONTROLLER, startpcCrt, code.curPc()); |
1120 | } else { |
1121 | // We are seeing a nonempty switch. |
1122 | sel.load(); |
1123 | if (genCrt) |
1124 | code.crt.put(TreeInfo.skipParens(tree.selector), |
1125 | CRT_FLOW_CONTROLLER, startpcCrt, code.curPc()); |
1126 | Env<GenContext> switchEnv = env.dup(tree, new GenContext()); |
1127 | switchEnv.info.isSwitch = true; |
1128 | |
1129 | // Compute number of labels and minimum and maximum label values. |
1130 | // For each case, store its label in an array. |
1131 | int lo = Integer.MAX_VALUE; // minimum label. |
1132 | int hi = Integer.MIN_VALUE; // maximum label. |
1133 | int nlabels = 0; // number of labels. |
1134 | |
1135 | int[] labels = new int[cases.length()]; // the label array. |
1136 | int defaultIndex = -1; // the index of the default clause. |
1137 | |
1138 | List<JCCase> l = cases; |
1139 | for (int i = 0; i < labels.length; i++) { |
1140 | if (l.head.pat != null) { |
1141 | int val = ((Number)l.head.pat.type.constValue()).intValue(); |
1142 | labels[i] = val; |
1143 | if (val < lo) lo = val; |
1144 | if (hi < val) hi = val; |
1145 | nlabels++; |
1146 | } else { |
1147 | assert defaultIndex == -1; |
1148 | defaultIndex = i; |
1149 | } |
1150 | l = l.tail; |
1151 | } |
1152 | |
1153 | // Determine whether to issue a tableswitch or a lookupswitch |
1154 | // instruction. |
1155 | long table_space_cost = 4 + ((long) hi - lo + 1); // words |
1156 | long table_time_cost = 3; // comparisons |
1157 | long lookup_space_cost = 3 + 2 * (long) nlabels; |
1158 | long lookup_time_cost = nlabels; |
1159 | int opcode = |
1160 | nlabels > 0 && |
1161 | table_space_cost + 3 * table_time_cost <= |
1162 | lookup_space_cost + 3 * lookup_time_cost |
1163 | ? |
1164 | tableswitch : lookupswitch; |
1165 | |
1166 | int startpc = code.curPc(); // the position of the selector operation |
1167 | code.emitop0(opcode); |
1168 | code.align(4); |
1169 | int tableBase = code.curPc(); // the start of the jump table |
1170 | int[] offsets = null; // a table of offsets for a lookupswitch |
1171 | code.emit4(-1); // leave space for default offset |
1172 | if (opcode == tableswitch) { |
1173 | code.emit4(lo); // minimum label |
1174 | code.emit4(hi); // maximum label |
1175 | for (long i = lo; i <= hi; i++) { // leave space for jump table |
1176 | code.emit4(-1); |
1177 | } |
1178 | } else { |
1179 | code.emit4(nlabels); // number of labels |
1180 | for (int i = 0; i < nlabels; i++) { |
1181 | code.emit4(-1); code.emit4(-1); // leave space for lookup table |
1182 | } |
1183 | offsets = new int[labels.length]; |
1184 | } |
1185 | Code.State stateSwitch = code.state.dup(); |
1186 | code.markDead(); |
1187 | |
1188 | // For each case do: |
1189 | l = cases; |
1190 | for (int i = 0; i < labels.length; i++) { |
1191 | JCCase c = l.head; |
1192 | l = l.tail; |
1193 | |
1194 | int pc = code.entryPoint(stateSwitch); |
1195 | // Insert offset directly into code or else into the |
1196 | // offsets table. |
1197 | if (i != defaultIndex) { |
1198 | if (opcode == tableswitch) { |
1199 | code.put4( |
1200 | tableBase + 4 * (labels[i] - lo + 3), |
1201 | pc - startpc); |
1202 | } else { |
1203 | offsets[i] = pc - startpc; |
1204 | } |
1205 | } else { |
1206 | code.put4(tableBase, pc - startpc); |
1207 | } |
1208 | |
1209 | // Generate code for the statements in this case. |
1210 | genStats(c.stats, switchEnv, CRT_FLOW_TARGET); |
1211 | } |
1212 | |
1213 | // Resolve all breaks. |
1214 | code.resolve(switchEnv.info.exit); |
1215 | |
1216 | // If we have not set the default offset, we do so now. |
1217 | if (code.get4(tableBase) == -1) { |
1218 | code.put4(tableBase, code.entryPoint(stateSwitch) - startpc); |
1219 | } |
1220 | |
1221 | if (opcode == tableswitch) { |
1222 | // Let any unfilled slots point to the default case. |
1223 | int defaultOffset = code.get4(tableBase); |
1224 | for (long i = lo; i <= hi; i++) { |
1225 | int t = (int)(tableBase + 4 * (i - lo + 3)); |
1226 | if (code.get4(t) == -1) |
1227 | code.put4(t, defaultOffset); |
1228 | } |
1229 | } else { |
1230 | // Sort non-default offsets and copy into lookup table. |
1231 | if (defaultIndex >= 0) |
1232 | for (int i = defaultIndex; i < labels.length - 1; i++) { |
1233 | labels[i] = labels[i+1]; |
1234 | offsets[i] = offsets[i+1]; |
1235 | } |
1236 | if (nlabels > 0) |
1237 | qsort2(labels, offsets, 0, nlabels - 1); |
1238 | for (int i = 0; i < nlabels; i++) { |
1239 | int caseidx = tableBase + 8 * (i + 1); |
1240 | code.put4(caseidx, labels[i]); |
1241 | code.put4(caseidx + 4, offsets[i]); |
1242 | } |
1243 | } |
1244 | } |
1245 | code.endScopes(limit); |
1246 | } |
1247 | //where |
1248 | /** Sort (int) arrays of keys and values |
1249 | */ |
1250 | static void qsort2(int[] keys, int[] values, int lo, int hi) { |
1251 | int i = lo; |
1252 | int j = hi; |
1253 | int pivot = keys[(i+j)/2]; |
1254 | do { |
1255 | while (keys[i] < pivot) i++; |
1256 | while (pivot < keys[j]) j--; |
1257 | if (i <= j) { |
1258 | int temp1 = keys[i]; |
1259 | keys[i] = keys[j]; |
1260 | keys[j] = temp1; |
1261 | int temp2 = values[i]; |
1262 | values[i] = values[j]; |
1263 | values[j] = temp2; |
1264 | i++; |
1265 | j--; |
1266 | } |
1267 | } while (i <= j); |
1268 | if (lo < j) qsort2(keys, values, lo, j); |
1269 | if (i < hi) qsort2(keys, values, i, hi); |
1270 | } |
1271 | |
1272 | public void visitSynchronized(JCSynchronized tree) { |
1273 | int limit = code.nextreg; |
1274 | // Generate code to evaluate lock and save in temporary variable. |
1275 | final LocalItem lockVar = makeTemp(syms.objectType); |
1276 | genExpr(tree.lock, tree.lock.type).load().duplicate(); |
1277 | lockVar.store(); |
1278 | |
1279 | // Generate code to enter monitor. |
1280 | code.emitop0(monitorenter); |
1281 | code.state.lock(lockVar.reg); |
1282 | |
1283 | // Generate code for a try statement with given body, no catch clauses |
1284 | // in a new environment with the "exit-monitor" operation as finalizer. |
1285 | final Env<GenContext> syncEnv = env.dup(tree, new GenContext()); |
1286 | syncEnv.info.finalize = new GenFinalizer() { |
1287 | void gen() { |
1288 | genLast(); |
1289 | assert syncEnv.info.gaps.length() % 2 == 0; |
1290 | syncEnv.info.gaps.append(code.curPc()); |
1291 | } |
1292 | void genLast() { |
1293 | if (code.isAlive()) { |
1294 | lockVar.load(); |
1295 | code.emitop0(monitorexit); |
1296 | code.state.unlock(lockVar.reg); |
1297 | } |
1298 | } |
1299 | }; |
1300 | syncEnv.info.gaps = new ListBuffer<Integer>(); |
1301 | genTry(tree.body, List.<JCCatch>nil(), syncEnv); |
1302 | code.endScopes(limit); |
1303 | } |
1304 | |
1305 | public void visitTry(final JCTry tree) { |
1306 | // Generate code for a try statement with given body and catch clauses, |
1307 | // in a new environment which calls the finally block if there is one. |
1308 | final Env<GenContext> tryEnv = env.dup(tree, new GenContext()); |
1309 | final Env<GenContext> oldEnv = env; |
1310 | if (!useJsrLocally) { |
1311 | useJsrLocally = |
1312 | (stackMap == StackMapFormat.NONE) && |
1313 | (jsrlimit <= 0 || |
1314 | jsrlimit < 100 && |
1315 | estimateCodeComplexity(tree.finalizer)>jsrlimit); |
1316 | } |
1317 | tryEnv.info.finalize = new GenFinalizer() { |
1318 | void gen() { |
1319 | if (useJsrLocally) { |
1320 | if (tree.finalizer != null) { |
1321 | Code.State jsrState = code.state.dup(); |
1322 | jsrState.push(code.jsrReturnValue); |
1323 | tryEnv.info.cont = |
1324 | new Chain(code.emitJump(jsr), |
1325 | tryEnv.info.cont, |
1326 | jsrState); |
1327 | } |
1328 | assert tryEnv.info.gaps.length() % 2 == 0; |
1329 | tryEnv.info.gaps.append(code.curPc()); |
1330 | } else { |
1331 | assert tryEnv.info.gaps.length() % 2 == 0; |
1332 | tryEnv.info.gaps.append(code.curPc()); |
1333 | genLast(); |
1334 | } |
1335 | } |
1336 | void genLast() { |
1337 | if (tree.finalizer != null) |
1338 | genStat(tree.finalizer, oldEnv, CRT_BLOCK); |
1339 | } |
1340 | boolean hasFinalizer() { |
1341 | return tree.finalizer != null; |
1342 | } |
1343 | }; |
1344 | tryEnv.info.gaps = new ListBuffer<Integer>(); |
1345 | genTry(tree.body, tree.catchers, tryEnv); |
1346 | } |
1347 | //where |
1348 | /** Generate code for a try or synchronized statement |
1349 | * @param body The body of the try or synchronized statement. |
1350 | * @param catchers The lis of catch clauses. |
1351 | * @param env the environment current for the body. |
1352 | */ |
1353 | void genTry(JCTree body, List<JCCatch> catchers, Env<GenContext> env) { |
1354 | int limit = code.nextreg; |
1355 | int startpc = code.curPc(); |
1356 | Code.State stateTry = code.state.dup(); |
1357 | genStat(body, env, CRT_BLOCK); |
1358 | int endpc = code.curPc(); |
1359 | boolean hasFinalizer = |
1360 | env.info.finalize != null && |
1361 | env.info.finalize.hasFinalizer(); |
1362 | List<Integer> gaps = env.info.gaps.toList(); |
1363 | code.statBegin(TreeInfo.endPos(body)); |
1364 | genFinalizer(env); |
1365 | code.statBegin(TreeInfo.endPos(env.tree)); |
1366 | Chain exitChain = code.branch(goto_); |
1367 | endFinalizerGap(env); |
1368 | if (startpc != endpc) for (List<JCCatch> l = catchers; l.nonEmpty(); l = l.tail) { |
1369 | // start off with exception on stack |
1370 | code.entryPoint(stateTry, l.head.param.sym.type); |
1371 | genCatch(l.head, env, startpc, endpc, gaps); |
1372 | genFinalizer(env); |
1373 | if (hasFinalizer || l.tail.nonEmpty()) { |
1374 | code.statBegin(TreeInfo.endPos(env.tree)); |
1375 | exitChain = code.mergeChains(exitChain, |
1376 | code.branch(goto_)); |
1377 | } |
1378 | endFinalizerGap(env); |
1379 | } |
1380 | if (hasFinalizer) { |
1381 | // Create a new register segement to avoid allocating |
1382 | // the same variables in finalizers and other statements. |
1383 | code.newRegSegment(); |
1384 | |
1385 | // Add a catch-all clause. |
1386 | |
1387 | // start off with exception on stack |
1388 | int catchallpc = code.entryPoint(stateTry, syms.throwableType); |
1389 | |
1390 | // Register all exception ranges for catch all clause. |
1391 | // The range of the catch all clause is from the beginning |
1392 | // of the try or synchronized block until the present |
1393 | // code pointer excluding all gaps in the current |
1394 | // environment's GenContext. |
1395 | int startseg = startpc; |
1396 | while (env.info.gaps.nonEmpty()) { |
1397 | int endseg = env.info.gaps.next().intValue(); |
1398 | registerCatch(body.pos(), startseg, endseg, |
1399 | catchallpc, 0); |
1400 | startseg = env.info.gaps.next().intValue(); |
1401 | } |
1402 | code.statBegin(TreeInfo.finalizerPos(env.tree)); |
1403 | code.markStatBegin(); |
1404 | |
1405 | Item excVar = makeTemp(syms.throwableType); |
1406 | excVar.store(); |
1407 | genFinalizer(env); |
1408 | excVar.load(); |
1409 | registerCatch(body.pos(), startseg, |
1410 | env.info.gaps.next().intValue(), |
1411 | catchallpc, 0); |
1412 | code.emitop0(athrow); |
1413 | code.markDead(); |
1414 | |
1415 | // If there are jsr's to this finalizer, ... |
1416 | if (env.info.cont != null) { |
1417 | // Resolve all jsr's. |
1418 | code.resolve(env.info.cont); |
1419 | |
1420 | // Mark statement line number |
1421 | code.statBegin(TreeInfo.finalizerPos(env.tree)); |
1422 | code.markStatBegin(); |
1423 | |
1424 | // Save return address. |
1425 | LocalItem retVar = makeTemp(syms.throwableType); |
1426 | retVar.store(); |
1427 | |
1428 | // Generate finalizer code. |
1429 | env.info.finalize.genLast(); |
1430 | |
1431 | // Return. |
1432 | code.emitop1w(ret, retVar.reg); |
1433 | code.markDead(); |
1434 | } |
1435 | } |
1436 | |
1437 | // Resolve all breaks. |
1438 | code.resolve(exitChain); |
1439 | |
1440 | // End the scopes of all try-local variables in variable info. |
1441 | code.endScopes(limit); |
1442 | } |
1443 | |
1444 | /** Generate code for a catch clause. |
1445 | * @param tree The catch clause. |
1446 | * @param env The environment current in the enclosing try. |
1447 | * @param startpc Start pc of try-block. |
1448 | * @param endpc End pc of try-block. |
1449 | */ |
1450 | void genCatch(JCCatch tree, |
1451 | Env<GenContext> env, |
1452 | int startpc, int endpc, |
1453 | List<Integer> gaps) { |
1454 | if (startpc != endpc) { |
1455 | int catchType = makeRef(tree.pos(), tree.param.type); |
1456 | while (gaps.nonEmpty()) { |
1457 | int end = gaps.head.intValue(); |
1458 | registerCatch(tree.pos(), |
1459 | startpc, end, code.curPc(), |
1460 | catchType); |
1461 | gaps = gaps.tail; |
1462 | startpc = gaps.head.intValue(); |
1463 | gaps = gaps.tail; |
1464 | } |
1465 | if (startpc < endpc) |
1466 | registerCatch(tree.pos(), |
1467 | startpc, endpc, code.curPc(), |
1468 | catchType); |
1469 | VarSymbol exparam = tree.param.sym; |
1470 | code.statBegin(tree.pos); |
1471 | code.markStatBegin(); |
1472 | int limit = code.nextreg; |
1473 | int exlocal = code.newLocal(exparam); |
1474 | items.makeLocalItem(exparam).store(); |
1475 | code.statBegin(TreeInfo.firstStatPos(tree.body)); |
1476 | genStat(tree.body, env, CRT_BLOCK); |
1477 | code.endScopes(limit); |
1478 | code.statBegin(TreeInfo.endPos(tree.body)); |
1479 | } |
1480 | } |
1481 | |
1482 | /** Register a catch clause in the "Exceptions" code-attribute. |
1483 | */ |
1484 | void registerCatch(DiagnosticPosition pos, |
1485 | int startpc, int endpc, |
1486 | int handler_pc, int catch_type) { |
1487 | if (startpc != endpc) { |
1488 | char startpc1 = (char)startpc; |
1489 | char endpc1 = (char)endpc; |
1490 | char handler_pc1 = (char)handler_pc; |
1491 | if (startpc1 == startpc && |
1492 | endpc1 == endpc && |
1493 | handler_pc1 == handler_pc) { |
1494 | code.addCatch(startpc1, endpc1, handler_pc1, |
1495 | (char)catch_type); |
1496 | } else { |
1497 | if (!useJsrLocally && !target.generateStackMapTable()) { |
1498 | useJsrLocally = true; |
1499 | throw new CodeSizeOverflow(); |
1500 | } else { |
1501 | log.error(pos, "limit.code.too.large.for.try.stmt"); |
1502 | nerrs++; |
1503 | } |
1504 | } |
1505 | } |
1506 | } |
1507 | |
1508 | /** Very roughly estimate the number of instructions needed for |
1509 | * the given tree. |
1510 | */ |
1511 | int estimateCodeComplexity(JCTree tree) { |
1512 | if (tree == null) return 0; |
1513 | class ComplexityScanner extends TreeScanner { |
1514 | int complexity = 0; |
1515 | public void scan(JCTree tree) { |
1516 | if (complexity > jsrlimit) return; |
1517 | super.scan(tree); |
1518 | } |
1519 | public void visitClassDef(JCClassDecl tree) {} |
1520 | public void visitDoLoop(JCDoWhileLoop tree) |
1521 | { super.visitDoLoop(tree); complexity++; } |
1522 | public void visitWhileLoop(JCWhileLoop tree) |
1523 | { super.visitWhileLoop(tree); complexity++; } |
1524 | public void visitForLoop(JCForLoop tree) |
1525 | { super.visitForLoop(tree); complexity++; } |
1526 | public void visitSwitch(JCSwitch tree) |
1527 | { super.visitSwitch(tree); complexity+=5; } |
1528 | public void visitCase(JCCase tree) |
1529 | { super.visitCase(tree); complexity++; } |
1530 | public void visitSynchronized(JCSynchronized tree) |
1531 | { super.visitSynchronized(tree); complexity+=6; } |
1532 | public void visitTry(JCTry tree) |
1533 | { super.visitTry(tree); |
1534 | if (tree.finalizer != null) complexity+=6; } |
1535 | public void visitCatch(JCCatch tree) |
1536 | { super.visitCatch(tree); complexity+=2; } |
1537 | public void visitConditional(JCConditional tree) |
1538 | { super.visitConditional(tree); complexity+=2; } |
1539 | public void visitIf(JCIf tree) |
1540 | { super.visitIf(tree); complexity+=2; } |
1541 | // note: for break, continue, and return we don't take unwind() into account. |
1542 | public void visitBreak(JCBreak tree) |
1543 | { super.visitBreak(tree); complexity+=1; } |
1544 | public void visitContinue(JCContinue tree) |
1545 | { super.visitContinue(tree); complexity+=1; } |
1546 | public void visitReturn(JCReturn tree) |
1547 | { super.visitReturn(tree); complexity+=1; } |
1548 | public void visitThrow(JCThrow tree) |
1549 | { super.visitThrow(tree); complexity+=1; } |
1550 | public void visitAssert(JCAssert tree) |
1551 | { super.visitAssert(tree); complexity+=5; } |
1552 | public void visitApply(JCMethodInvocation tree) |
1553 | { super.visitApply(tree); complexity+=2; } |
1554 | public void visitNewClass(JCNewClass tree) |
1555 | { scan(tree.encl); scan(tree.args); complexity+=2; } |
1556 | public void visitNewArray(JCNewArray tree) |
1557 | { super.visitNewArray(tree); complexity+=5; } |
1558 | public void visitAssign(JCAssign tree) |
1559 | { super.visitAssign(tree); complexity+=1; } |
1560 | public void visitAssignop(JCAssignOp tree) |
1561 | { super.visitAssignop(tree); complexity+=2; } |
1562 | public void visitUnary(JCUnary tree) |
1563 | { complexity+=1; |
1564 | if (tree.type.constValue() == null) super.visitUnary(tree); } |
1565 | public void visitBinary(JCBinary tree) |
1566 | { complexity+=1; |
1567 | if (tree.type.constValue() == null) super.visitBinary(tree); } |
1568 | public void visitTypeTest(JCInstanceOf tree) |
1569 | { super.visitTypeTest(tree); complexity+=1; } |
1570 | public void visitIndexed(JCArrayAccess tree) |
1571 | { super.visitIndexed(tree); complexity+=1; } |
1572 | public void visitSelect(JCFieldAccess tree) |
1573 | { super.visitSelect(tree); |
1574 | if (tree.sym.kind == VAR) complexity+=1; } |
1575 | public void visitIdent(JCIdent tree) { |
1576 | if (tree.sym.kind == VAR) { |
1577 | complexity+=1; |
1578 | if (tree.type.constValue() == null && |
1579 | tree.sym.owner.kind == TYP) |
1580 | complexity+=1; |
1581 | } |
1582 | } |
1583 | public void visitLiteral(JCLiteral tree) |
1584 | { complexity+=1; } |
1585 | public void visitTree(JCTree tree) {} |
1586 | public void visitWildcard(JCWildcard tree) { |
1587 | throw new AssertionError(this.getClass().getName()); |
1588 | } |
1589 | } |
1590 | ComplexityScanner scanner = new ComplexityScanner(); |
1591 | tree.accept(scanner); |
1592 | return scanner.complexity; |
1593 | } |
1594 | |
1595 | public void visitIf(JCIf tree) { |
1596 | int limit = code.nextreg; |
1597 | Chain thenExit = null; |
1598 | CondItem c = genCond(TreeInfo.skipParens(tree.cond), |
1599 | CRT_FLOW_CONTROLLER); |
1600 | Chain elseChain = c.jumpFalse(); |
1601 | if (!c.isFalse()) { |
1602 | code.resolve(c.trueJumps); |
1603 | genStat(tree.thenpart, env, CRT_STATEMENT | CRT_FLOW_TARGET); |
1604 | thenExit = code.branch(goto_); |
1605 | } |
1606 | if (elseChain != null) { |
1607 | code.resolve(elseChain); |
1608 | if (tree.elsepart != null) |
1609 | genStat(tree.elsepart, env,CRT_STATEMENT | CRT_FLOW_TARGET); |
1610 | } |
1611 | code.resolve(thenExit); |
1612 | code.endScopes(limit); |
1613 | } |
1614 | |
1615 | public void visitExec(JCExpressionStatement tree) { |
1616 | // Optimize x++ to ++x and x-- to --x. |
1617 | JCExpression e = tree.expr; |
1618 | switch (e.getTag()) { |
1619 | case JCTree.POSTINC: |
1620 | ((JCUnary) e).setTag(JCTree.PREINC); |
1621 | break; |
1622 | case JCTree.POSTDEC: |
1623 | ((JCUnary) e).setTag(JCTree.PREDEC); |
1624 | break; |
1625 | } |
1626 | genExpr(tree.expr, tree.expr.type).drop(); |
1627 | } |
1628 | |
1629 | public void visitBreak(JCBreak tree) { |
1630 | Env<GenContext> targetEnv = unwind(tree.target, env); |
1631 | assert code.state.stacksize == 0; |
1632 | targetEnv.info.addExit(code.branch(goto_)); |
1633 | endFinalizerGaps(env, targetEnv); |
1634 | } |
1635 | |
1636 | public void visitContinue(JCContinue tree) { |
1637 | Env<GenContext> targetEnv = unwind(tree.target, env); |
1638 | assert code.state.stacksize == 0; |
1639 | targetEnv.info.addCont(code.branch(goto_)); |
1640 | endFinalizerGaps(env, targetEnv); |
1641 | } |
1642 | |
1643 | public void visitReturn(JCReturn tree) { |
1644 | int limit = code.nextreg; |
1645 | final Env<GenContext> targetEnv; |
1646 | if (tree.expr != null) { |
1647 | Item r = genExpr(tree.expr, pt).load(); |
1648 | if (hasFinally(env.enclMethod, env)) { |
1649 | r = makeTemp(pt); |
1650 | r.store(); |
1651 | } |
1652 | targetEnv = unwind(env.enclMethod, env); |
1653 | r.load(); |
1654 | code.emitop0(ireturn + Code.truncate(Code.typecode(pt))); |
1655 | } else { |
1656 | targetEnv = unwind(env.enclMethod, env); |
1657 | code.emitop0(return_); |
1658 | } |
1659 | endFinalizerGaps(env, targetEnv); |
1660 | code.endScopes(limit); |
1661 | } |
1662 | |
1663 | public void visitThrow(JCThrow tree) { |
1664 | genExpr(tree.expr, tree.expr.type).load(); |
1665 | code.emitop0(athrow); |
1666 | } |
1667 | |
1668 | /* ************************************************************************ |
1669 | * Visitor methods for expressions |
1670 | *************************************************************************/ |
1671 | |
1672 | public void visitApply(JCMethodInvocation tree) { |
1673 | // Generate code for method. |
1674 | Item m = genExpr(tree.meth, methodType); |
1675 | // Generate code for all arguments, where the expected types are |
1676 | // the parameters of the method's external type (that is, any implicit |
1677 | // outer instance of a super(...) call appears as first parameter). |
1678 | genArgs(tree.args, |
1679 | TreeInfo.symbol(tree.meth).externalType(types).getParameterTypes()); |
1680 | result = m.invoke(); |
1681 | } |
1682 | |
1683 | public void visitConditional(JCConditional tree) { |
1684 | Chain thenExit = null; |
1685 | CondItem c = genCond(tree.cond, CRT_FLOW_CONTROLLER); |
1686 | Chain elseChain = c.jumpFalse(); |
1687 | if (!c.isFalse()) { |
1688 | code.resolve(c.trueJumps); |
1689 | int startpc = genCrt ? code.curPc() : 0; |
1690 | genExpr(tree.truepart, pt).load(); |
1691 | code.state.forceStackTop(tree.type); |
1692 | if (genCrt) code.crt.put(tree.truepart, CRT_FLOW_TARGET, |
1693 | startpc, code.curPc()); |
1694 | thenExit = code.branch(goto_); |
1695 | } |
1696 | if (elseChain != null) { |
1697 | code.resolve(elseChain); |
1698 | int startpc = genCrt ? code.curPc() : 0; |
1699 | genExpr(tree.falsepart, pt).load(); |
1700 | code.state.forceStackTop(tree.type); |
1701 | if (genCrt) code.crt.put(tree.falsepart, CRT_FLOW_TARGET, |
1702 | startpc, code.curPc()); |
1703 | } |
1704 | code.resolve(thenExit); |
1705 | result = items.makeStackItem(pt); |
1706 | } |
1707 | |
1708 | public void visitNewClass(JCNewClass tree) { |
1709 | // Enclosing instances or anonymous classes should have been eliminated |
1710 | // by now. |
1711 | assert tree.encl == null && tree.def == null; |
1712 | |
1713 | code.emitop2(new_, makeRef(tree.pos(), tree.type)); |
1714 | code.emitop0(dup); |
1715 | |
1716 | // Generate code for all arguments, where the expected types are |
1717 | // the parameters of the constructor's external type (that is, |
1718 | // any implicit outer instance appears as first parameter). |
1719 | genArgs(tree.args, tree.constructor.externalType(types).getParameterTypes()); |
1720 | |
1721 | items.makeMemberItem(tree.constructor, true).invoke(); |
1722 | result = items.makeStackItem(tree.type); |
1723 | } |
1724 | |
1725 | public void visitNewArray(JCNewArray tree) { |
1726 | if (tree.elems != null) { |
1727 | Type elemtype = types.elemtype(tree.type); |
1728 | loadIntConst(tree.elems.length()); |
1729 | Item arr = makeNewArray(tree.pos(), tree.type, 1); |
1730 | int i = 0; |
1731 | for (List<JCExpression> l = tree.elems; l.nonEmpty(); l = l.tail) { |
1732 | arr.duplicate(); |
1733 | loadIntConst(i); |
1734 | i++; |
1735 | genExpr(l.head, elemtype).load(); |
1736 | items.makeIndexedItem(elemtype).store(); |
1737 | } |
1738 | result = arr; |
1739 | } else { |
1740 | for (List<JCExpression> l = tree.dims; l.nonEmpty(); l = l.tail) { |
1741 | genExpr(l.head, syms.intType).load(); |
1742 | } |
1743 | result = makeNewArray(tree.pos(), tree.type, tree.dims.length()); |
1744 | } |
1745 | } |
1746 | //where |
1747 | /** Generate code to create an array with given element type and number |
1748 | * of dimensions. |
1749 | */ |
1750 | Item makeNewArray(DiagnosticPosition pos, Type type, int ndims) { |
1751 | Type elemtype = types.elemtype(type); |
1752 | if (types.dimensions(elemtype) + ndims > ClassFile.MAX_DIMENSIONS) { |
1753 | log.error(pos, "limit.dimensions"); |
1754 | nerrs++; |
1755 | } |
1756 | int elemcode = Code.arraycode(elemtype); |
1757 | if (elemcode == 0 || (elemcode == 1 && ndims == 1)) { |
1758 | code.emitAnewarray(makeRef(pos, elemtype), type); |
1759 | } else if (elemcode == 1) { |
1760 | code.emitMultianewarray(ndims, makeRef(pos, type), type); |
1761 | } else { |
1762 | code.emitNewarray(elemcode, type); |
1763 | } |
1764 | return items.makeStackItem(type); |
1765 | } |
1766 | |
1767 | public void visitParens(JCParens tree) { |
1768 | result = genExpr(tree.expr, tree.expr.type); |
1769 | } |
1770 | |
1771 | public void visitAssign(JCAssign tree) { |
1772 | Item l = genExpr(tree.lhs, tree.lhs.type); |
1773 | genExpr(tree.rhs, tree.lhs.type).load(); |
1774 | result = items.makeAssignItem(l); |
1775 | } |
1776 | |
1777 | public void visitAssignop(JCAssignOp tree) { |
1778 | OperatorSymbol operator = (OperatorSymbol) tree.operator; |
1779 | Item l; |
1780 | if (operator.opcode == string_add) { |
1781 | // Generate code to make a string buffer |
1782 | makeStringBuffer(tree.pos()); |
1783 | |
1784 | // Generate code for first string, possibly save one |
1785 | // copy under buffer |
1786 | l = genExpr(tree.lhs, tree.lhs.type); |
1787 | if (l.width() > 0) { |
1788 | code.emitop0(dup_x1 + 3 * (l.width() - 1)); |
1789 | } |
1790 | |
1791 | // Load first string and append to buffer. |
1792 | l.load(); |
1793 | appendString(tree.lhs); |
1794 | |
1795 | // Append all other strings to buffer. |
1796 | appendStrings(tree.rhs); |
1797 | |
1798 | // Convert buffer to string. |
1799 | bufferToString(tree.pos()); |
1800 | } else { |
1801 | // Generate code for first expression |
1802 | l = genExpr(tree.lhs, tree.lhs.type); |
1803 | |
1804 | // If we have an increment of -32768 to +32767 of a local |
1805 | // int variable we can use an incr instruction instead of |
1806 | // proceeding further. |
1807 | if ((tree.getTag() == JCTree.PLUS_ASG || tree.getTag() == JCTree.MINUS_ASG) && |
1808 | l instanceof LocalItem && |
1809 | tree.lhs.type.tag <= INT && |
1810 | tree.rhs.type.tag <= INT && |
1811 | tree.rhs.type.constValue() != null) { |
1812 | int ival = ((Number) tree.rhs.type.constValue()).intValue(); |
1813 | if (tree.getTag() == JCTree.MINUS_ASG) ival = -ival; |
1814 | ((LocalItem)l).incr(ival); |
1815 | result = l; |
1816 | return; |
1817 | } |
1818 | // Otherwise, duplicate expression, load one copy |
1819 | // and complete binary operation. |
1820 | l.duplicate(); |
1821 | l.coerce(operator.type.getParameterTypes().head).load(); |
1822 | completeBinop(tree.lhs, tree.rhs, operator).coerce(tree.lhs.type); |
1823 | } |
1824 | result = items.makeAssignItem(l); |
1825 | } |
1826 | |
1827 | public void visitUnary(JCUnary tree) { |
1828 | OperatorSymbol operator = (OperatorSymbol)tree.operator; |
1829 | if (tree.getTag() == JCTree.NOT) { |
1830 | CondItem od = genCond(tree.arg, false); |
1831 | result = od.negate(); |
1832 | } else { |
1833 | Item od = genExpr(tree.arg, operator.type.getParameterTypes().head); |
1834 | switch (tree.getTag()) { |
1835 | case JCTree.POS: |
1836 | result = od.load(); |
1837 | break; |
1838 | case JCTree.NEG: |
1839 | result = od.load(); |
1840 | code.emitop0(operator.opcode); |
1841 | break; |
1842 | case JCTree.COMPL: |
1843 | result = od.load(); |
1844 | emitMinusOne(od.typecode); |
1845 | code.emitop0(operator.opcode); |
1846 | break; |
1847 | case JCTree.PREINC: case JCTree.PREDEC: |
1848 | od.duplicate(); |
1849 | if (od instanceof LocalItem && |
1850 | (operator.opcode == iadd || operator.opcode == isub)) { |
1851 | ((LocalItem)od).incr(tree.getTag() == JCTree.PREINC ? 1 : -1); |
1852 | result = od; |
1853 | } else { |
1854 | od.load(); |
1855 | code.emitop0(one(od.typecode)); |
1856 | code.emitop0(operator.opcode); |
1857 | // Perform narrowing primitive conversion if byte, |
1858 | // char, or short. Fix for 4304655. |
1859 | if (od.typecode != INTcode && |
1860 | Code.truncate(od.typecode) == INTcode) |
1861 | code.emitop0(int2byte + od.typecode - BYTEcode); |
1862 | result = items.makeAssignItem(od); |
1863 | } |
1864 | break; |
1865 | case JCTree.POSTINC: case JCTree.POSTDEC: |
1866 | od.duplicate(); |
1867 | if (od instanceof LocalItem && |
1868 | (operator.opcode == iadd || operator.opcode == isub)) { |
1869 | Item res = od.load(); |
1870 | ((LocalItem)od).incr(tree.getTag() == JCTree.POSTINC ? 1 : -1); |
1871 | result = res; |
1872 | } else { |
1873 | Item res = od.load(); |
1874 | od.stash(od.typecode); |
1875 | code.emitop0(one(od.typecode)); |
1876 | code.emitop0(operator.opcode); |
1877 | // Perform narrowing primitive conversion if byte, |
1878 | // char, or short. Fix for 4304655. |
1879 | if (od.typecode != INTcode && |
1880 | Code.truncate(od.typecode) == INTcode) |
1881 | code.emitop0(int2byte + od.typecode - BYTEcode); |
1882 | od.store(); |
1883 | result = res; |
1884 | } |
1885 | break; |
1886 | case JCTree.NULLCHK: |
1887 | result = od.load(); |
1888 | code.emitop0(dup); |
1889 | genNullCheck(tree.pos()); |
1890 | break; |
1891 | default: |
1892 | assert false; |
1893 | } |
1894 | } |
1895 | } |
1896 | |
1897 | /** Generate a null check from the object value at stack top. */ |
1898 | private void genNullCheck(DiagnosticPosition pos) { |
1899 | callMethod(pos, syms.objectType, names.getClass, |
1900 | List.<Type>nil(), false); |
1901 | code.emitop0(pop); |
1902 | } |
1903 | |
1904 | public void visitBinary(JCBinary tree) { |
1905 | OperatorSymbol operator = (OperatorSymbol)tree.operator; |
1906 | if (operator.opcode == string_add) { |
1907 | // Create a string buffer. |
1908 | makeStringBuffer(tree.pos()); |
1909 | // Append all strings to buffer. |
1910 | appendStrings(tree); |
1911 | // Convert buffer to string. |
1912 | bufferToString(tree.pos()); |
1913 | result = items.makeStackItem(syms.stringType); |
1914 | } else if (tree.getTag() == JCTree.AND) { |
1915 | CondItem lcond = genCond(tree.lhs, CRT_FLOW_CONTROLLER); |
1916 | if (!lcond.isFalse()) { |
1917 | Chain falseJumps = lcond.jumpFalse(); |
1918 | code.resolve(lcond.trueJumps); |
1919 | CondItem rcond = genCond(tree.rhs, CRT_FLOW_TARGET); |
1920 | result = items. |
1921 | makeCondItem(rcond.opcode, |
1922 | rcond.trueJumps, |
1923 | code.mergeChains(falseJumps, |
1924 | rcond.falseJumps)); |
1925 | } else { |
1926 | result = lcond; |
1927 | } |
1928 | } else if (tree.getTag() == JCTree.OR) { |
1929 | CondItem lcond = genCond(tree.lhs, CRT_FLOW_CONTROLLER); |
1930 | if (!lcond.isTrue()) { |
1931 | Chain trueJumps = lcond.jumpTrue(); |
1932 | code.resolve(lcond.falseJumps); |
1933 | CondItem rcond = genCond(tree.rhs, CRT_FLOW_TARGET); |
1934 | result = items. |
1935 | makeCondItem(rcond.opcode, |
1936 | code.mergeChains(trueJumps, rcond.trueJumps), |
1937 | rcond.falseJumps); |
1938 | } else { |
1939 | result = lcond; |
1940 | } |
1941 | } else { |
1942 | Item od = genExpr(tree.lhs, operator.type.getParameterTypes().head); |
1943 | od.load(); |
1944 | result = completeBinop(tree.lhs, tree.rhs, operator); |
1945 | } |
1946 | } |
1947 | //where |
1948 | /** Make a new string buffer. |
1949 | */ |
1950 | void makeStringBuffer(DiagnosticPosition pos) { |
1951 | code.emitop2(new_, makeRef(pos, stringBufferType)); |
1952 | code.emitop0(dup); |
1953 | callMethod( |
1954 | pos, stringBufferType, names.init, List.<Type>nil(), false); |
1955 | } |
1956 | |
1957 | /** Append value (on tos) to string buffer (on tos - 1). |
1958 | */ |
1959 | void appendString(JCTree tree) { |
1960 | Type t = tree.type.baseType(); |
1961 | if (t.tag > lastBaseTag && t.tsym != syms.stringType.tsym) { |
1962 | t = syms.objectType; |
1963 | } |
1964 | items.makeMemberItem(getStringBufferAppend(tree, t), false).invoke(); |
1965 | } |
1966 | Symbol getStringBufferAppend(JCTree tree, Type t) { |
1967 | assert t.constValue() == null; |
1968 | Symbol method = stringBufferAppend.get(t); |
1969 | if (method == null) { |
1970 | method = rs.resolveInternalMethod(tree.pos(), |
1971 | attrEnv, |
1972 | stringBufferType, |
1973 | names.append, |
1974 | List.of(t), |
1975 | null); |
1976 | stringBufferAppend.put(t, method); |
1977 | } |
1978 | return method; |
1979 | } |
1980 | |
1981 | /** Add all strings in tree to string buffer. |
1982 | */ |
1983 | void appendStrings(JCTree tree) { |
1984 | tree = TreeInfo.skipParens(tree); |
1985 | if (tree.getTag() == JCTree.PLUS && tree.type.constValue() == null) { |
1986 | JCBinary op = (JCBinary) tree; |
1987 | if (op.operator.kind == MTH && |
1988 | ((OperatorSymbol) op.operator).opcode == string_add) { |
1989 | appendStrings(op.lhs); |
1990 | appendStrings(op.rhs); |
1991 | return; |
1992 | } |
1993 | } |
1994 | genExpr(tree, tree.type).load(); |
1995 | appendString(tree); |
1996 | } |
1997 | |
1998 | /** Convert string buffer on tos to string. |
1999 | */ |
2000 | void bufferToString(DiagnosticPosition pos) { |
2001 | callMethod( |
2002 | pos, |
2003 | stringBufferType, |
2004 | names.toString, |
2005 | List.<Type>nil(), |
2006 | false); |
2007 | } |
2008 | |
2009 | /** Complete generating code for operation, with left operand |
2010 | * already on stack. |
2011 | * @param lhs The tree representing the left operand. |
2012 | * @param rhs The tree representing the right operand. |
2013 | * @param operator The operator symbol. |
2014 | */ |
2015 | Item completeBinop(JCTree lhs, JCTree rhs, OperatorSymbol operator) { |
2016 | MethodType optype = (MethodType)operator.type; |
2017 | int opcode = operator.opcode; |
2018 | if (opcode >= if_icmpeq && opcode <= if_icmple && |
2019 | rhs.type.constValue() instanceof Number && |
2020 | ((Number) rhs.type.constValue()).intValue() == 0) { |
2021 | opcode = opcode + (ifeq - if_icmpeq); |
2022 | } else if (opcode >= if_acmpeq && opcode <= if_acmpne && |
2023 | TreeInfo.isNull(rhs)) { |
2024 | opcode = opcode + (if_acmp_null - if_acmpeq); |
2025 | } else { |
2026 | // The expected type of the right operand is |
2027 | // the second parameter type of the operator, except for |
2028 | // shifts with long shiftcount, where we convert the opcode |
2029 | // to a short shift and the expected type to int. |
2030 | Type rtype = operator.erasure(types).getParameterTypes().tail.head; |
2031 | if (opcode >= ishll && opcode <= lushrl) { |
2032 | opcode = opcode + (ishl - ishll); |
2033 | rtype = syms.intType; |
2034 | } |
2035 | // Generate code for right operand and load. |
2036 | genExpr(rhs, rtype).load(); |
2037 | // If there are two consecutive opcode instructions, |
2038 | // emit the first now. |
2039 | if (opcode >= (1 << preShift)) { |
2040 | code.emitop0(opcode >> preShift); |
2041 | opcode = opcode & 0xFF; |
2042 | } |
2043 | } |
2044 | if (opcode >= ifeq && opcode <= if_acmpne || |
2045 | opcode == if_acmp_null || opcode == if_acmp_nonnull) { |
2046 | return items.makeCondItem(opcode); |
2047 | } else { |
2048 | code.emitop0(opcode); |
2049 | return items.makeStackItem(optype.restype); |
2050 | } |
2051 | } |
2052 | |
2053 | public void visitTypeCast(JCTypeCast tree) { |
2054 | result = genExpr(tree.expr, tree.clazz.type).load(); |
2055 | // Additional code is only needed if we cast to a reference type |
2056 | // which is not statically a supertype of the expression's type. |
2057 | // For basic types, the coerce(...) in genExpr(...) will do |
2058 | // the conversion. |
2059 | if (tree.clazz.type.tag > lastBaseTag && |
2060 | types.asSuper(tree.expr.type, tree.clazz.type.tsym) == null) { |
2061 | code.emitop2(checkcast, makeRef(tree.pos(), tree.clazz.type)); |
2062 | } |
2063 | } |
2064 | |
2065 | public void visitWildcard(JCWildcard tree) { |
2066 | throw new AssertionError(this.getClass().getName()); |
2067 | } |
2068 | |
2069 | public void visitTypeTest(JCInstanceOf tree) { |
2070 | genExpr(tree.expr, tree.expr.type).load(); |
2071 | code.emitop2(instanceof_, makeRef(tree.pos(), tree.clazz.type)); |
2072 | result = items.makeStackItem(syms.booleanType); |
2073 | } |
2074 | |
2075 | public void visitIndexed(JCArrayAccess tree) { |
2076 | genExpr(tree.indexed, tree.indexed.type).load(); |
2077 | genExpr(tree.index, syms.intType).load(); |
2078 | result = items.makeIndexedItem(tree.type); |
2079 | } |
2080 | |
2081 | public void visitIdent(JCIdent tree) { |
2082 | Symbol sym = tree.sym; |
2083 | if (tree.name == names._this || tree.name == names._super) { |
2084 | Item res = tree.name == names._this |
2085 | ? items.makeThisItem() |
2086 | : items.makeSuperItem(); |
2087 | if (sym.kind == MTH) { |
2088 | // Generate code to address the constructor. |
2089 | res.load(); |
2090 | res = items.makeMemberItem(sym, true); |
2091 | } |
2092 | result = res; |
2093 | } else if (sym.kind == VAR && sym.owner.kind == MTH) { |
2094 | result = items.makeLocalItem((VarSymbol)sym); |
2095 | } else if ((sym.flags() & STATIC) != 0) { |
2096 | if (!isAccessSuper(env.enclMethod)) |
2097 | sym = binaryQualifier(sym, env.enclClass.type); |
2098 | result = items.makeStaticItem(sym); |
2099 | } else { |
2100 | items.makeThisItem().load(); |
2101 | sym = binaryQualifier(sym, env.enclClass.type); |
2102 | result = items.makeMemberItem(sym, (sym.flags() & PRIVATE) != 0); |
2103 | } |
2104 | } |
2105 | |
2106 | public void visitSelect(JCFieldAccess tree) { |
2107 | Symbol sym = tree.sym; |
2108 | |
2109 | if (tree.name == names._class) { |
2110 | assert target.hasClassLiterals(); |
2111 | code.emitop2(ldc2, makeRef(tree.pos(), tree.selected.type)); |
2112 | result = items.makeStackItem(pt); |
2113 | return; |
2114 | } |
2115 | |
2116 | Symbol ssym = TreeInfo.symbol(tree.selected); |
2117 | |
2118 | // Are we selecting via super? |
2119 | boolean selectSuper = |
2120 | ssym != null && (ssym.kind == TYP || ssym.name == names._super); |
2121 | |
2122 | // Are we accessing a member of the superclass in an access method |
2123 | // resulting from a qualified super? |
2124 | boolean accessSuper = isAccessSuper(env.enclMethod); |
2125 | |
2126 | Item base = (selectSuper) |
2127 | ? items.makeSuperItem() |
2128 | : genExpr(tree.selected, tree.selected.type); |
2129 | |
2130 | if (sym.kind == VAR && ((VarSymbol) sym).getConstValue() != null) { |
2131 | // We are seeing a variable that is constant but its selecting |
2132 | // expression is not. |
2133 | if ((sym.flags() & STATIC) != 0) { |
2134 | if (!selectSuper && (ssym == null || ssym.kind != TYP)) |
2135 | base = base.load(); |
2136 | base.drop(); |
2137 | } else { |
2138 | base.load(); |
2139 | genNullCheck(tree.selected.pos()); |
2140 | } |
2141 | result = items. |
2142 | makeImmediateItem(sym.type, ((VarSymbol) sym).getConstValue()); |
2143 | } else { |
2144 | if (!accessSuper) |
2145 | sym = binaryQualifier(sym, tree.selected.type); |
2146 | if ((sym.flags() & STATIC) != 0) { |
2147 | if (!selectSuper && (ssym == null || ssym.kind != TYP)) |
2148 | base = base.load(); |
2149 | base.drop(); |
2150 | result = items.makeStaticItem(sym); |
2151 | } else { |
2152 | base.load(); |
2153 | if (sym == syms.lengthVar) { |
2154 | code.emitop0(arraylength); |
2155 | result = items.makeStackItem(syms.intType); |
2156 | } else { |
2157 | result = items. |
2158 | makeMemberItem(sym, |
2159 | (sym.flags() & PRIVATE) != 0 || |
2160 | selectSuper || accessSuper); |
2161 | } |
2162 | } |
2163 | } |
2164 | } |
2165 | |
2166 | public void visitLiteral(JCLiteral tree) { |
2167 | if (tree.type.tag == TypeTags.BOT) { |
2168 | code.emitop0(aconst_null); |
2169 | if (types.dimensions(pt) > 1) { |
2170 | code.emitop2(checkcast, makeRef(tree.pos(), pt)); |
2171 | result = items.makeStackItem(pt); |
2172 | } else { |
2173 | result = items.makeStackItem(tree.type); |
2174 | } |
2175 | } |
2176 | else |
2177 | result = items.makeImmediateItem(tree.type, tree.value); |
2178 | } |
2179 | |
2180 | public void visitLetExpr(LetExpr tree) { |
2181 | int limit = code.nextreg; |
2182 | genStats(tree.defs, env); |
2183 | result = genExpr(tree.expr, tree.expr.type).load(); |
2184 | code.endScopes(limit); |
2185 | } |
2186 | |
2187 | /* ************************************************************************ |
2188 | * main method |
2189 | *************************************************************************/ |
2190 | |
2191 | /** Generate code for a class definition. |
2192 | * @param env The attribution environment that belongs to the |
2193 | * outermost class containing this class definition. |
2194 | * We need this for resolving some additional symbols. |
2195 | * @param cdef The tree representing the class definition. |
2196 | * @return True if code is generated with no errors. |
2197 | */ |
2198 | public boolean genClass(Env<AttrContext> env, JCClassDecl cdef) { |
2199 | try { |
2200 | attrEnv = env; |
2201 | ClassSymbol c = cdef.sym; |
2202 | this.toplevel = env.toplevel; |
2203 | this.endPositions = toplevel.endPositions; |
2204 | // If this is a class definition requiring Miranda methods, |
2205 | // add them. |
2206 | if (generateIproxies && |
2207 | (c.flags() & (INTERFACE|ABSTRACT)) == ABSTRACT |
2208 | && !allowGenerics // no Miranda methods available with generics |
2209 | ) |
2210 | implementInterfaceMethods(c); |
2211 | cdef.defs = normalizeDefs(cdef.defs, c); |
2212 | c.pool = pool; |
2213 | pool.reset(); |
2214 | Env<GenContext> localEnv = |
2215 | new Env<GenContext>(cdef, new GenContext()); |
2216 | localEnv.toplevel = env.toplevel; |
2217 | localEnv.enclClass = cdef; |
2218 | for (List<JCTree> l = cdef.defs; l.nonEmpty(); l = l.tail) { |
2219 | genDef(l.head, localEnv); |
2220 | } |
2221 | if (pool.numEntries() > Pool.MAX_ENTRIES) { |
2222 | log.error(cdef.pos(), "limit.pool"); |
2223 | nerrs++; |
2224 | } |
2225 | if (nerrs != 0) { |
2226 | // if errors, discard code |
2227 | for (List<JCTree> l = cdef.defs; l.nonEmpty(); l = l.tail) { |
2228 | if (l.head.getTag() == JCTree.METHODDEF) |
2229 | ((JCMethodDecl) l.head).sym.code = null; |
2230 | } |
2231 | } |
2232 | cdef.defs = List.nil(); // discard trees |
2233 | return nerrs == 0; |
2234 | } finally { |
2235 | // note: this method does NOT support recursion. |
2236 | attrEnv = null; |
2237 | this.env = null; |
2238 | toplevel = null; |
2239 | endPositions = null; |
2240 | nerrs = 0; |
2241 | } |
2242 | } |
2243 | |
2244 | /* ************************************************************************ |
2245 | * Auxiliary classes |
2246 | *************************************************************************/ |
2247 | |
2248 | /** An abstract class for finalizer generation. |
2249 | */ |
2250 | abstract class GenFinalizer { |
2251 | /** Generate code to clean up when unwinding. */ |
2252 | abstract void gen(); |
2253 | |
2254 | /** Generate code to clean up at last. */ |
2255 | abstract void genLast(); |
2256 | |
2257 | /** Does this finalizer have some nontrivial cleanup to perform? */ |
2258 | boolean hasFinalizer() { return true; } |
2259 | } |
2260 | |
2261 | /** code generation contexts, |
2262 | * to be used as type parameter for environments. |
2263 | */ |
2264 | static class GenContext { |
2265 | |
2266 | /** A chain for all unresolved jumps that exit the current environment. |
2267 | */ |
2268 | Chain exit = null; |
2269 | |
2270 | /** A chain for all unresolved jumps that continue in the |
2271 | * current environment. |
2272 | */ |
2273 | Chain cont = null; |
2274 | |
2275 | /** A closure that generates the finalizer of the current environment. |
2276 | * Only set for Synchronized and Try contexts. |
2277 | */ |
2278 | GenFinalizer finalize = null; |
2279 | |
2280 | /** Is this a switch statement? If so, allocate registers |
2281 | * even when the variable declaration is unreachable. |
2282 | */ |
2283 | boolean isSwitch = false; |
2284 | |
2285 | /** A list buffer containing all gaps in the finalizer range, |
2286 | * where a catch all exception should not apply. |
2287 | */ |
2288 | ListBuffer<Integer> gaps = null; |
2289 | |
2290 | /** Add given chain to exit chain. |
2291 | */ |
2292 | void addExit(Chain c) { |
2293 | exit = Code.mergeChains(c, exit); |
2294 | } |
2295 | |
2296 | /** Add given chain to cont chain. |
2297 | */ |
2298 | void addCont(Chain c) { |
2299 | cont = Code.mergeChains(c, cont); |
2300 | } |
2301 | } |
2302 | } |