|
1 /* |
|
2 * Copyright (c) 1999, 2014, Oracle and/or its affiliates. All rights reserved. |
|
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
|
4 * |
|
5 * This code is free software; you can redistribute it and/or modify it |
|
6 * under the terms of the GNU General Public License version 2 only, as |
|
7 * published by the Free Software Foundation. Oracle designates this |
|
8 * particular file as subject to the "Classpath" exception as provided |
|
9 * by Oracle in the LICENSE file that accompanied this code. |
|
10 * |
|
11 * This code is distributed in the hope that it will be useful, but WITHOUT |
|
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
14 * version 2 for more details (a copy is included in the LICENSE file that |
|
15 * accompanied this code). |
|
16 * |
|
17 * You should have received a copy of the GNU General Public License version |
|
18 * 2 along with this work; if not, write to the Free Software Foundation, |
|
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
20 * |
|
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
|
22 * or visit www.oracle.com if you need additional information or have any |
|
23 * questions. |
|
24 */ |
|
25 |
|
26 package com.sun.tools.javac.jvm; |
|
27 |
|
28 import java.util.*; |
|
29 |
|
30 import com.sun.tools.javac.util.*; |
|
31 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; |
|
32 import com.sun.tools.javac.util.List; |
|
33 import com.sun.tools.javac.code.*; |
|
34 import com.sun.tools.javac.code.Attribute.TypeCompound; |
|
35 import com.sun.tools.javac.code.Symbol.VarSymbol; |
|
36 import com.sun.tools.javac.comp.*; |
|
37 import com.sun.tools.javac.tree.*; |
|
38 |
|
39 import com.sun.tools.javac.code.Symbol.*; |
|
40 import com.sun.tools.javac.code.Type.*; |
|
41 import com.sun.tools.javac.jvm.Code.*; |
|
42 import com.sun.tools.javac.jvm.Items.*; |
|
43 import com.sun.tools.javac.tree.EndPosTable; |
|
44 import com.sun.tools.javac.tree.JCTree.*; |
|
45 |
|
46 import static com.sun.tools.javac.code.Flags.*; |
|
47 import static com.sun.tools.javac.code.Kinds.*; |
|
48 import static com.sun.tools.javac.code.TypeTag.*; |
|
49 import static com.sun.tools.javac.jvm.ByteCodes.*; |
|
50 import static com.sun.tools.javac.jvm.CRTFlags.*; |
|
51 import static com.sun.tools.javac.main.Option.*; |
|
52 import static com.sun.tools.javac.tree.JCTree.Tag.*; |
|
53 |
|
54 /** This pass maps flat Java (i.e. without inner classes) to bytecodes. |
|
55 * |
|
56 * <p><b>This is NOT part of any supported API. |
|
57 * If you write code that depends on this, you do so at your own risk. |
|
58 * This code and its internal interfaces are subject to change or |
|
59 * deletion without notice.</b> |
|
60 */ |
|
61 public class Gen extends JCTree.Visitor { |
|
62 protected static final Context.Key<Gen> genKey = |
|
63 new Context.Key<Gen>(); |
|
64 |
|
65 private final Log log; |
|
66 private final Symtab syms; |
|
67 private final Check chk; |
|
68 private final Resolve rs; |
|
69 private final TreeMaker make; |
|
70 private final Names names; |
|
71 private final Target target; |
|
72 private final Type stringBufferType; |
|
73 private final Map<Type,Symbol> stringBufferAppend; |
|
74 private Name accessDollar; |
|
75 private final Types types; |
|
76 private final Lower lower; |
|
77 |
|
78 /** Switch: GJ mode? |
|
79 */ |
|
80 private final boolean allowGenerics; |
|
81 |
|
82 /** Set when Miranda method stubs are to be generated. */ |
|
83 private final boolean generateIproxies; |
|
84 |
|
85 /** Format of stackmap tables to be generated. */ |
|
86 private final Code.StackMapFormat stackMap; |
|
87 |
|
88 /** A type that serves as the expected type for all method expressions. |
|
89 */ |
|
90 private final Type methodType; |
|
91 |
|
92 public static Gen instance(Context context) { |
|
93 Gen instance = context.get(genKey); |
|
94 if (instance == null) |
|
95 instance = new Gen(context); |
|
96 return instance; |
|
97 } |
|
98 |
|
99 /** Constant pool, reset by genClass. |
|
100 */ |
|
101 private Pool pool; |
|
102 |
|
103 /** LVTRanges info. |
|
104 */ |
|
105 private LVTRanges lvtRanges; |
|
106 |
|
107 private final boolean typeAnnoAsserts; |
|
108 |
|
109 protected Gen(Context context) { |
|
110 context.put(genKey, this); |
|
111 |
|
112 names = Names.instance(context); |
|
113 log = Log.instance(context); |
|
114 syms = Symtab.instance(context); |
|
115 chk = Check.instance(context); |
|
116 rs = Resolve.instance(context); |
|
117 make = TreeMaker.instance(context); |
|
118 target = Target.instance(context); |
|
119 types = Types.instance(context); |
|
120 methodType = new MethodType(null, null, null, syms.methodClass); |
|
121 allowGenerics = Source.instance(context).allowGenerics(); |
|
122 stringBufferType = target.useStringBuilder() |
|
123 ? syms.stringBuilderType |
|
124 : syms.stringBufferType; |
|
125 stringBufferAppend = new HashMap<Type,Symbol>(); |
|
126 accessDollar = names. |
|
127 fromString("access" + target.syntheticNameChar()); |
|
128 lower = Lower.instance(context); |
|
129 |
|
130 Options options = Options.instance(context); |
|
131 lineDebugInfo = |
|
132 options.isUnset(G_CUSTOM) || |
|
133 options.isSet(G_CUSTOM, "lines"); |
|
134 varDebugInfo = |
|
135 options.isUnset(G_CUSTOM) |
|
136 ? options.isSet(G) |
|
137 : options.isSet(G_CUSTOM, "vars"); |
|
138 if (varDebugInfo) { |
|
139 lvtRanges = LVTRanges.instance(context); |
|
140 } |
|
141 genCrt = options.isSet(XJCOV); |
|
142 debugCode = options.isSet("debugcode"); |
|
143 allowInvokedynamic = target.hasInvokedynamic() || options.isSet("invokedynamic"); |
|
144 pool = new Pool(types); |
|
145 typeAnnoAsserts = options.isSet("TypeAnnotationAsserts"); |
|
146 |
|
147 generateIproxies = |
|
148 target.requiresIproxy() || |
|
149 options.isSet("miranda"); |
|
150 |
|
151 if (target.generateStackMapTable()) { |
|
152 // ignore cldc because we cannot have both stackmap formats |
|
153 this.stackMap = StackMapFormat.JSR202; |
|
154 } else { |
|
155 if (target.generateCLDCStackmap()) { |
|
156 this.stackMap = StackMapFormat.CLDC; |
|
157 } else { |
|
158 this.stackMap = StackMapFormat.NONE; |
|
159 } |
|
160 } |
|
161 |
|
162 // by default, avoid jsr's for simple finalizers |
|
163 int setjsrlimit = 50; |
|
164 String jsrlimitString = options.get("jsrlimit"); |
|
165 if (jsrlimitString != null) { |
|
166 try { |
|
167 setjsrlimit = Integer.parseInt(jsrlimitString); |
|
168 } catch (NumberFormatException ex) { |
|
169 // ignore ill-formed numbers for jsrlimit |
|
170 } |
|
171 } |
|
172 this.jsrlimit = setjsrlimit; |
|
173 this.useJsrLocally = false; // reset in visitTry |
|
174 } |
|
175 |
|
176 /** Switches |
|
177 */ |
|
178 private final boolean lineDebugInfo; |
|
179 private final boolean varDebugInfo; |
|
180 private final boolean genCrt; |
|
181 private final boolean debugCode; |
|
182 private final boolean allowInvokedynamic; |
|
183 |
|
184 /** Default limit of (approximate) size of finalizer to inline. |
|
185 * Zero means always use jsr. 100 or greater means never use |
|
186 * jsr. |
|
187 */ |
|
188 private final int jsrlimit; |
|
189 |
|
190 /** True if jsr is used. |
|
191 */ |
|
192 private boolean useJsrLocally; |
|
193 |
|
194 /** Code buffer, set by genMethod. |
|
195 */ |
|
196 private Code code; |
|
197 |
|
198 /** Items structure, set by genMethod. |
|
199 */ |
|
200 private Items items; |
|
201 |
|
202 /** Environment for symbol lookup, set by genClass |
|
203 */ |
|
204 private Env<AttrContext> attrEnv; |
|
205 |
|
206 /** The top level tree. |
|
207 */ |
|
208 private JCCompilationUnit toplevel; |
|
209 |
|
210 /** The number of code-gen errors in this class. |
|
211 */ |
|
212 private int nerrs = 0; |
|
213 |
|
214 /** An object containing mappings of syntax trees to their |
|
215 * ending source positions. |
|
216 */ |
|
217 EndPosTable endPosTable; |
|
218 |
|
219 /** Generate code to load an integer constant. |
|
220 * @param n The integer to be loaded. |
|
221 */ |
|
222 void loadIntConst(int n) { |
|
223 items.makeImmediateItem(syms.intType, n).load(); |
|
224 } |
|
225 |
|
226 /** The opcode that loads a zero constant of a given type code. |
|
227 * @param tc The given type code (@see ByteCode). |
|
228 */ |
|
229 public static int zero(int tc) { |
|
230 switch(tc) { |
|
231 case INTcode: case BYTEcode: case SHORTcode: case CHARcode: |
|
232 return iconst_0; |
|
233 case LONGcode: |
|
234 return lconst_0; |
|
235 case FLOATcode: |
|
236 return fconst_0; |
|
237 case DOUBLEcode: |
|
238 return dconst_0; |
|
239 default: |
|
240 throw new AssertionError("zero"); |
|
241 } |
|
242 } |
|
243 |
|
244 /** The opcode that loads a one constant of a given type code. |
|
245 * @param tc The given type code (@see ByteCode). |
|
246 */ |
|
247 public static int one(int tc) { |
|
248 return zero(tc) + 1; |
|
249 } |
|
250 |
|
251 /** Generate code to load -1 of the given type code (either int or long). |
|
252 * @param tc The given type code (@see ByteCode). |
|
253 */ |
|
254 void emitMinusOne(int tc) { |
|
255 if (tc == LONGcode) { |
|
256 items.makeImmediateItem(syms.longType, new Long(-1)).load(); |
|
257 } else { |
|
258 code.emitop0(iconst_m1); |
|
259 } |
|
260 } |
|
261 |
|
262 /** Construct a symbol to reflect the qualifying type that should |
|
263 * appear in the byte code as per JLS 13.1. |
|
264 * |
|
265 * For {@literal target >= 1.2}: Clone a method with the qualifier as owner (except |
|
266 * for those cases where we need to work around VM bugs). |
|
267 * |
|
268 * For {@literal target <= 1.1}: If qualified variable or method is defined in a |
|
269 * non-accessible class, clone it with the qualifier class as owner. |
|
270 * |
|
271 * @param sym The accessed symbol |
|
272 * @param site The qualifier's type. |
|
273 */ |
|
274 Symbol binaryQualifier(Symbol sym, Type site) { |
|
275 |
|
276 if (site.hasTag(ARRAY)) { |
|
277 if (sym == syms.lengthVar || |
|
278 sym.owner != syms.arrayClass) |
|
279 return sym; |
|
280 // array clone can be qualified by the array type in later targets |
|
281 Symbol qualifier = target.arrayBinaryCompatibility() |
|
282 ? new ClassSymbol(Flags.PUBLIC, site.tsym.name, |
|
283 site, syms.noSymbol) |
|
284 : syms.objectType.tsym; |
|
285 return sym.clone(qualifier); |
|
286 } |
|
287 |
|
288 if (sym.owner == site.tsym || |
|
289 (sym.flags() & (STATIC | SYNTHETIC)) == (STATIC | SYNTHETIC)) { |
|
290 return sym; |
|
291 } |
|
292 if (!target.obeyBinaryCompatibility()) |
|
293 return rs.isAccessible(attrEnv, (TypeSymbol)sym.owner) |
|
294 ? sym |
|
295 : sym.clone(site.tsym); |
|
296 |
|
297 if (!target.interfaceFieldsBinaryCompatibility()) { |
|
298 if ((sym.owner.flags() & INTERFACE) != 0 && sym.kind == VAR) |
|
299 return sym; |
|
300 } |
|
301 |
|
302 // leave alone methods inherited from Object |
|
303 // JLS 13.1. |
|
304 if (sym.owner == syms.objectType.tsym) |
|
305 return sym; |
|
306 |
|
307 if (!target.interfaceObjectOverridesBinaryCompatibility()) { |
|
308 if ((sym.owner.flags() & INTERFACE) != 0 && |
|
309 syms.objectType.tsym.members().lookup(sym.name).scope != null) |
|
310 return sym; |
|
311 } |
|
312 |
|
313 return sym.clone(site.tsym); |
|
314 } |
|
315 |
|
316 /** Insert a reference to given type in the constant pool, |
|
317 * checking for an array with too many dimensions; |
|
318 * return the reference's index. |
|
319 * @param type The type for which a reference is inserted. |
|
320 */ |
|
321 int makeRef(DiagnosticPosition pos, Type type) { |
|
322 checkDimension(pos, type); |
|
323 if (type.isAnnotated()) { |
|
324 // Treat annotated types separately - we don't want |
|
325 // to collapse all of them - at least for annotated |
|
326 // exceptions. |
|
327 // TODO: review this. |
|
328 return pool.put((Object)type); |
|
329 } else { |
|
330 return pool.put(type.hasTag(CLASS) ? (Object)type.tsym : (Object)type); |
|
331 } |
|
332 } |
|
333 |
|
334 /** Check if the given type is an array with too many dimensions. |
|
335 */ |
|
336 private void checkDimension(DiagnosticPosition pos, Type t) { |
|
337 switch (t.getTag()) { |
|
338 case METHOD: |
|
339 checkDimension(pos, t.getReturnType()); |
|
340 for (List<Type> args = t.getParameterTypes(); args.nonEmpty(); args = args.tail) |
|
341 checkDimension(pos, args.head); |
|
342 break; |
|
343 case ARRAY: |
|
344 if (types.dimensions(t) > ClassFile.MAX_DIMENSIONS) { |
|
345 log.error(pos, "limit.dimensions"); |
|
346 nerrs++; |
|
347 } |
|
348 break; |
|
349 default: |
|
350 break; |
|
351 } |
|
352 } |
|
353 |
|
354 /** Create a tempory variable. |
|
355 * @param type The variable's type. |
|
356 */ |
|
357 LocalItem makeTemp(Type type) { |
|
358 VarSymbol v = new VarSymbol(Flags.SYNTHETIC, |
|
359 names.empty, |
|
360 type, |
|
361 env.enclMethod.sym); |
|
362 code.newLocal(v); |
|
363 return items.makeLocalItem(v); |
|
364 } |
|
365 |
|
366 /** Generate code to call a non-private method or constructor. |
|
367 * @param pos Position to be used for error reporting. |
|
368 * @param site The type of which the method is a member. |
|
369 * @param name The method's name. |
|
370 * @param argtypes The method's argument types. |
|
371 * @param isStatic A flag that indicates whether we call a |
|
372 * static or instance method. |
|
373 */ |
|
374 void callMethod(DiagnosticPosition pos, |
|
375 Type site, Name name, List<Type> argtypes, |
|
376 boolean isStatic) { |
|
377 Symbol msym = rs. |
|
378 resolveInternalMethod(pos, attrEnv, site, name, argtypes, null); |
|
379 if (isStatic) items.makeStaticItem(msym).invoke(); |
|
380 else items.makeMemberItem(msym, name == names.init).invoke(); |
|
381 } |
|
382 |
|
383 /** Is the given method definition an access method |
|
384 * resulting from a qualified super? This is signified by an odd |
|
385 * access code. |
|
386 */ |
|
387 private boolean isAccessSuper(JCMethodDecl enclMethod) { |
|
388 return |
|
389 (enclMethod.mods.flags & SYNTHETIC) != 0 && |
|
390 isOddAccessName(enclMethod.name); |
|
391 } |
|
392 |
|
393 /** Does given name start with "access$" and end in an odd digit? |
|
394 */ |
|
395 private boolean isOddAccessName(Name name) { |
|
396 return |
|
397 name.startsWith(accessDollar) && |
|
398 (name.getByteAt(name.getByteLength() - 1) & 1) == 1; |
|
399 } |
|
400 |
|
401 /* ************************************************************************ |
|
402 * Non-local exits |
|
403 *************************************************************************/ |
|
404 |
|
405 /** Generate code to invoke the finalizer associated with given |
|
406 * environment. |
|
407 * Any calls to finalizers are appended to the environments `cont' chain. |
|
408 * Mark beginning of gap in catch all range for finalizer. |
|
409 */ |
|
410 void genFinalizer(Env<GenContext> env) { |
|
411 if (code.isAlive() && env.info.finalize != null) |
|
412 env.info.finalize.gen(); |
|
413 } |
|
414 |
|
415 /** Generate code to call all finalizers of structures aborted by |
|
416 * a non-local |
|
417 * exit. Return target environment of the non-local exit. |
|
418 * @param target The tree representing the structure that's aborted |
|
419 * @param env The environment current at the non-local exit. |
|
420 */ |
|
421 Env<GenContext> unwind(JCTree target, Env<GenContext> env) { |
|
422 Env<GenContext> env1 = env; |
|
423 while (true) { |
|
424 genFinalizer(env1); |
|
425 if (env1.tree == target) break; |
|
426 env1 = env1.next; |
|
427 } |
|
428 return env1; |
|
429 } |
|
430 |
|
431 /** Mark end of gap in catch-all range for finalizer. |
|
432 * @param env the environment which might contain the finalizer |
|
433 * (if it does, env.info.gaps != null). |
|
434 */ |
|
435 void endFinalizerGap(Env<GenContext> env) { |
|
436 if (env.info.gaps != null && env.info.gaps.length() % 2 == 1) |
|
437 env.info.gaps.append(code.curCP()); |
|
438 } |
|
439 |
|
440 /** Mark end of all gaps in catch-all ranges for finalizers of environments |
|
441 * lying between, and including to two environments. |
|
442 * @param from the most deeply nested environment to mark |
|
443 * @param to the least deeply nested environment to mark |
|
444 */ |
|
445 void endFinalizerGaps(Env<GenContext> from, Env<GenContext> to) { |
|
446 Env<GenContext> last = null; |
|
447 while (last != to) { |
|
448 endFinalizerGap(from); |
|
449 last = from; |
|
450 from = from.next; |
|
451 } |
|
452 } |
|
453 |
|
454 /** Do any of the structures aborted by a non-local exit have |
|
455 * finalizers that require an empty stack? |
|
456 * @param target The tree representing the structure that's aborted |
|
457 * @param env The environment current at the non-local exit. |
|
458 */ |
|
459 boolean hasFinally(JCTree target, Env<GenContext> env) { |
|
460 while (env.tree != target) { |
|
461 if (env.tree.hasTag(TRY) && env.info.finalize.hasFinalizer()) |
|
462 return true; |
|
463 env = env.next; |
|
464 } |
|
465 return false; |
|
466 } |
|
467 |
|
468 /* ************************************************************************ |
|
469 * Normalizing class-members. |
|
470 *************************************************************************/ |
|
471 |
|
472 /** Distribute member initializer code into constructors and {@code <clinit>} |
|
473 * method. |
|
474 * @param defs The list of class member declarations. |
|
475 * @param c The enclosing class. |
|
476 */ |
|
477 List<JCTree> normalizeDefs(List<JCTree> defs, ClassSymbol c) { |
|
478 ListBuffer<JCStatement> initCode = new ListBuffer<JCStatement>(); |
|
479 ListBuffer<Attribute.TypeCompound> initTAs = new ListBuffer<Attribute.TypeCompound>(); |
|
480 ListBuffer<JCStatement> clinitCode = new ListBuffer<JCStatement>(); |
|
481 ListBuffer<Attribute.TypeCompound> clinitTAs = new ListBuffer<Attribute.TypeCompound>(); |
|
482 ListBuffer<JCTree> methodDefs = new ListBuffer<JCTree>(); |
|
483 // Sort definitions into three listbuffers: |
|
484 // - initCode for instance initializers |
|
485 // - clinitCode for class initializers |
|
486 // - methodDefs for method definitions |
|
487 for (List<JCTree> l = defs; l.nonEmpty(); l = l.tail) { |
|
488 JCTree def = l.head; |
|
489 switch (def.getTag()) { |
|
490 case BLOCK: |
|
491 JCBlock block = (JCBlock)def; |
|
492 if ((block.flags & STATIC) != 0) |
|
493 clinitCode.append(block); |
|
494 else |
|
495 initCode.append(block); |
|
496 break; |
|
497 case METHODDEF: |
|
498 methodDefs.append(def); |
|
499 break; |
|
500 case VARDEF: |
|
501 JCVariableDecl vdef = (JCVariableDecl) def; |
|
502 VarSymbol sym = vdef.sym; |
|
503 checkDimension(vdef.pos(), sym.type); |
|
504 if (vdef.init != null) { |
|
505 if ((sym.flags() & STATIC) == 0) { |
|
506 // Always initialize instance variables. |
|
507 JCStatement init = make.at(vdef.pos()). |
|
508 Assignment(sym, vdef.init); |
|
509 initCode.append(init); |
|
510 endPosTable.replaceTree(vdef, init); |
|
511 initTAs.addAll(getAndRemoveNonFieldTAs(sym)); |
|
512 } else if (sym.getConstValue() == null) { |
|
513 // Initialize class (static) variables only if |
|
514 // they are not compile-time constants. |
|
515 JCStatement init = make.at(vdef.pos). |
|
516 Assignment(sym, vdef.init); |
|
517 clinitCode.append(init); |
|
518 endPosTable.replaceTree(vdef, init); |
|
519 clinitTAs.addAll(getAndRemoveNonFieldTAs(sym)); |
|
520 } else { |
|
521 checkStringConstant(vdef.init.pos(), sym.getConstValue()); |
|
522 } |
|
523 } |
|
524 break; |
|
525 default: |
|
526 Assert.error(); |
|
527 } |
|
528 } |
|
529 // Insert any instance initializers into all constructors. |
|
530 if (initCode.length() != 0) { |
|
531 List<JCStatement> inits = initCode.toList(); |
|
532 initTAs.addAll(c.getInitTypeAttributes()); |
|
533 List<Attribute.TypeCompound> initTAlist = initTAs.toList(); |
|
534 for (JCTree t : methodDefs) { |
|
535 normalizeMethod((JCMethodDecl)t, inits, initTAlist); |
|
536 } |
|
537 } |
|
538 // If there are class initializers, create a <clinit> method |
|
539 // that contains them as its body. |
|
540 if (clinitCode.length() != 0) { |
|
541 MethodSymbol clinit = new MethodSymbol( |
|
542 STATIC | (c.flags() & STRICTFP), |
|
543 names.clinit, |
|
544 new MethodType( |
|
545 List.<Type>nil(), syms.voidType, |
|
546 List.<Type>nil(), syms.methodClass), |
|
547 c); |
|
548 c.members().enter(clinit); |
|
549 List<JCStatement> clinitStats = clinitCode.toList(); |
|
550 JCBlock block = make.at(clinitStats.head.pos()).Block(0, clinitStats); |
|
551 block.endpos = TreeInfo.endPos(clinitStats.last()); |
|
552 methodDefs.append(make.MethodDef(clinit, block)); |
|
553 |
|
554 if (!clinitTAs.isEmpty()) |
|
555 clinit.appendUniqueTypeAttributes(clinitTAs.toList()); |
|
556 if (!c.getClassInitTypeAttributes().isEmpty()) |
|
557 clinit.appendUniqueTypeAttributes(c.getClassInitTypeAttributes()); |
|
558 } |
|
559 // Return all method definitions. |
|
560 return methodDefs.toList(); |
|
561 } |
|
562 |
|
563 private List<Attribute.TypeCompound> getAndRemoveNonFieldTAs(VarSymbol sym) { |
|
564 List<TypeCompound> tas = sym.getRawTypeAttributes(); |
|
565 ListBuffer<Attribute.TypeCompound> fieldTAs = new ListBuffer<Attribute.TypeCompound>(); |
|
566 ListBuffer<Attribute.TypeCompound> nonfieldTAs = new ListBuffer<Attribute.TypeCompound>(); |
|
567 for (TypeCompound ta : tas) { |
|
568 if (ta.getPosition().type == TargetType.FIELD) { |
|
569 fieldTAs.add(ta); |
|
570 } else { |
|
571 if (typeAnnoAsserts) { |
|
572 Assert.error("Type annotation does not have a valid positior"); |
|
573 } |
|
574 |
|
575 nonfieldTAs.add(ta); |
|
576 } |
|
577 } |
|
578 sym.setTypeAttributes(fieldTAs.toList()); |
|
579 return nonfieldTAs.toList(); |
|
580 } |
|
581 |
|
582 /** Check a constant value and report if it is a string that is |
|
583 * too large. |
|
584 */ |
|
585 private void checkStringConstant(DiagnosticPosition pos, Object constValue) { |
|
586 if (nerrs != 0 || // only complain about a long string once |
|
587 constValue == null || |
|
588 !(constValue instanceof String) || |
|
589 ((String)constValue).length() < Pool.MAX_STRING_LENGTH) |
|
590 return; |
|
591 log.error(pos, "limit.string"); |
|
592 nerrs++; |
|
593 } |
|
594 |
|
595 /** Insert instance initializer code into initial constructor. |
|
596 * @param md The tree potentially representing a |
|
597 * constructor's definition. |
|
598 * @param initCode The list of instance initializer statements. |
|
599 * @param initTAs Type annotations from the initializer expression. |
|
600 */ |
|
601 void normalizeMethod(JCMethodDecl md, List<JCStatement> initCode, List<TypeCompound> initTAs) { |
|
602 if (md.name == names.init && TreeInfo.isInitialConstructor(md)) { |
|
603 // We are seeing a constructor that does not call another |
|
604 // constructor of the same class. |
|
605 List<JCStatement> stats = md.body.stats; |
|
606 ListBuffer<JCStatement> newstats = new ListBuffer<JCStatement>(); |
|
607 |
|
608 if (stats.nonEmpty()) { |
|
609 // Copy initializers of synthetic variables generated in |
|
610 // the translation of inner classes. |
|
611 while (TreeInfo.isSyntheticInit(stats.head)) { |
|
612 newstats.append(stats.head); |
|
613 stats = stats.tail; |
|
614 } |
|
615 // Copy superclass constructor call |
|
616 newstats.append(stats.head); |
|
617 stats = stats.tail; |
|
618 // Copy remaining synthetic initializers. |
|
619 while (stats.nonEmpty() && |
|
620 TreeInfo.isSyntheticInit(stats.head)) { |
|
621 newstats.append(stats.head); |
|
622 stats = stats.tail; |
|
623 } |
|
624 // Now insert the initializer code. |
|
625 newstats.appendList(initCode); |
|
626 // And copy all remaining statements. |
|
627 while (stats.nonEmpty()) { |
|
628 newstats.append(stats.head); |
|
629 stats = stats.tail; |
|
630 } |
|
631 } |
|
632 md.body.stats = newstats.toList(); |
|
633 if (md.body.endpos == Position.NOPOS) |
|
634 md.body.endpos = TreeInfo.endPos(md.body.stats.last()); |
|
635 |
|
636 md.sym.appendUniqueTypeAttributes(initTAs); |
|
637 } |
|
638 } |
|
639 |
|
640 /* ******************************************************************** |
|
641 * Adding miranda methods |
|
642 *********************************************************************/ |
|
643 |
|
644 /** Add abstract methods for all methods defined in one of |
|
645 * the interfaces of a given class, |
|
646 * provided they are not already implemented in the class. |
|
647 * |
|
648 * @param c The class whose interfaces are searched for methods |
|
649 * for which Miranda methods should be added. |
|
650 */ |
|
651 void implementInterfaceMethods(ClassSymbol c) { |
|
652 implementInterfaceMethods(c, c); |
|
653 } |
|
654 |
|
655 /** Add abstract methods for all methods defined in one of |
|
656 * the interfaces of a given class, |
|
657 * provided they are not already implemented in the class. |
|
658 * |
|
659 * @param c The class whose interfaces are searched for methods |
|
660 * for which Miranda methods should be added. |
|
661 * @param site The class in which a definition may be needed. |
|
662 */ |
|
663 void implementInterfaceMethods(ClassSymbol c, ClassSymbol site) { |
|
664 for (List<Type> l = types.interfaces(c.type); l.nonEmpty(); l = l.tail) { |
|
665 ClassSymbol i = (ClassSymbol)l.head.tsym; |
|
666 for (Scope.Entry e = i.members().elems; |
|
667 e != null; |
|
668 e = e.sibling) |
|
669 { |
|
670 if (e.sym.kind == MTH && (e.sym.flags() & STATIC) == 0) |
|
671 { |
|
672 MethodSymbol absMeth = (MethodSymbol)e.sym; |
|
673 MethodSymbol implMeth = absMeth.binaryImplementation(site, types); |
|
674 if (implMeth == null) |
|
675 addAbstractMethod(site, absMeth); |
|
676 else if ((implMeth.flags() & IPROXY) != 0) |
|
677 adjustAbstractMethod(site, implMeth, absMeth); |
|
678 } |
|
679 } |
|
680 implementInterfaceMethods(i, site); |
|
681 } |
|
682 } |
|
683 |
|
684 /** Add an abstract methods to a class |
|
685 * which implicitly implements a method defined in some interface |
|
686 * implemented by the class. These methods are called "Miranda methods". |
|
687 * Enter the newly created method into its enclosing class scope. |
|
688 * Note that it is not entered into the class tree, as the emitter |
|
689 * doesn't need to see it there to emit an abstract method. |
|
690 * |
|
691 * @param c The class to which the Miranda method is added. |
|
692 * @param m The interface method symbol for which a Miranda method |
|
693 * is added. |
|
694 */ |
|
695 private void addAbstractMethod(ClassSymbol c, |
|
696 MethodSymbol m) { |
|
697 MethodSymbol absMeth = new MethodSymbol( |
|
698 m.flags() | IPROXY | SYNTHETIC, m.name, |
|
699 m.type, // was c.type.memberType(m), but now only !generics supported |
|
700 c); |
|
701 c.members().enter(absMeth); // add to symbol table |
|
702 } |
|
703 |
|
704 private void adjustAbstractMethod(ClassSymbol c, |
|
705 MethodSymbol pm, |
|
706 MethodSymbol im) { |
|
707 MethodType pmt = (MethodType)pm.type; |
|
708 Type imt = types.memberType(c.type, im); |
|
709 pmt.thrown = chk.intersect(pmt.getThrownTypes(), imt.getThrownTypes()); |
|
710 } |
|
711 |
|
712 /* ************************************************************************ |
|
713 * Traversal methods |
|
714 *************************************************************************/ |
|
715 |
|
716 /** Visitor argument: The current environment. |
|
717 */ |
|
718 Env<GenContext> env; |
|
719 |
|
720 /** Visitor argument: The expected type (prototype). |
|
721 */ |
|
722 Type pt; |
|
723 |
|
724 /** Visitor result: The item representing the computed value. |
|
725 */ |
|
726 Item result; |
|
727 |
|
728 /** Visitor method: generate code for a definition, catching and reporting |
|
729 * any completion failures. |
|
730 * @param tree The definition to be visited. |
|
731 * @param env The environment current at the definition. |
|
732 */ |
|
733 public void genDef(JCTree tree, Env<GenContext> env) { |
|
734 Env<GenContext> prevEnv = this.env; |
|
735 try { |
|
736 this.env = env; |
|
737 tree.accept(this); |
|
738 } catch (CompletionFailure ex) { |
|
739 chk.completionError(tree.pos(), ex); |
|
740 } finally { |
|
741 this.env = prevEnv; |
|
742 } |
|
743 } |
|
744 |
|
745 /** Derived visitor method: check whether CharacterRangeTable |
|
746 * should be emitted, if so, put a new entry into CRTable |
|
747 * and call method to generate bytecode. |
|
748 * If not, just call method to generate bytecode. |
|
749 * @see #genStat(JCTree, Env) |
|
750 * |
|
751 * @param tree The tree to be visited. |
|
752 * @param env The environment to use. |
|
753 * @param crtFlags The CharacterRangeTable flags |
|
754 * indicating type of the entry. |
|
755 */ |
|
756 public void genStat(JCTree tree, Env<GenContext> env, int crtFlags) { |
|
757 if (!genCrt) { |
|
758 genStat(tree, env); |
|
759 return; |
|
760 } |
|
761 int startpc = code.curCP(); |
|
762 genStat(tree, env); |
|
763 if (tree.hasTag(Tag.BLOCK)) crtFlags |= CRT_BLOCK; |
|
764 code.crt.put(tree, crtFlags, startpc, code.curCP()); |
|
765 } |
|
766 |
|
767 /** Derived visitor method: generate code for a statement. |
|
768 */ |
|
769 public void genStat(JCTree tree, Env<GenContext> env) { |
|
770 if (code.isAlive()) { |
|
771 code.statBegin(tree.pos); |
|
772 genDef(tree, env); |
|
773 } else if (env.info.isSwitch && tree.hasTag(VARDEF)) { |
|
774 // variables whose declarations are in a switch |
|
775 // can be used even if the decl is unreachable. |
|
776 code.newLocal(((JCVariableDecl) tree).sym); |
|
777 } |
|
778 } |
|
779 |
|
780 /** Derived visitor method: check whether CharacterRangeTable |
|
781 * should be emitted, if so, put a new entry into CRTable |
|
782 * and call method to generate bytecode. |
|
783 * If not, just call method to generate bytecode. |
|
784 * @see #genStats(List, Env) |
|
785 * |
|
786 * @param trees The list of trees to be visited. |
|
787 * @param env The environment to use. |
|
788 * @param crtFlags The CharacterRangeTable flags |
|
789 * indicating type of the entry. |
|
790 */ |
|
791 public void genStats(List<JCStatement> trees, Env<GenContext> env, int crtFlags) { |
|
792 if (!genCrt) { |
|
793 genStats(trees, env); |
|
794 return; |
|
795 } |
|
796 if (trees.length() == 1) { // mark one statement with the flags |
|
797 genStat(trees.head, env, crtFlags | CRT_STATEMENT); |
|
798 } else { |
|
799 int startpc = code.curCP(); |
|
800 genStats(trees, env); |
|
801 code.crt.put(trees, crtFlags, startpc, code.curCP()); |
|
802 } |
|
803 } |
|
804 |
|
805 /** Derived visitor method: generate code for a list of statements. |
|
806 */ |
|
807 public void genStats(List<? extends JCTree> trees, Env<GenContext> env) { |
|
808 for (List<? extends JCTree> l = trees; l.nonEmpty(); l = l.tail) |
|
809 genStat(l.head, env, CRT_STATEMENT); |
|
810 } |
|
811 |
|
812 /** Derived visitor method: check whether CharacterRangeTable |
|
813 * should be emitted, if so, put a new entry into CRTable |
|
814 * and call method to generate bytecode. |
|
815 * If not, just call method to generate bytecode. |
|
816 * @see #genCond(JCTree,boolean) |
|
817 * |
|
818 * @param tree The tree to be visited. |
|
819 * @param crtFlags The CharacterRangeTable flags |
|
820 * indicating type of the entry. |
|
821 */ |
|
822 public CondItem genCond(JCTree tree, int crtFlags) { |
|
823 if (!genCrt) return genCond(tree, false); |
|
824 int startpc = code.curCP(); |
|
825 CondItem item = genCond(tree, (crtFlags & CRT_FLOW_CONTROLLER) != 0); |
|
826 code.crt.put(tree, crtFlags, startpc, code.curCP()); |
|
827 return item; |
|
828 } |
|
829 |
|
830 /** Derived visitor method: generate code for a boolean |
|
831 * expression in a control-flow context. |
|
832 * @param _tree The expression to be visited. |
|
833 * @param markBranches The flag to indicate that the condition is |
|
834 * a flow controller so produced conditions |
|
835 * should contain a proper tree to generate |
|
836 * CharacterRangeTable branches for them. |
|
837 */ |
|
838 public CondItem genCond(JCTree _tree, boolean markBranches) { |
|
839 JCTree inner_tree = TreeInfo.skipParens(_tree); |
|
840 if (inner_tree.hasTag(CONDEXPR)) { |
|
841 JCConditional tree = (JCConditional)inner_tree; |
|
842 CondItem cond = genCond(tree.cond, CRT_FLOW_CONTROLLER); |
|
843 if (cond.isTrue()) { |
|
844 code.resolve(cond.trueJumps); |
|
845 CondItem result = genCond(tree.truepart, CRT_FLOW_TARGET); |
|
846 if (markBranches) result.tree = tree.truepart; |
|
847 return result; |
|
848 } |
|
849 if (cond.isFalse()) { |
|
850 code.resolve(cond.falseJumps); |
|
851 CondItem result = genCond(tree.falsepart, CRT_FLOW_TARGET); |
|
852 if (markBranches) result.tree = tree.falsepart; |
|
853 return result; |
|
854 } |
|
855 Chain secondJumps = cond.jumpFalse(); |
|
856 code.resolve(cond.trueJumps); |
|
857 CondItem first = genCond(tree.truepart, CRT_FLOW_TARGET); |
|
858 if (markBranches) first.tree = tree.truepart; |
|
859 Chain falseJumps = first.jumpFalse(); |
|
860 code.resolve(first.trueJumps); |
|
861 Chain trueJumps = code.branch(goto_); |
|
862 code.resolve(secondJumps); |
|
863 CondItem second = genCond(tree.falsepart, CRT_FLOW_TARGET); |
|
864 CondItem result = items.makeCondItem(second.opcode, |
|
865 Code.mergeChains(trueJumps, second.trueJumps), |
|
866 Code.mergeChains(falseJumps, second.falseJumps)); |
|
867 if (markBranches) result.tree = tree.falsepart; |
|
868 return result; |
|
869 } else { |
|
870 CondItem result = genExpr(_tree, syms.booleanType).mkCond(); |
|
871 if (markBranches) result.tree = _tree; |
|
872 return result; |
|
873 } |
|
874 } |
|
875 |
|
876 /** Visitor class for expressions which might be constant expressions. |
|
877 * This class is a subset of TreeScanner. Intended to visit trees pruned by |
|
878 * Lower as long as constant expressions looking for references to any |
|
879 * ClassSymbol. Any such reference will be added to the constant pool so |
|
880 * automated tools can detect class dependencies better. |
|
881 */ |
|
882 class ClassReferenceVisitor extends JCTree.Visitor { |
|
883 |
|
884 @Override |
|
885 public void visitTree(JCTree tree) {} |
|
886 |
|
887 @Override |
|
888 public void visitBinary(JCBinary tree) { |
|
889 tree.lhs.accept(this); |
|
890 tree.rhs.accept(this); |
|
891 } |
|
892 |
|
893 @Override |
|
894 public void visitSelect(JCFieldAccess tree) { |
|
895 if (tree.selected.type.hasTag(CLASS)) { |
|
896 makeRef(tree.selected.pos(), tree.selected.type); |
|
897 } |
|
898 } |
|
899 |
|
900 @Override |
|
901 public void visitIdent(JCIdent tree) { |
|
902 if (tree.sym.owner instanceof ClassSymbol) { |
|
903 pool.put(tree.sym.owner); |
|
904 } |
|
905 } |
|
906 |
|
907 @Override |
|
908 public void visitConditional(JCConditional tree) { |
|
909 tree.cond.accept(this); |
|
910 tree.truepart.accept(this); |
|
911 tree.falsepart.accept(this); |
|
912 } |
|
913 |
|
914 @Override |
|
915 public void visitUnary(JCUnary tree) { |
|
916 tree.arg.accept(this); |
|
917 } |
|
918 |
|
919 @Override |
|
920 public void visitParens(JCParens tree) { |
|
921 tree.expr.accept(this); |
|
922 } |
|
923 |
|
924 @Override |
|
925 public void visitTypeCast(JCTypeCast tree) { |
|
926 tree.expr.accept(this); |
|
927 } |
|
928 } |
|
929 |
|
930 private ClassReferenceVisitor classReferenceVisitor = new ClassReferenceVisitor(); |
|
931 |
|
932 /** Visitor method: generate code for an expression, catching and reporting |
|
933 * any completion failures. |
|
934 * @param tree The expression to be visited. |
|
935 * @param pt The expression's expected type (proto-type). |
|
936 */ |
|
937 public Item genExpr(JCTree tree, Type pt) { |
|
938 Type prevPt = this.pt; |
|
939 try { |
|
940 if (tree.type.constValue() != null) { |
|
941 // Short circuit any expressions which are constants |
|
942 tree.accept(classReferenceVisitor); |
|
943 checkStringConstant(tree.pos(), tree.type.constValue()); |
|
944 result = items.makeImmediateItem(tree.type, tree.type.constValue()); |
|
945 } else { |
|
946 this.pt = pt; |
|
947 tree.accept(this); |
|
948 } |
|
949 return result.coerce(pt); |
|
950 } catch (CompletionFailure ex) { |
|
951 chk.completionError(tree.pos(), ex); |
|
952 code.state.stacksize = 1; |
|
953 return items.makeStackItem(pt); |
|
954 } finally { |
|
955 this.pt = prevPt; |
|
956 } |
|
957 } |
|
958 |
|
959 /** Derived visitor method: generate code for a list of method arguments. |
|
960 * @param trees The argument expressions to be visited. |
|
961 * @param pts The expression's expected types (i.e. the formal parameter |
|
962 * types of the invoked method). |
|
963 */ |
|
964 public void genArgs(List<JCExpression> trees, List<Type> pts) { |
|
965 for (List<JCExpression> l = trees; l.nonEmpty(); l = l.tail) { |
|
966 genExpr(l.head, pts.head).load(); |
|
967 pts = pts.tail; |
|
968 } |
|
969 // require lists be of same length |
|
970 Assert.check(pts.isEmpty()); |
|
971 } |
|
972 |
|
973 /* ************************************************************************ |
|
974 * Visitor methods for statements and definitions |
|
975 *************************************************************************/ |
|
976 |
|
977 /** Thrown when the byte code size exceeds limit. |
|
978 */ |
|
979 public static class CodeSizeOverflow extends RuntimeException { |
|
980 private static final long serialVersionUID = 0; |
|
981 public CodeSizeOverflow() {} |
|
982 } |
|
983 |
|
984 public void visitMethodDef(JCMethodDecl tree) { |
|
985 // Create a new local environment that points pack at method |
|
986 // definition. |
|
987 Env<GenContext> localEnv = env.dup(tree); |
|
988 localEnv.enclMethod = tree; |
|
989 // The expected type of every return statement in this method |
|
990 // is the method's return type. |
|
991 this.pt = tree.sym.erasure(types).getReturnType(); |
|
992 |
|
993 checkDimension(tree.pos(), tree.sym.erasure(types)); |
|
994 genMethod(tree, localEnv, false); |
|
995 } |
|
996 //where |
|
997 /** Generate code for a method. |
|
998 * @param tree The tree representing the method definition. |
|
999 * @param env The environment current for the method body. |
|
1000 * @param fatcode A flag that indicates whether all jumps are |
|
1001 * within 32K. We first invoke this method under |
|
1002 * the assumption that fatcode == false, i.e. all |
|
1003 * jumps are within 32K. If this fails, fatcode |
|
1004 * is set to true and we try again. |
|
1005 */ |
|
1006 void genMethod(JCMethodDecl tree, Env<GenContext> env, boolean fatcode) { |
|
1007 MethodSymbol meth = tree.sym; |
|
1008 int extras = 0; |
|
1009 // Count up extra parameters |
|
1010 if (meth.isConstructor()) { |
|
1011 extras++; |
|
1012 if (meth.enclClass().isInner() && |
|
1013 !meth.enclClass().isStatic()) { |
|
1014 extras++; |
|
1015 } |
|
1016 } else if ((tree.mods.flags & STATIC) == 0) { |
|
1017 extras++; |
|
1018 } |
|
1019 // System.err.println("Generating " + meth + " in " + meth.owner); //DEBUG |
|
1020 if (Code.width(types.erasure(env.enclMethod.sym.type).getParameterTypes()) + extras > |
|
1021 ClassFile.MAX_PARAMETERS) { |
|
1022 log.error(tree.pos(), "limit.parameters"); |
|
1023 nerrs++; |
|
1024 } |
|
1025 |
|
1026 else if (tree.body != null) { |
|
1027 // Create a new code structure and initialize it. |
|
1028 int startpcCrt = initCode(tree, env, fatcode); |
|
1029 |
|
1030 try { |
|
1031 genStat(tree.body, env); |
|
1032 } catch (CodeSizeOverflow e) { |
|
1033 // Failed due to code limit, try again with jsr/ret |
|
1034 startpcCrt = initCode(tree, env, fatcode); |
|
1035 genStat(tree.body, env); |
|
1036 } |
|
1037 |
|
1038 if (code.state.stacksize != 0) { |
|
1039 log.error(tree.body.pos(), "stack.sim.error", tree); |
|
1040 throw new AssertionError(); |
|
1041 } |
|
1042 |
|
1043 // If last statement could complete normally, insert a |
|
1044 // return at the end. |
|
1045 if (code.isAlive()) { |
|
1046 code.statBegin(TreeInfo.endPos(tree.body)); |
|
1047 if (env.enclMethod == null || |
|
1048 env.enclMethod.sym.type.getReturnType().hasTag(VOID)) { |
|
1049 code.emitop0(return_); |
|
1050 } else { |
|
1051 // sometime dead code seems alive (4415991); |
|
1052 // generate a small loop instead |
|
1053 int startpc = code.entryPoint(); |
|
1054 CondItem c = items.makeCondItem(goto_); |
|
1055 code.resolve(c.jumpTrue(), startpc); |
|
1056 } |
|
1057 } |
|
1058 if (genCrt) |
|
1059 code.crt.put(tree.body, |
|
1060 CRT_BLOCK, |
|
1061 startpcCrt, |
|
1062 code.curCP()); |
|
1063 |
|
1064 code.endScopes(0); |
|
1065 |
|
1066 // If we exceeded limits, panic |
|
1067 if (code.checkLimits(tree.pos(), log)) { |
|
1068 nerrs++; |
|
1069 return; |
|
1070 } |
|
1071 |
|
1072 // If we generated short code but got a long jump, do it again |
|
1073 // with fatCode = true. |
|
1074 if (!fatcode && code.fatcode) genMethod(tree, env, true); |
|
1075 |
|
1076 // Clean up |
|
1077 if(stackMap == StackMapFormat.JSR202) { |
|
1078 code.lastFrame = null; |
|
1079 code.frameBeforeLast = null; |
|
1080 } |
|
1081 |
|
1082 // Compress exception table |
|
1083 code.compressCatchTable(); |
|
1084 |
|
1085 // Fill in type annotation positions for exception parameters |
|
1086 code.fillExceptionParameterPositions(); |
|
1087 } |
|
1088 } |
|
1089 |
|
1090 private int initCode(JCMethodDecl tree, Env<GenContext> env, boolean fatcode) { |
|
1091 MethodSymbol meth = tree.sym; |
|
1092 |
|
1093 // Create a new code structure. |
|
1094 meth.code = code = new Code(meth, |
|
1095 fatcode, |
|
1096 lineDebugInfo ? toplevel.lineMap : null, |
|
1097 varDebugInfo, |
|
1098 stackMap, |
|
1099 debugCode, |
|
1100 genCrt ? new CRTable(tree, env.toplevel.endPositions) |
|
1101 : null, |
|
1102 syms, |
|
1103 types, |
|
1104 pool, |
|
1105 varDebugInfo ? lvtRanges : null); |
|
1106 items = new Items(pool, code, syms, types); |
|
1107 if (code.debugCode) { |
|
1108 System.err.println(meth + " for body " + tree); |
|
1109 } |
|
1110 |
|
1111 // If method is not static, create a new local variable address |
|
1112 // for `this'. |
|
1113 if ((tree.mods.flags & STATIC) == 0) { |
|
1114 Type selfType = meth.owner.type; |
|
1115 if (meth.isConstructor() && selfType != syms.objectType) |
|
1116 selfType = UninitializedType.uninitializedThis(selfType); |
|
1117 code.setDefined( |
|
1118 code.newLocal( |
|
1119 new VarSymbol(FINAL, names._this, selfType, meth.owner))); |
|
1120 } |
|
1121 |
|
1122 // Mark all parameters as defined from the beginning of |
|
1123 // the method. |
|
1124 for (List<JCVariableDecl> l = tree.params; l.nonEmpty(); l = l.tail) { |
|
1125 checkDimension(l.head.pos(), l.head.sym.type); |
|
1126 code.setDefined(code.newLocal(l.head.sym)); |
|
1127 } |
|
1128 |
|
1129 // Get ready to generate code for method body. |
|
1130 int startpcCrt = genCrt ? code.curCP() : 0; |
|
1131 code.entryPoint(); |
|
1132 |
|
1133 // Suppress initial stackmap |
|
1134 code.pendingStackMap = false; |
|
1135 |
|
1136 return startpcCrt; |
|
1137 } |
|
1138 |
|
1139 public void visitVarDef(JCVariableDecl tree) { |
|
1140 VarSymbol v = tree.sym; |
|
1141 code.newLocal(v); |
|
1142 if (tree.init != null) { |
|
1143 checkStringConstant(tree.init.pos(), v.getConstValue()); |
|
1144 if (v.getConstValue() == null || varDebugInfo) { |
|
1145 genExpr(tree.init, v.erasure(types)).load(); |
|
1146 items.makeLocalItem(v).store(); |
|
1147 } |
|
1148 } |
|
1149 checkDimension(tree.pos(), v.type); |
|
1150 } |
|
1151 |
|
1152 public void visitSkip(JCSkip tree) { |
|
1153 } |
|
1154 |
|
1155 public void visitBlock(JCBlock tree) { |
|
1156 int limit = code.nextreg; |
|
1157 Env<GenContext> localEnv = env.dup(tree, new GenContext()); |
|
1158 genStats(tree.stats, localEnv); |
|
1159 // End the scope of all block-local variables in variable info. |
|
1160 if (!env.tree.hasTag(METHODDEF)) { |
|
1161 code.statBegin(tree.endpos); |
|
1162 code.endScopes(limit); |
|
1163 code.pendingStatPos = Position.NOPOS; |
|
1164 } |
|
1165 } |
|
1166 |
|
1167 public void visitDoLoop(JCDoWhileLoop tree) { |
|
1168 genLoop(tree, tree.body, tree.cond, List.<JCExpressionStatement>nil(), false); |
|
1169 } |
|
1170 |
|
1171 public void visitWhileLoop(JCWhileLoop tree) { |
|
1172 genLoop(tree, tree.body, tree.cond, List.<JCExpressionStatement>nil(), true); |
|
1173 } |
|
1174 |
|
1175 public void visitForLoop(JCForLoop tree) { |
|
1176 int limit = code.nextreg; |
|
1177 genStats(tree.init, env); |
|
1178 genLoop(tree, tree.body, tree.cond, tree.step, true); |
|
1179 code.endScopes(limit); |
|
1180 } |
|
1181 //where |
|
1182 /** Generate code for a loop. |
|
1183 * @param loop The tree representing the loop. |
|
1184 * @param body The loop's body. |
|
1185 * @param cond The loop's controling condition. |
|
1186 * @param step "Step" statements to be inserted at end of |
|
1187 * each iteration. |
|
1188 * @param testFirst True if the loop test belongs before the body. |
|
1189 */ |
|
1190 private void genLoop(JCStatement loop, |
|
1191 JCStatement body, |
|
1192 JCExpression cond, |
|
1193 List<JCExpressionStatement> step, |
|
1194 boolean testFirst) { |
|
1195 Env<GenContext> loopEnv = env.dup(loop, new GenContext()); |
|
1196 int startpc = code.entryPoint(); |
|
1197 if (testFirst) { |
|
1198 CondItem c; |
|
1199 if (cond != null) { |
|
1200 code.statBegin(cond.pos); |
|
1201 c = genCond(TreeInfo.skipParens(cond), CRT_FLOW_CONTROLLER); |
|
1202 } else { |
|
1203 c = items.makeCondItem(goto_); |
|
1204 } |
|
1205 Chain loopDone = c.jumpFalse(); |
|
1206 code.resolve(c.trueJumps); |
|
1207 genStat(body, loopEnv, CRT_STATEMENT | CRT_FLOW_TARGET); |
|
1208 if (varDebugInfo) { |
|
1209 checkLoopLocalVarRangeEnding(loop, body, |
|
1210 LoopLocalVarRangeEndingPoint.BEFORE_STEPS); |
|
1211 } |
|
1212 code.resolve(loopEnv.info.cont); |
|
1213 genStats(step, loopEnv); |
|
1214 if (varDebugInfo) { |
|
1215 checkLoopLocalVarRangeEnding(loop, body, |
|
1216 LoopLocalVarRangeEndingPoint.AFTER_STEPS); |
|
1217 } |
|
1218 code.resolve(code.branch(goto_), startpc); |
|
1219 code.resolve(loopDone); |
|
1220 } else { |
|
1221 genStat(body, loopEnv, CRT_STATEMENT | CRT_FLOW_TARGET); |
|
1222 if (varDebugInfo) { |
|
1223 checkLoopLocalVarRangeEnding(loop, body, |
|
1224 LoopLocalVarRangeEndingPoint.BEFORE_STEPS); |
|
1225 } |
|
1226 code.resolve(loopEnv.info.cont); |
|
1227 genStats(step, loopEnv); |
|
1228 if (varDebugInfo) { |
|
1229 checkLoopLocalVarRangeEnding(loop, body, |
|
1230 LoopLocalVarRangeEndingPoint.AFTER_STEPS); |
|
1231 } |
|
1232 CondItem c; |
|
1233 if (cond != null) { |
|
1234 code.statBegin(cond.pos); |
|
1235 c = genCond(TreeInfo.skipParens(cond), CRT_FLOW_CONTROLLER); |
|
1236 } else { |
|
1237 c = items.makeCondItem(goto_); |
|
1238 } |
|
1239 code.resolve(c.jumpTrue(), startpc); |
|
1240 code.resolve(c.falseJumps); |
|
1241 } |
|
1242 code.resolve(loopEnv.info.exit); |
|
1243 } |
|
1244 |
|
1245 private enum LoopLocalVarRangeEndingPoint { |
|
1246 BEFORE_STEPS, |
|
1247 AFTER_STEPS, |
|
1248 } |
|
1249 |
|
1250 /** |
|
1251 * Checks whether we have reached an alive range ending point for local |
|
1252 * variables after a loop. |
|
1253 * |
|
1254 * Local variables alive range ending point for loops varies depending |
|
1255 * on the loop type. The range can be closed before or after the code |
|
1256 * for the steps sentences has been generated. |
|
1257 * |
|
1258 * - While loops has no steps so in that case the range is closed just |
|
1259 * after the body of the loop. |
|
1260 * |
|
1261 * - For-like loops may have steps so as long as the steps sentences |
|
1262 * can possibly contain non-synthetic local variables, the alive range |
|
1263 * for local variables must be closed after the steps in this case. |
|
1264 */ |
|
1265 private void checkLoopLocalVarRangeEnding(JCTree loop, JCTree body, |
|
1266 LoopLocalVarRangeEndingPoint endingPoint) { |
|
1267 if (varDebugInfo && lvtRanges.containsKey(code.meth, body)) { |
|
1268 switch (endingPoint) { |
|
1269 case BEFORE_STEPS: |
|
1270 if (!loop.hasTag(FORLOOP)) { |
|
1271 code.closeAliveRanges(body); |
|
1272 } |
|
1273 break; |
|
1274 case AFTER_STEPS: |
|
1275 if (loop.hasTag(FORLOOP)) { |
|
1276 code.closeAliveRanges(body); |
|
1277 } |
|
1278 break; |
|
1279 } |
|
1280 } |
|
1281 } |
|
1282 |
|
1283 public void visitForeachLoop(JCEnhancedForLoop tree) { |
|
1284 throw new AssertionError(); // should have been removed by Lower. |
|
1285 } |
|
1286 |
|
1287 public void visitLabelled(JCLabeledStatement tree) { |
|
1288 Env<GenContext> localEnv = env.dup(tree, new GenContext()); |
|
1289 genStat(tree.body, localEnv, CRT_STATEMENT); |
|
1290 code.resolve(localEnv.info.exit); |
|
1291 } |
|
1292 |
|
1293 public void visitSwitch(JCSwitch tree) { |
|
1294 int limit = code.nextreg; |
|
1295 Assert.check(!tree.selector.type.hasTag(CLASS)); |
|
1296 int startpcCrt = genCrt ? code.curCP() : 0; |
|
1297 Item sel = genExpr(tree.selector, syms.intType); |
|
1298 List<JCCase> cases = tree.cases; |
|
1299 if (cases.isEmpty()) { |
|
1300 // We are seeing: switch <sel> {} |
|
1301 sel.load().drop(); |
|
1302 if (genCrt) |
|
1303 code.crt.put(TreeInfo.skipParens(tree.selector), |
|
1304 CRT_FLOW_CONTROLLER, startpcCrt, code.curCP()); |
|
1305 } else { |
|
1306 // We are seeing a nonempty switch. |
|
1307 sel.load(); |
|
1308 if (genCrt) |
|
1309 code.crt.put(TreeInfo.skipParens(tree.selector), |
|
1310 CRT_FLOW_CONTROLLER, startpcCrt, code.curCP()); |
|
1311 Env<GenContext> switchEnv = env.dup(tree, new GenContext()); |
|
1312 switchEnv.info.isSwitch = true; |
|
1313 |
|
1314 // Compute number of labels and minimum and maximum label values. |
|
1315 // For each case, store its label in an array. |
|
1316 int lo = Integer.MAX_VALUE; // minimum label. |
|
1317 int hi = Integer.MIN_VALUE; // maximum label. |
|
1318 int nlabels = 0; // number of labels. |
|
1319 |
|
1320 int[] labels = new int[cases.length()]; // the label array. |
|
1321 int defaultIndex = -1; // the index of the default clause. |
|
1322 |
|
1323 List<JCCase> l = cases; |
|
1324 for (int i = 0; i < labels.length; i++) { |
|
1325 if (l.head.pat != null) { |
|
1326 int val = ((Number)l.head.pat.type.constValue()).intValue(); |
|
1327 labels[i] = val; |
|
1328 if (val < lo) lo = val; |
|
1329 if (hi < val) hi = val; |
|
1330 nlabels++; |
|
1331 } else { |
|
1332 Assert.check(defaultIndex == -1); |
|
1333 defaultIndex = i; |
|
1334 } |
|
1335 l = l.tail; |
|
1336 } |
|
1337 |
|
1338 // Determine whether to issue a tableswitch or a lookupswitch |
|
1339 // instruction. |
|
1340 long table_space_cost = 4 + ((long) hi - lo + 1); // words |
|
1341 long table_time_cost = 3; // comparisons |
|
1342 long lookup_space_cost = 3 + 2 * (long) nlabels; |
|
1343 long lookup_time_cost = nlabels; |
|
1344 int opcode = |
|
1345 nlabels > 0 && |
|
1346 table_space_cost + 3 * table_time_cost <= |
|
1347 lookup_space_cost + 3 * lookup_time_cost |
|
1348 ? |
|
1349 tableswitch : lookupswitch; |
|
1350 |
|
1351 int startpc = code.curCP(); // the position of the selector operation |
|
1352 code.emitop0(opcode); |
|
1353 code.align(4); |
|
1354 int tableBase = code.curCP(); // the start of the jump table |
|
1355 int[] offsets = null; // a table of offsets for a lookupswitch |
|
1356 code.emit4(-1); // leave space for default offset |
|
1357 if (opcode == tableswitch) { |
|
1358 code.emit4(lo); // minimum label |
|
1359 code.emit4(hi); // maximum label |
|
1360 for (long i = lo; i <= hi; i++) { // leave space for jump table |
|
1361 code.emit4(-1); |
|
1362 } |
|
1363 } else { |
|
1364 code.emit4(nlabels); // number of labels |
|
1365 for (int i = 0; i < nlabels; i++) { |
|
1366 code.emit4(-1); code.emit4(-1); // leave space for lookup table |
|
1367 } |
|
1368 offsets = new int[labels.length]; |
|
1369 } |
|
1370 Code.State stateSwitch = code.state.dup(); |
|
1371 code.markDead(); |
|
1372 |
|
1373 // For each case do: |
|
1374 l = cases; |
|
1375 for (int i = 0; i < labels.length; i++) { |
|
1376 JCCase c = l.head; |
|
1377 l = l.tail; |
|
1378 |
|
1379 int pc = code.entryPoint(stateSwitch); |
|
1380 // Insert offset directly into code or else into the |
|
1381 // offsets table. |
|
1382 if (i != defaultIndex) { |
|
1383 if (opcode == tableswitch) { |
|
1384 code.put4( |
|
1385 tableBase + 4 * (labels[i] - lo + 3), |
|
1386 pc - startpc); |
|
1387 } else { |
|
1388 offsets[i] = pc - startpc; |
|
1389 } |
|
1390 } else { |
|
1391 code.put4(tableBase, pc - startpc); |
|
1392 } |
|
1393 |
|
1394 // Generate code for the statements in this case. |
|
1395 genStats(c.stats, switchEnv, CRT_FLOW_TARGET); |
|
1396 if (varDebugInfo && lvtRanges.containsKey(code.meth, c.stats.last())) { |
|
1397 code.closeAliveRanges(c.stats.last()); |
|
1398 } |
|
1399 } |
|
1400 |
|
1401 // Resolve all breaks. |
|
1402 code.resolve(switchEnv.info.exit); |
|
1403 |
|
1404 // If we have not set the default offset, we do so now. |
|
1405 if (code.get4(tableBase) == -1) { |
|
1406 code.put4(tableBase, code.entryPoint(stateSwitch) - startpc); |
|
1407 } |
|
1408 |
|
1409 if (opcode == tableswitch) { |
|
1410 // Let any unfilled slots point to the default case. |
|
1411 int defaultOffset = code.get4(tableBase); |
|
1412 for (long i = lo; i <= hi; i++) { |
|
1413 int t = (int)(tableBase + 4 * (i - lo + 3)); |
|
1414 if (code.get4(t) == -1) |
|
1415 code.put4(t, defaultOffset); |
|
1416 } |
|
1417 } else { |
|
1418 // Sort non-default offsets and copy into lookup table. |
|
1419 if (defaultIndex >= 0) |
|
1420 for (int i = defaultIndex; i < labels.length - 1; i++) { |
|
1421 labels[i] = labels[i+1]; |
|
1422 offsets[i] = offsets[i+1]; |
|
1423 } |
|
1424 if (nlabels > 0) |
|
1425 qsort2(labels, offsets, 0, nlabels - 1); |
|
1426 for (int i = 0; i < nlabels; i++) { |
|
1427 int caseidx = tableBase + 8 * (i + 1); |
|
1428 code.put4(caseidx, labels[i]); |
|
1429 code.put4(caseidx + 4, offsets[i]); |
|
1430 } |
|
1431 } |
|
1432 } |
|
1433 code.endScopes(limit); |
|
1434 } |
|
1435 //where |
|
1436 /** Sort (int) arrays of keys and values |
|
1437 */ |
|
1438 static void qsort2(int[] keys, int[] values, int lo, int hi) { |
|
1439 int i = lo; |
|
1440 int j = hi; |
|
1441 int pivot = keys[(i+j)/2]; |
|
1442 do { |
|
1443 while (keys[i] < pivot) i++; |
|
1444 while (pivot < keys[j]) j--; |
|
1445 if (i <= j) { |
|
1446 int temp1 = keys[i]; |
|
1447 keys[i] = keys[j]; |
|
1448 keys[j] = temp1; |
|
1449 int temp2 = values[i]; |
|
1450 values[i] = values[j]; |
|
1451 values[j] = temp2; |
|
1452 i++; |
|
1453 j--; |
|
1454 } |
|
1455 } while (i <= j); |
|
1456 if (lo < j) qsort2(keys, values, lo, j); |
|
1457 if (i < hi) qsort2(keys, values, i, hi); |
|
1458 } |
|
1459 |
|
1460 public void visitSynchronized(JCSynchronized tree) { |
|
1461 int limit = code.nextreg; |
|
1462 // Generate code to evaluate lock and save in temporary variable. |
|
1463 final LocalItem lockVar = makeTemp(syms.objectType); |
|
1464 genExpr(tree.lock, tree.lock.type).load().duplicate(); |
|
1465 lockVar.store(); |
|
1466 |
|
1467 // Generate code to enter monitor. |
|
1468 code.emitop0(monitorenter); |
|
1469 code.state.lock(lockVar.reg); |
|
1470 |
|
1471 // Generate code for a try statement with given body, no catch clauses |
|
1472 // in a new environment with the "exit-monitor" operation as finalizer. |
|
1473 final Env<GenContext> syncEnv = env.dup(tree, new GenContext()); |
|
1474 syncEnv.info.finalize = new GenFinalizer() { |
|
1475 void gen() { |
|
1476 genLast(); |
|
1477 Assert.check(syncEnv.info.gaps.length() % 2 == 0); |
|
1478 syncEnv.info.gaps.append(code.curCP()); |
|
1479 } |
|
1480 void genLast() { |
|
1481 if (code.isAlive()) { |
|
1482 lockVar.load(); |
|
1483 code.emitop0(monitorexit); |
|
1484 code.state.unlock(lockVar.reg); |
|
1485 } |
|
1486 } |
|
1487 }; |
|
1488 syncEnv.info.gaps = new ListBuffer<Integer>(); |
|
1489 genTry(tree.body, List.<JCCatch>nil(), syncEnv); |
|
1490 code.endScopes(limit); |
|
1491 } |
|
1492 |
|
1493 public void visitTry(final JCTry tree) { |
|
1494 // Generate code for a try statement with given body and catch clauses, |
|
1495 // in a new environment which calls the finally block if there is one. |
|
1496 final Env<GenContext> tryEnv = env.dup(tree, new GenContext()); |
|
1497 final Env<GenContext> oldEnv = env; |
|
1498 if (!useJsrLocally) { |
|
1499 useJsrLocally = |
|
1500 (stackMap == StackMapFormat.NONE) && |
|
1501 (jsrlimit <= 0 || |
|
1502 jsrlimit < 100 && |
|
1503 estimateCodeComplexity(tree.finalizer)>jsrlimit); |
|
1504 } |
|
1505 tryEnv.info.finalize = new GenFinalizer() { |
|
1506 void gen() { |
|
1507 if (useJsrLocally) { |
|
1508 if (tree.finalizer != null) { |
|
1509 Code.State jsrState = code.state.dup(); |
|
1510 jsrState.push(Code.jsrReturnValue); |
|
1511 tryEnv.info.cont = |
|
1512 new Chain(code.emitJump(jsr), |
|
1513 tryEnv.info.cont, |
|
1514 jsrState); |
|
1515 } |
|
1516 Assert.check(tryEnv.info.gaps.length() % 2 == 0); |
|
1517 tryEnv.info.gaps.append(code.curCP()); |
|
1518 } else { |
|
1519 Assert.check(tryEnv.info.gaps.length() % 2 == 0); |
|
1520 tryEnv.info.gaps.append(code.curCP()); |
|
1521 genLast(); |
|
1522 } |
|
1523 } |
|
1524 void genLast() { |
|
1525 if (tree.finalizer != null) |
|
1526 genStat(tree.finalizer, oldEnv, CRT_BLOCK); |
|
1527 } |
|
1528 boolean hasFinalizer() { |
|
1529 return tree.finalizer != null; |
|
1530 } |
|
1531 }; |
|
1532 tryEnv.info.gaps = new ListBuffer<Integer>(); |
|
1533 genTry(tree.body, tree.catchers, tryEnv); |
|
1534 } |
|
1535 //where |
|
1536 /** Generate code for a try or synchronized statement |
|
1537 * @param body The body of the try or synchronized statement. |
|
1538 * @param catchers The lis of catch clauses. |
|
1539 * @param env the environment current for the body. |
|
1540 */ |
|
1541 void genTry(JCTree body, List<JCCatch> catchers, Env<GenContext> env) { |
|
1542 int limit = code.nextreg; |
|
1543 int startpc = code.curCP(); |
|
1544 Code.State stateTry = code.state.dup(); |
|
1545 genStat(body, env, CRT_BLOCK); |
|
1546 int endpc = code.curCP(); |
|
1547 boolean hasFinalizer = |
|
1548 env.info.finalize != null && |
|
1549 env.info.finalize.hasFinalizer(); |
|
1550 List<Integer> gaps = env.info.gaps.toList(); |
|
1551 code.statBegin(TreeInfo.endPos(body)); |
|
1552 genFinalizer(env); |
|
1553 code.statBegin(TreeInfo.endPos(env.tree)); |
|
1554 Chain exitChain = code.branch(goto_); |
|
1555 if (varDebugInfo && lvtRanges.containsKey(code.meth, body)) { |
|
1556 code.closeAliveRanges(body); |
|
1557 } |
|
1558 endFinalizerGap(env); |
|
1559 if (startpc != endpc) for (List<JCCatch> l = catchers; l.nonEmpty(); l = l.tail) { |
|
1560 // start off with exception on stack |
|
1561 code.entryPoint(stateTry, l.head.param.sym.type); |
|
1562 genCatch(l.head, env, startpc, endpc, gaps); |
|
1563 genFinalizer(env); |
|
1564 if (hasFinalizer || l.tail.nonEmpty()) { |
|
1565 code.statBegin(TreeInfo.endPos(env.tree)); |
|
1566 exitChain = Code.mergeChains(exitChain, |
|
1567 code.branch(goto_)); |
|
1568 } |
|
1569 endFinalizerGap(env); |
|
1570 } |
|
1571 if (hasFinalizer) { |
|
1572 // Create a new register segement to avoid allocating |
|
1573 // the same variables in finalizers and other statements. |
|
1574 code.newRegSegment(); |
|
1575 |
|
1576 // Add a catch-all clause. |
|
1577 |
|
1578 // start off with exception on stack |
|
1579 int catchallpc = code.entryPoint(stateTry, syms.throwableType); |
|
1580 |
|
1581 // Register all exception ranges for catch all clause. |
|
1582 // The range of the catch all clause is from the beginning |
|
1583 // of the try or synchronized block until the present |
|
1584 // code pointer excluding all gaps in the current |
|
1585 // environment's GenContext. |
|
1586 int startseg = startpc; |
|
1587 while (env.info.gaps.nonEmpty()) { |
|
1588 int endseg = env.info.gaps.next().intValue(); |
|
1589 registerCatch(body.pos(), startseg, endseg, |
|
1590 catchallpc, 0); |
|
1591 startseg = env.info.gaps.next().intValue(); |
|
1592 } |
|
1593 code.statBegin(TreeInfo.finalizerPos(env.tree)); |
|
1594 code.markStatBegin(); |
|
1595 |
|
1596 Item excVar = makeTemp(syms.throwableType); |
|
1597 excVar.store(); |
|
1598 genFinalizer(env); |
|
1599 excVar.load(); |
|
1600 registerCatch(body.pos(), startseg, |
|
1601 env.info.gaps.next().intValue(), |
|
1602 catchallpc, 0); |
|
1603 code.emitop0(athrow); |
|
1604 code.markDead(); |
|
1605 |
|
1606 // If there are jsr's to this finalizer, ... |
|
1607 if (env.info.cont != null) { |
|
1608 // Resolve all jsr's. |
|
1609 code.resolve(env.info.cont); |
|
1610 |
|
1611 // Mark statement line number |
|
1612 code.statBegin(TreeInfo.finalizerPos(env.tree)); |
|
1613 code.markStatBegin(); |
|
1614 |
|
1615 // Save return address. |
|
1616 LocalItem retVar = makeTemp(syms.throwableType); |
|
1617 retVar.store(); |
|
1618 |
|
1619 // Generate finalizer code. |
|
1620 env.info.finalize.genLast(); |
|
1621 |
|
1622 // Return. |
|
1623 code.emitop1w(ret, retVar.reg); |
|
1624 code.markDead(); |
|
1625 } |
|
1626 } |
|
1627 // Resolve all breaks. |
|
1628 code.resolve(exitChain); |
|
1629 |
|
1630 code.endScopes(limit); |
|
1631 } |
|
1632 |
|
1633 /** Generate code for a catch clause. |
|
1634 * @param tree The catch clause. |
|
1635 * @param env The environment current in the enclosing try. |
|
1636 * @param startpc Start pc of try-block. |
|
1637 * @param endpc End pc of try-block. |
|
1638 */ |
|
1639 void genCatch(JCCatch tree, |
|
1640 Env<GenContext> env, |
|
1641 int startpc, int endpc, |
|
1642 List<Integer> gaps) { |
|
1643 if (startpc != endpc) { |
|
1644 List<JCExpression> subClauses = TreeInfo.isMultiCatch(tree) ? |
|
1645 ((JCTypeUnion)tree.param.vartype).alternatives : |
|
1646 List.of(tree.param.vartype); |
|
1647 while (gaps.nonEmpty()) { |
|
1648 for (JCExpression subCatch : subClauses) { |
|
1649 int catchType = makeRef(tree.pos(), subCatch.type); |
|
1650 int end = gaps.head.intValue(); |
|
1651 registerCatch(tree.pos(), |
|
1652 startpc, end, code.curCP(), |
|
1653 catchType); |
|
1654 if (subCatch.type.isAnnotated()) { |
|
1655 for (Attribute.TypeCompound tc : |
|
1656 subCatch.type.getAnnotationMirrors()) { |
|
1657 tc.position.type_index = catchType; |
|
1658 } |
|
1659 } |
|
1660 } |
|
1661 gaps = gaps.tail; |
|
1662 startpc = gaps.head.intValue(); |
|
1663 gaps = gaps.tail; |
|
1664 } |
|
1665 if (startpc < endpc) { |
|
1666 for (JCExpression subCatch : subClauses) { |
|
1667 int catchType = makeRef(tree.pos(), subCatch.type); |
|
1668 registerCatch(tree.pos(), |
|
1669 startpc, endpc, code.curCP(), |
|
1670 catchType); |
|
1671 if (subCatch.type.isAnnotated()) { |
|
1672 for (Attribute.TypeCompound tc : |
|
1673 subCatch.type.getAnnotationMirrors()) { |
|
1674 tc.position.type_index = catchType; |
|
1675 } |
|
1676 } |
|
1677 } |
|
1678 } |
|
1679 VarSymbol exparam = tree.param.sym; |
|
1680 code.statBegin(tree.pos); |
|
1681 code.markStatBegin(); |
|
1682 int limit = code.nextreg; |
|
1683 int exlocal = code.newLocal(exparam); |
|
1684 items.makeLocalItem(exparam).store(); |
|
1685 code.statBegin(TreeInfo.firstStatPos(tree.body)); |
|
1686 genStat(tree.body, env, CRT_BLOCK); |
|
1687 code.endScopes(limit); |
|
1688 code.statBegin(TreeInfo.endPos(tree.body)); |
|
1689 } |
|
1690 } |
|
1691 |
|
1692 /** Register a catch clause in the "Exceptions" code-attribute. |
|
1693 */ |
|
1694 void registerCatch(DiagnosticPosition pos, |
|
1695 int startpc, int endpc, |
|
1696 int handler_pc, int catch_type) { |
|
1697 char startpc1 = (char)startpc; |
|
1698 char endpc1 = (char)endpc; |
|
1699 char handler_pc1 = (char)handler_pc; |
|
1700 if (startpc1 == startpc && |
|
1701 endpc1 == endpc && |
|
1702 handler_pc1 == handler_pc) { |
|
1703 code.addCatch(startpc1, endpc1, handler_pc1, |
|
1704 (char)catch_type); |
|
1705 } else { |
|
1706 if (!useJsrLocally && !target.generateStackMapTable()) { |
|
1707 useJsrLocally = true; |
|
1708 throw new CodeSizeOverflow(); |
|
1709 } else { |
|
1710 log.error(pos, "limit.code.too.large.for.try.stmt"); |
|
1711 nerrs++; |
|
1712 } |
|
1713 } |
|
1714 } |
|
1715 |
|
1716 /** Very roughly estimate the number of instructions needed for |
|
1717 * the given tree. |
|
1718 */ |
|
1719 int estimateCodeComplexity(JCTree tree) { |
|
1720 if (tree == null) return 0; |
|
1721 class ComplexityScanner extends TreeScanner { |
|
1722 int complexity = 0; |
|
1723 public void scan(JCTree tree) { |
|
1724 if (complexity > jsrlimit) return; |
|
1725 super.scan(tree); |
|
1726 } |
|
1727 public void visitClassDef(JCClassDecl tree) {} |
|
1728 public void visitDoLoop(JCDoWhileLoop tree) |
|
1729 { super.visitDoLoop(tree); complexity++; } |
|
1730 public void visitWhileLoop(JCWhileLoop tree) |
|
1731 { super.visitWhileLoop(tree); complexity++; } |
|
1732 public void visitForLoop(JCForLoop tree) |
|
1733 { super.visitForLoop(tree); complexity++; } |
|
1734 public void visitSwitch(JCSwitch tree) |
|
1735 { super.visitSwitch(tree); complexity+=5; } |
|
1736 public void visitCase(JCCase tree) |
|
1737 { super.visitCase(tree); complexity++; } |
|
1738 public void visitSynchronized(JCSynchronized tree) |
|
1739 { super.visitSynchronized(tree); complexity+=6; } |
|
1740 public void visitTry(JCTry tree) |
|
1741 { super.visitTry(tree); |
|
1742 if (tree.finalizer != null) complexity+=6; } |
|
1743 public void visitCatch(JCCatch tree) |
|
1744 { super.visitCatch(tree); complexity+=2; } |
|
1745 public void visitConditional(JCConditional tree) |
|
1746 { super.visitConditional(tree); complexity+=2; } |
|
1747 public void visitIf(JCIf tree) |
|
1748 { super.visitIf(tree); complexity+=2; } |
|
1749 // note: for break, continue, and return we don't take unwind() into account. |
|
1750 public void visitBreak(JCBreak tree) |
|
1751 { super.visitBreak(tree); complexity+=1; } |
|
1752 public void visitContinue(JCContinue tree) |
|
1753 { super.visitContinue(tree); complexity+=1; } |
|
1754 public void visitReturn(JCReturn tree) |
|
1755 { super.visitReturn(tree); complexity+=1; } |
|
1756 public void visitThrow(JCThrow tree) |
|
1757 { super.visitThrow(tree); complexity+=1; } |
|
1758 public void visitAssert(JCAssert tree) |
|
1759 { super.visitAssert(tree); complexity+=5; } |
|
1760 public void visitApply(JCMethodInvocation tree) |
|
1761 { super.visitApply(tree); complexity+=2; } |
|
1762 public void visitNewClass(JCNewClass tree) |
|
1763 { scan(tree.encl); scan(tree.args); complexity+=2; } |
|
1764 public void visitNewArray(JCNewArray tree) |
|
1765 { super.visitNewArray(tree); complexity+=5; } |
|
1766 public void visitAssign(JCAssign tree) |
|
1767 { super.visitAssign(tree); complexity+=1; } |
|
1768 public void visitAssignop(JCAssignOp tree) |
|
1769 { super.visitAssignop(tree); complexity+=2; } |
|
1770 public void visitUnary(JCUnary tree) |
|
1771 { complexity+=1; |
|
1772 if (tree.type.constValue() == null) super.visitUnary(tree); } |
|
1773 public void visitBinary(JCBinary tree) |
|
1774 { complexity+=1; |
|
1775 if (tree.type.constValue() == null) super.visitBinary(tree); } |
|
1776 public void visitTypeTest(JCInstanceOf tree) |
|
1777 { super.visitTypeTest(tree); complexity+=1; } |
|
1778 public void visitIndexed(JCArrayAccess tree) |
|
1779 { super.visitIndexed(tree); complexity+=1; } |
|
1780 public void visitSelect(JCFieldAccess tree) |
|
1781 { super.visitSelect(tree); |
|
1782 if (tree.sym.kind == VAR) complexity+=1; } |
|
1783 public void visitIdent(JCIdent tree) { |
|
1784 if (tree.sym.kind == VAR) { |
|
1785 complexity+=1; |
|
1786 if (tree.type.constValue() == null && |
|
1787 tree.sym.owner.kind == TYP) |
|
1788 complexity+=1; |
|
1789 } |
|
1790 } |
|
1791 public void visitLiteral(JCLiteral tree) |
|
1792 { complexity+=1; } |
|
1793 public void visitTree(JCTree tree) {} |
|
1794 public void visitWildcard(JCWildcard tree) { |
|
1795 throw new AssertionError(this.getClass().getName()); |
|
1796 } |
|
1797 } |
|
1798 ComplexityScanner scanner = new ComplexityScanner(); |
|
1799 tree.accept(scanner); |
|
1800 return scanner.complexity; |
|
1801 } |
|
1802 |
|
1803 public void visitIf(JCIf tree) { |
|
1804 int limit = code.nextreg; |
|
1805 Chain thenExit = null; |
|
1806 CondItem c = genCond(TreeInfo.skipParens(tree.cond), |
|
1807 CRT_FLOW_CONTROLLER); |
|
1808 Chain elseChain = c.jumpFalse(); |
|
1809 if (!c.isFalse()) { |
|
1810 code.resolve(c.trueJumps); |
|
1811 genStat(tree.thenpart, env, CRT_STATEMENT | CRT_FLOW_TARGET); |
|
1812 thenExit = code.branch(goto_); |
|
1813 if (varDebugInfo && lvtRanges.containsKey(code.meth, tree.thenpart)) { |
|
1814 code.closeAliveRanges(tree.thenpart, code.cp); |
|
1815 } |
|
1816 } |
|
1817 if (elseChain != null) { |
|
1818 code.resolve(elseChain); |
|
1819 if (tree.elsepart != null) { |
|
1820 genStat(tree.elsepart, env,CRT_STATEMENT | CRT_FLOW_TARGET); |
|
1821 if (varDebugInfo && lvtRanges.containsKey(code.meth, tree.elsepart)) { |
|
1822 code.closeAliveRanges(tree.elsepart); |
|
1823 } |
|
1824 } |
|
1825 } |
|
1826 code.resolve(thenExit); |
|
1827 code.endScopes(limit); |
|
1828 } |
|
1829 |
|
1830 public void visitExec(JCExpressionStatement tree) { |
|
1831 // Optimize x++ to ++x and x-- to --x. |
|
1832 JCExpression e = tree.expr; |
|
1833 switch (e.getTag()) { |
|
1834 case POSTINC: |
|
1835 ((JCUnary) e).setTag(PREINC); |
|
1836 break; |
|
1837 case POSTDEC: |
|
1838 ((JCUnary) e).setTag(PREDEC); |
|
1839 break; |
|
1840 } |
|
1841 genExpr(tree.expr, tree.expr.type).drop(); |
|
1842 } |
|
1843 |
|
1844 public void visitBreak(JCBreak tree) { |
|
1845 Env<GenContext> targetEnv = unwind(tree.target, env); |
|
1846 Assert.check(code.state.stacksize == 0); |
|
1847 targetEnv.info.addExit(code.branch(goto_)); |
|
1848 endFinalizerGaps(env, targetEnv); |
|
1849 } |
|
1850 |
|
1851 public void visitContinue(JCContinue tree) { |
|
1852 Env<GenContext> targetEnv = unwind(tree.target, env); |
|
1853 Assert.check(code.state.stacksize == 0); |
|
1854 targetEnv.info.addCont(code.branch(goto_)); |
|
1855 endFinalizerGaps(env, targetEnv); |
|
1856 } |
|
1857 |
|
1858 public void visitReturn(JCReturn tree) { |
|
1859 int limit = code.nextreg; |
|
1860 final Env<GenContext> targetEnv; |
|
1861 if (tree.expr != null) { |
|
1862 Item r = genExpr(tree.expr, pt).load(); |
|
1863 if (hasFinally(env.enclMethod, env)) { |
|
1864 r = makeTemp(pt); |
|
1865 r.store(); |
|
1866 } |
|
1867 targetEnv = unwind(env.enclMethod, env); |
|
1868 r.load(); |
|
1869 code.emitop0(ireturn + Code.truncate(Code.typecode(pt))); |
|
1870 } else { |
|
1871 /* If we have a statement like: |
|
1872 * |
|
1873 * return; |
|
1874 * |
|
1875 * we need to store the code.pendingStatPos value before generating |
|
1876 * the finalizer. |
|
1877 */ |
|
1878 int tmpPos = code.pendingStatPos; |
|
1879 targetEnv = unwind(env.enclMethod, env); |
|
1880 code.pendingStatPos = tmpPos; |
|
1881 code.emitop0(return_); |
|
1882 } |
|
1883 endFinalizerGaps(env, targetEnv); |
|
1884 code.endScopes(limit); |
|
1885 } |
|
1886 |
|
1887 public void visitThrow(JCThrow tree) { |
|
1888 genExpr(tree.expr, tree.expr.type).load(); |
|
1889 code.emitop0(athrow); |
|
1890 } |
|
1891 |
|
1892 /* ************************************************************************ |
|
1893 * Visitor methods for expressions |
|
1894 *************************************************************************/ |
|
1895 |
|
1896 public void visitApply(JCMethodInvocation tree) { |
|
1897 setTypeAnnotationPositions(tree.pos); |
|
1898 // Generate code for method. |
|
1899 Item m = genExpr(tree.meth, methodType); |
|
1900 // Generate code for all arguments, where the expected types are |
|
1901 // the parameters of the method's external type (that is, any implicit |
|
1902 // outer instance of a super(...) call appears as first parameter). |
|
1903 MethodSymbol msym = (MethodSymbol)TreeInfo.symbol(tree.meth); |
|
1904 genArgs(tree.args, |
|
1905 msym.externalType(types).getParameterTypes()); |
|
1906 if (!msym.isDynamic()) { |
|
1907 code.statBegin(tree.pos); |
|
1908 } |
|
1909 result = m.invoke(); |
|
1910 } |
|
1911 |
|
1912 public void visitConditional(JCConditional tree) { |
|
1913 Chain thenExit = null; |
|
1914 CondItem c = genCond(tree.cond, CRT_FLOW_CONTROLLER); |
|
1915 Chain elseChain = c.jumpFalse(); |
|
1916 if (!c.isFalse()) { |
|
1917 code.resolve(c.trueJumps); |
|
1918 int startpc = genCrt ? code.curCP() : 0; |
|
1919 genExpr(tree.truepart, pt).load(); |
|
1920 code.state.forceStackTop(tree.type); |
|
1921 if (genCrt) code.crt.put(tree.truepart, CRT_FLOW_TARGET, |
|
1922 startpc, code.curCP()); |
|
1923 thenExit = code.branch(goto_); |
|
1924 } |
|
1925 if (elseChain != null) { |
|
1926 code.resolve(elseChain); |
|
1927 int startpc = genCrt ? code.curCP() : 0; |
|
1928 genExpr(tree.falsepart, pt).load(); |
|
1929 code.state.forceStackTop(tree.type); |
|
1930 if (genCrt) code.crt.put(tree.falsepart, CRT_FLOW_TARGET, |
|
1931 startpc, code.curCP()); |
|
1932 } |
|
1933 code.resolve(thenExit); |
|
1934 result = items.makeStackItem(pt); |
|
1935 } |
|
1936 |
|
1937 private void setTypeAnnotationPositions(int treePos) { |
|
1938 MethodSymbol meth = code.meth; |
|
1939 boolean initOrClinit = code.meth.getKind() == javax.lang.model.element.ElementKind.CONSTRUCTOR |
|
1940 || code.meth.getKind() == javax.lang.model.element.ElementKind.STATIC_INIT; |
|
1941 |
|
1942 for (Attribute.TypeCompound ta : meth.getRawTypeAttributes()) { |
|
1943 if (ta.hasUnknownPosition()) |
|
1944 ta.tryFixPosition(); |
|
1945 |
|
1946 if (ta.position.matchesPos(treePos)) |
|
1947 ta.position.updatePosOffset(code.cp); |
|
1948 } |
|
1949 |
|
1950 if (!initOrClinit) |
|
1951 return; |
|
1952 |
|
1953 for (Attribute.TypeCompound ta : meth.owner.getRawTypeAttributes()) { |
|
1954 if (ta.hasUnknownPosition()) |
|
1955 ta.tryFixPosition(); |
|
1956 |
|
1957 if (ta.position.matchesPos(treePos)) |
|
1958 ta.position.updatePosOffset(code.cp); |
|
1959 } |
|
1960 |
|
1961 ClassSymbol clazz = meth.enclClass(); |
|
1962 for (Symbol s : new com.sun.tools.javac.model.FilteredMemberList(clazz.members())) { |
|
1963 if (!s.getKind().isField()) |
|
1964 continue; |
|
1965 |
|
1966 for (Attribute.TypeCompound ta : s.getRawTypeAttributes()) { |
|
1967 if (ta.hasUnknownPosition()) |
|
1968 ta.tryFixPosition(); |
|
1969 |
|
1970 if (ta.position.matchesPos(treePos)) |
|
1971 ta.position.updatePosOffset(code.cp); |
|
1972 } |
|
1973 } |
|
1974 } |
|
1975 |
|
1976 public void visitNewClass(JCNewClass tree) { |
|
1977 // Enclosing instances or anonymous classes should have been eliminated |
|
1978 // by now. |
|
1979 Assert.check(tree.encl == null && tree.def == null); |
|
1980 setTypeAnnotationPositions(tree.pos); |
|
1981 |
|
1982 code.emitop2(new_, makeRef(tree.pos(), tree.type)); |
|
1983 code.emitop0(dup); |
|
1984 |
|
1985 // Generate code for all arguments, where the expected types are |
|
1986 // the parameters of the constructor's external type (that is, |
|
1987 // any implicit outer instance appears as first parameter). |
|
1988 genArgs(tree.args, tree.constructor.externalType(types).getParameterTypes()); |
|
1989 |
|
1990 items.makeMemberItem(tree.constructor, true).invoke(); |
|
1991 result = items.makeStackItem(tree.type); |
|
1992 } |
|
1993 |
|
1994 public void visitNewArray(JCNewArray tree) { |
|
1995 setTypeAnnotationPositions(tree.pos); |
|
1996 |
|
1997 if (tree.elems != null) { |
|
1998 Type elemtype = types.elemtype(tree.type); |
|
1999 loadIntConst(tree.elems.length()); |
|
2000 Item arr = makeNewArray(tree.pos(), tree.type, 1); |
|
2001 int i = 0; |
|
2002 for (List<JCExpression> l = tree.elems; l.nonEmpty(); l = l.tail) { |
|
2003 arr.duplicate(); |
|
2004 loadIntConst(i); |
|
2005 i++; |
|
2006 genExpr(l.head, elemtype).load(); |
|
2007 items.makeIndexedItem(elemtype).store(); |
|
2008 } |
|
2009 result = arr; |
|
2010 } else { |
|
2011 for (List<JCExpression> l = tree.dims; l.nonEmpty(); l = l.tail) { |
|
2012 genExpr(l.head, syms.intType).load(); |
|
2013 } |
|
2014 result = makeNewArray(tree.pos(), tree.type, tree.dims.length()); |
|
2015 } |
|
2016 } |
|
2017 //where |
|
2018 /** Generate code to create an array with given element type and number |
|
2019 * of dimensions. |
|
2020 */ |
|
2021 Item makeNewArray(DiagnosticPosition pos, Type type, int ndims) { |
|
2022 Type elemtype = types.elemtype(type); |
|
2023 if (types.dimensions(type) > ClassFile.MAX_DIMENSIONS) { |
|
2024 log.error(pos, "limit.dimensions"); |
|
2025 nerrs++; |
|
2026 } |
|
2027 int elemcode = Code.arraycode(elemtype); |
|
2028 if (elemcode == 0 || (elemcode == 1 && ndims == 1)) { |
|
2029 code.emitAnewarray(makeRef(pos, elemtype), type); |
|
2030 } else if (elemcode == 1) { |
|
2031 code.emitMultianewarray(ndims, makeRef(pos, type), type); |
|
2032 } else { |
|
2033 code.emitNewarray(elemcode, type); |
|
2034 } |
|
2035 return items.makeStackItem(type); |
|
2036 } |
|
2037 |
|
2038 public void visitParens(JCParens tree) { |
|
2039 result = genExpr(tree.expr, tree.expr.type); |
|
2040 } |
|
2041 |
|
2042 public void visitAssign(JCAssign tree) { |
|
2043 Item l = genExpr(tree.lhs, tree.lhs.type); |
|
2044 genExpr(tree.rhs, tree.lhs.type).load(); |
|
2045 result = items.makeAssignItem(l); |
|
2046 } |
|
2047 |
|
2048 public void visitAssignop(JCAssignOp tree) { |
|
2049 OperatorSymbol operator = (OperatorSymbol) tree.operator; |
|
2050 Item l; |
|
2051 if (operator.opcode == string_add) { |
|
2052 // Generate code to make a string buffer |
|
2053 makeStringBuffer(tree.pos()); |
|
2054 |
|
2055 // Generate code for first string, possibly save one |
|
2056 // copy under buffer |
|
2057 l = genExpr(tree.lhs, tree.lhs.type); |
|
2058 if (l.width() > 0) { |
|
2059 code.emitop0(dup_x1 + 3 * (l.width() - 1)); |
|
2060 } |
|
2061 |
|
2062 // Load first string and append to buffer. |
|
2063 l.load(); |
|
2064 appendString(tree.lhs); |
|
2065 |
|
2066 // Append all other strings to buffer. |
|
2067 appendStrings(tree.rhs); |
|
2068 |
|
2069 // Convert buffer to string. |
|
2070 bufferToString(tree.pos()); |
|
2071 } else { |
|
2072 // Generate code for first expression |
|
2073 l = genExpr(tree.lhs, tree.lhs.type); |
|
2074 |
|
2075 // If we have an increment of -32768 to +32767 of a local |
|
2076 // int variable we can use an incr instruction instead of |
|
2077 // proceeding further. |
|
2078 if ((tree.hasTag(PLUS_ASG) || tree.hasTag(MINUS_ASG)) && |
|
2079 l instanceof LocalItem && |
|
2080 tree.lhs.type.getTag().isSubRangeOf(INT) && |
|
2081 tree.rhs.type.getTag().isSubRangeOf(INT) && |
|
2082 tree.rhs.type.constValue() != null) { |
|
2083 int ival = ((Number) tree.rhs.type.constValue()).intValue(); |
|
2084 if (tree.hasTag(MINUS_ASG)) ival = -ival; |
|
2085 ((LocalItem)l).incr(ival); |
|
2086 result = l; |
|
2087 return; |
|
2088 } |
|
2089 // Otherwise, duplicate expression, load one copy |
|
2090 // and complete binary operation. |
|
2091 l.duplicate(); |
|
2092 l.coerce(operator.type.getParameterTypes().head).load(); |
|
2093 completeBinop(tree.lhs, tree.rhs, operator).coerce(tree.lhs.type); |
|
2094 } |
|
2095 result = items.makeAssignItem(l); |
|
2096 } |
|
2097 |
|
2098 public void visitUnary(JCUnary tree) { |
|
2099 OperatorSymbol operator = (OperatorSymbol)tree.operator; |
|
2100 if (tree.hasTag(NOT)) { |
|
2101 CondItem od = genCond(tree.arg, false); |
|
2102 result = od.negate(); |
|
2103 } else { |
|
2104 Item od = genExpr(tree.arg, operator.type.getParameterTypes().head); |
|
2105 switch (tree.getTag()) { |
|
2106 case POS: |
|
2107 result = od.load(); |
|
2108 break; |
|
2109 case NEG: |
|
2110 result = od.load(); |
|
2111 code.emitop0(operator.opcode); |
|
2112 break; |
|
2113 case COMPL: |
|
2114 result = od.load(); |
|
2115 emitMinusOne(od.typecode); |
|
2116 code.emitop0(operator.opcode); |
|
2117 break; |
|
2118 case PREINC: case PREDEC: |
|
2119 od.duplicate(); |
|
2120 if (od instanceof LocalItem && |
|
2121 (operator.opcode == iadd || operator.opcode == isub)) { |
|
2122 ((LocalItem)od).incr(tree.hasTag(PREINC) ? 1 : -1); |
|
2123 result = od; |
|
2124 } else { |
|
2125 od.load(); |
|
2126 code.emitop0(one(od.typecode)); |
|
2127 code.emitop0(operator.opcode); |
|
2128 // Perform narrowing primitive conversion if byte, |
|
2129 // char, or short. Fix for 4304655. |
|
2130 if (od.typecode != INTcode && |
|
2131 Code.truncate(od.typecode) == INTcode) |
|
2132 code.emitop0(int2byte + od.typecode - BYTEcode); |
|
2133 result = items.makeAssignItem(od); |
|
2134 } |
|
2135 break; |
|
2136 case POSTINC: case POSTDEC: |
|
2137 od.duplicate(); |
|
2138 if (od instanceof LocalItem && |
|
2139 (operator.opcode == iadd || operator.opcode == isub)) { |
|
2140 Item res = od.load(); |
|
2141 ((LocalItem)od).incr(tree.hasTag(POSTINC) ? 1 : -1); |
|
2142 result = res; |
|
2143 } else { |
|
2144 Item res = od.load(); |
|
2145 od.stash(od.typecode); |
|
2146 code.emitop0(one(od.typecode)); |
|
2147 code.emitop0(operator.opcode); |
|
2148 // Perform narrowing primitive conversion if byte, |
|
2149 // char, or short. Fix for 4304655. |
|
2150 if (od.typecode != INTcode && |
|
2151 Code.truncate(od.typecode) == INTcode) |
|
2152 code.emitop0(int2byte + od.typecode - BYTEcode); |
|
2153 od.store(); |
|
2154 result = res; |
|
2155 } |
|
2156 break; |
|
2157 case NULLCHK: |
|
2158 result = od.load(); |
|
2159 code.emitop0(dup); |
|
2160 genNullCheck(tree.pos()); |
|
2161 break; |
|
2162 default: |
|
2163 Assert.error(); |
|
2164 } |
|
2165 } |
|
2166 } |
|
2167 |
|
2168 /** Generate a null check from the object value at stack top. */ |
|
2169 private void genNullCheck(DiagnosticPosition pos) { |
|
2170 callMethod(pos, syms.objectType, names.getClass, |
|
2171 List.<Type>nil(), false); |
|
2172 code.emitop0(pop); |
|
2173 } |
|
2174 |
|
2175 public void visitBinary(JCBinary tree) { |
|
2176 OperatorSymbol operator = (OperatorSymbol)tree.operator; |
|
2177 if (operator.opcode == string_add) { |
|
2178 // Create a string buffer. |
|
2179 makeStringBuffer(tree.pos()); |
|
2180 // Append all strings to buffer. |
|
2181 appendStrings(tree); |
|
2182 // Convert buffer to string. |
|
2183 bufferToString(tree.pos()); |
|
2184 result = items.makeStackItem(syms.stringType); |
|
2185 } else if (tree.hasTag(AND)) { |
|
2186 CondItem lcond = genCond(tree.lhs, CRT_FLOW_CONTROLLER); |
|
2187 if (!lcond.isFalse()) { |
|
2188 Chain falseJumps = lcond.jumpFalse(); |
|
2189 code.resolve(lcond.trueJumps); |
|
2190 CondItem rcond = genCond(tree.rhs, CRT_FLOW_TARGET); |
|
2191 result = items. |
|
2192 makeCondItem(rcond.opcode, |
|
2193 rcond.trueJumps, |
|
2194 Code.mergeChains(falseJumps, |
|
2195 rcond.falseJumps)); |
|
2196 } else { |
|
2197 result = lcond; |
|
2198 } |
|
2199 } else if (tree.hasTag(OR)) { |
|
2200 CondItem lcond = genCond(tree.lhs, CRT_FLOW_CONTROLLER); |
|
2201 if (!lcond.isTrue()) { |
|
2202 Chain trueJumps = lcond.jumpTrue(); |
|
2203 code.resolve(lcond.falseJumps); |
|
2204 CondItem rcond = genCond(tree.rhs, CRT_FLOW_TARGET); |
|
2205 result = items. |
|
2206 makeCondItem(rcond.opcode, |
|
2207 Code.mergeChains(trueJumps, rcond.trueJumps), |
|
2208 rcond.falseJumps); |
|
2209 } else { |
|
2210 result = lcond; |
|
2211 } |
|
2212 } else { |
|
2213 Item od = genExpr(tree.lhs, operator.type.getParameterTypes().head); |
|
2214 od.load(); |
|
2215 result = completeBinop(tree.lhs, tree.rhs, operator); |
|
2216 } |
|
2217 } |
|
2218 //where |
|
2219 /** Make a new string buffer. |
|
2220 */ |
|
2221 void makeStringBuffer(DiagnosticPosition pos) { |
|
2222 code.emitop2(new_, makeRef(pos, stringBufferType)); |
|
2223 code.emitop0(dup); |
|
2224 callMethod( |
|
2225 pos, stringBufferType, names.init, List.<Type>nil(), false); |
|
2226 } |
|
2227 |
|
2228 /** Append value (on tos) to string buffer (on tos - 1). |
|
2229 */ |
|
2230 void appendString(JCTree tree) { |
|
2231 Type t = tree.type.baseType(); |
|
2232 if (!t.isPrimitive() && t.tsym != syms.stringType.tsym) { |
|
2233 t = syms.objectType; |
|
2234 } |
|
2235 items.makeMemberItem(getStringBufferAppend(tree, t), false).invoke(); |
|
2236 } |
|
2237 Symbol getStringBufferAppend(JCTree tree, Type t) { |
|
2238 Assert.checkNull(t.constValue()); |
|
2239 Symbol method = stringBufferAppend.get(t); |
|
2240 if (method == null) { |
|
2241 method = rs.resolveInternalMethod(tree.pos(), |
|
2242 attrEnv, |
|
2243 stringBufferType, |
|
2244 names.append, |
|
2245 List.of(t), |
|
2246 null); |
|
2247 stringBufferAppend.put(t, method); |
|
2248 } |
|
2249 return method; |
|
2250 } |
|
2251 |
|
2252 /** Add all strings in tree to string buffer. |
|
2253 */ |
|
2254 void appendStrings(JCTree tree) { |
|
2255 tree = TreeInfo.skipParens(tree); |
|
2256 if (tree.hasTag(PLUS) && tree.type.constValue() == null) { |
|
2257 JCBinary op = (JCBinary) tree; |
|
2258 if (op.operator.kind == MTH && |
|
2259 ((OperatorSymbol) op.operator).opcode == string_add) { |
|
2260 appendStrings(op.lhs); |
|
2261 appendStrings(op.rhs); |
|
2262 return; |
|
2263 } |
|
2264 } |
|
2265 genExpr(tree, tree.type).load(); |
|
2266 appendString(tree); |
|
2267 } |
|
2268 |
|
2269 /** Convert string buffer on tos to string. |
|
2270 */ |
|
2271 void bufferToString(DiagnosticPosition pos) { |
|
2272 callMethod( |
|
2273 pos, |
|
2274 stringBufferType, |
|
2275 names.toString, |
|
2276 List.<Type>nil(), |
|
2277 false); |
|
2278 } |
|
2279 |
|
2280 /** Complete generating code for operation, with left operand |
|
2281 * already on stack. |
|
2282 * @param lhs The tree representing the left operand. |
|
2283 * @param rhs The tree representing the right operand. |
|
2284 * @param operator The operator symbol. |
|
2285 */ |
|
2286 Item completeBinop(JCTree lhs, JCTree rhs, OperatorSymbol operator) { |
|
2287 MethodType optype = (MethodType)operator.type; |
|
2288 int opcode = operator.opcode; |
|
2289 if (opcode >= if_icmpeq && opcode <= if_icmple && |
|
2290 rhs.type.constValue() instanceof Number && |
|
2291 ((Number) rhs.type.constValue()).intValue() == 0) { |
|
2292 opcode = opcode + (ifeq - if_icmpeq); |
|
2293 } else if (opcode >= if_acmpeq && opcode <= if_acmpne && |
|
2294 TreeInfo.isNull(rhs)) { |
|
2295 opcode = opcode + (if_acmp_null - if_acmpeq); |
|
2296 } else { |
|
2297 // The expected type of the right operand is |
|
2298 // the second parameter type of the operator, except for |
|
2299 // shifts with long shiftcount, where we convert the opcode |
|
2300 // to a short shift and the expected type to int. |
|
2301 Type rtype = operator.erasure(types).getParameterTypes().tail.head; |
|
2302 if (opcode >= ishll && opcode <= lushrl) { |
|
2303 opcode = opcode + (ishl - ishll); |
|
2304 rtype = syms.intType; |
|
2305 } |
|
2306 // Generate code for right operand and load. |
|
2307 genExpr(rhs, rtype).load(); |
|
2308 // If there are two consecutive opcode instructions, |
|
2309 // emit the first now. |
|
2310 if (opcode >= (1 << preShift)) { |
|
2311 code.emitop0(opcode >> preShift); |
|
2312 opcode = opcode & 0xFF; |
|
2313 } |
|
2314 } |
|
2315 if (opcode >= ifeq && opcode <= if_acmpne || |
|
2316 opcode == if_acmp_null || opcode == if_acmp_nonnull) { |
|
2317 return items.makeCondItem(opcode); |
|
2318 } else { |
|
2319 code.emitop0(opcode); |
|
2320 return items.makeStackItem(optype.restype); |
|
2321 } |
|
2322 } |
|
2323 |
|
2324 public void visitTypeCast(JCTypeCast tree) { |
|
2325 setTypeAnnotationPositions(tree.pos); |
|
2326 result = genExpr(tree.expr, tree.clazz.type).load(); |
|
2327 // Additional code is only needed if we cast to a reference type |
|
2328 // which is not statically a supertype of the expression's type. |
|
2329 // For basic types, the coerce(...) in genExpr(...) will do |
|
2330 // the conversion. |
|
2331 if (!tree.clazz.type.isPrimitive() && |
|
2332 types.asSuper(tree.expr.type, tree.clazz.type.tsym) == null) { |
|
2333 code.emitop2(checkcast, makeRef(tree.pos(), tree.clazz.type)); |
|
2334 } |
|
2335 } |
|
2336 |
|
2337 public void visitWildcard(JCWildcard tree) { |
|
2338 throw new AssertionError(this.getClass().getName()); |
|
2339 } |
|
2340 |
|
2341 public void visitTypeTest(JCInstanceOf tree) { |
|
2342 setTypeAnnotationPositions(tree.pos); |
|
2343 genExpr(tree.expr, tree.expr.type).load(); |
|
2344 code.emitop2(instanceof_, makeRef(tree.pos(), tree.clazz.type)); |
|
2345 result = items.makeStackItem(syms.booleanType); |
|
2346 } |
|
2347 |
|
2348 public void visitIndexed(JCArrayAccess tree) { |
|
2349 genExpr(tree.indexed, tree.indexed.type).load(); |
|
2350 genExpr(tree.index, syms.intType).load(); |
|
2351 result = items.makeIndexedItem(tree.type); |
|
2352 } |
|
2353 |
|
2354 public void visitIdent(JCIdent tree) { |
|
2355 Symbol sym = tree.sym; |
|
2356 if (tree.name == names._this || tree.name == names._super) { |
|
2357 Item res = tree.name == names._this |
|
2358 ? items.makeThisItem() |
|
2359 : items.makeSuperItem(); |
|
2360 if (sym.kind == MTH) { |
|
2361 // Generate code to address the constructor. |
|
2362 res.load(); |
|
2363 res = items.makeMemberItem(sym, true); |
|
2364 } |
|
2365 result = res; |
|
2366 } else if (sym.kind == VAR && sym.owner.kind == MTH) { |
|
2367 result = items.makeLocalItem((VarSymbol)sym); |
|
2368 } else if (isInvokeDynamic(sym)) { |
|
2369 result = items.makeDynamicItem(sym); |
|
2370 } else if ((sym.flags() & STATIC) != 0) { |
|
2371 if (!isAccessSuper(env.enclMethod)) |
|
2372 sym = binaryQualifier(sym, env.enclClass.type); |
|
2373 result = items.makeStaticItem(sym); |
|
2374 } else { |
|
2375 items.makeThisItem().load(); |
|
2376 sym = binaryQualifier(sym, env.enclClass.type); |
|
2377 result = items.makeMemberItem(sym, (sym.flags() & PRIVATE) != 0); |
|
2378 } |
|
2379 } |
|
2380 |
|
2381 public void visitSelect(JCFieldAccess tree) { |
|
2382 Symbol sym = tree.sym; |
|
2383 |
|
2384 if (tree.name == names._class) { |
|
2385 Assert.check(target.hasClassLiterals()); |
|
2386 code.emitLdc(makeRef(tree.pos(), tree.selected.type)); |
|
2387 result = items.makeStackItem(pt); |
|
2388 return; |
|
2389 } |
|
2390 |
|
2391 Symbol ssym = TreeInfo.symbol(tree.selected); |
|
2392 |
|
2393 // Are we selecting via super? |
|
2394 boolean selectSuper = |
|
2395 ssym != null && (ssym.kind == TYP || ssym.name == names._super); |
|
2396 |
|
2397 // Are we accessing a member of the superclass in an access method |
|
2398 // resulting from a qualified super? |
|
2399 boolean accessSuper = isAccessSuper(env.enclMethod); |
|
2400 |
|
2401 Item base = (selectSuper) |
|
2402 ? items.makeSuperItem() |
|
2403 : genExpr(tree.selected, tree.selected.type); |
|
2404 |
|
2405 if (sym.kind == VAR && ((VarSymbol) sym).getConstValue() != null) { |
|
2406 // We are seeing a variable that is constant but its selecting |
|
2407 // expression is not. |
|
2408 if ((sym.flags() & STATIC) != 0) { |
|
2409 if (!selectSuper && (ssym == null || ssym.kind != TYP)) |
|
2410 base = base.load(); |
|
2411 base.drop(); |
|
2412 } else { |
|
2413 base.load(); |
|
2414 genNullCheck(tree.selected.pos()); |
|
2415 } |
|
2416 result = items. |
|
2417 makeImmediateItem(sym.type, ((VarSymbol) sym).getConstValue()); |
|
2418 } else { |
|
2419 if (isInvokeDynamic(sym)) { |
|
2420 result = items.makeDynamicItem(sym); |
|
2421 return; |
|
2422 } else { |
|
2423 sym = binaryQualifier(sym, tree.selected.type); |
|
2424 } |
|
2425 if ((sym.flags() & STATIC) != 0) { |
|
2426 if (!selectSuper && (ssym == null || ssym.kind != TYP)) |
|
2427 base = base.load(); |
|
2428 base.drop(); |
|
2429 result = items.makeStaticItem(sym); |
|
2430 } else { |
|
2431 base.load(); |
|
2432 if (sym == syms.lengthVar) { |
|
2433 code.emitop0(arraylength); |
|
2434 result = items.makeStackItem(syms.intType); |
|
2435 } else { |
|
2436 result = items. |
|
2437 makeMemberItem(sym, |
|
2438 (sym.flags() & PRIVATE) != 0 || |
|
2439 selectSuper || accessSuper); |
|
2440 } |
|
2441 } |
|
2442 } |
|
2443 } |
|
2444 |
|
2445 public boolean isInvokeDynamic(Symbol sym) { |
|
2446 return sym.kind == MTH && ((MethodSymbol)sym).isDynamic(); |
|
2447 } |
|
2448 |
|
2449 public void visitLiteral(JCLiteral tree) { |
|
2450 if (tree.type.hasTag(BOT)) { |
|
2451 code.emitop0(aconst_null); |
|
2452 if (types.dimensions(pt) > 1) { |
|
2453 code.emitop2(checkcast, makeRef(tree.pos(), pt)); |
|
2454 result = items.makeStackItem(pt); |
|
2455 } else { |
|
2456 result = items.makeStackItem(tree.type); |
|
2457 } |
|
2458 } |
|
2459 else |
|
2460 result = items.makeImmediateItem(tree.type, tree.value); |
|
2461 } |
|
2462 |
|
2463 public void visitLetExpr(LetExpr tree) { |
|
2464 int limit = code.nextreg; |
|
2465 genStats(tree.defs, env); |
|
2466 result = genExpr(tree.expr, tree.expr.type).load(); |
|
2467 code.endScopes(limit); |
|
2468 } |
|
2469 |
|
2470 private void generateReferencesToPrunedTree(ClassSymbol classSymbol, Pool pool) { |
|
2471 List<JCTree> prunedInfo = lower.prunedTree.get(classSymbol); |
|
2472 if (prunedInfo != null) { |
|
2473 for (JCTree prunedTree: prunedInfo) { |
|
2474 prunedTree.accept(classReferenceVisitor); |
|
2475 } |
|
2476 } |
|
2477 } |
|
2478 |
|
2479 /* ************************************************************************ |
|
2480 * main method |
|
2481 *************************************************************************/ |
|
2482 |
|
2483 /** Generate code for a class definition. |
|
2484 * @param env The attribution environment that belongs to the |
|
2485 * outermost class containing this class definition. |
|
2486 * We need this for resolving some additional symbols. |
|
2487 * @param cdef The tree representing the class definition. |
|
2488 * @return True if code is generated with no errors. |
|
2489 */ |
|
2490 public boolean genClass(Env<AttrContext> env, JCClassDecl cdef) { |
|
2491 try { |
|
2492 attrEnv = env; |
|
2493 ClassSymbol c = cdef.sym; |
|
2494 this.toplevel = env.toplevel; |
|
2495 this.endPosTable = toplevel.endPositions; |
|
2496 // If this is a class definition requiring Miranda methods, |
|
2497 // add them. |
|
2498 if (generateIproxies && |
|
2499 (c.flags() & (INTERFACE|ABSTRACT)) == ABSTRACT |
|
2500 && !allowGenerics // no Miranda methods available with generics |
|
2501 ) |
|
2502 implementInterfaceMethods(c); |
|
2503 cdef.defs = normalizeDefs(cdef.defs, c); |
|
2504 c.pool = pool; |
|
2505 pool.reset(); |
|
2506 generateReferencesToPrunedTree(c, pool); |
|
2507 Env<GenContext> localEnv = |
|
2508 new Env<GenContext>(cdef, new GenContext()); |
|
2509 localEnv.toplevel = env.toplevel; |
|
2510 localEnv.enclClass = cdef; |
|
2511 |
|
2512 /* We must not analyze synthetic methods |
|
2513 */ |
|
2514 if (varDebugInfo && (cdef.sym.flags() & SYNTHETIC) == 0) { |
|
2515 try { |
|
2516 LVTAssignAnalyzer lvtAssignAnalyzer = LVTAssignAnalyzer.make( |
|
2517 lvtRanges, syms, names); |
|
2518 lvtAssignAnalyzer.analyzeTree(localEnv); |
|
2519 } catch (Throwable e) { |
|
2520 throw e; |
|
2521 } |
|
2522 } |
|
2523 |
|
2524 for (List<JCTree> l = cdef.defs; l.nonEmpty(); l = l.tail) { |
|
2525 genDef(l.head, localEnv); |
|
2526 } |
|
2527 if (pool.numEntries() > Pool.MAX_ENTRIES) { |
|
2528 log.error(cdef.pos(), "limit.pool"); |
|
2529 nerrs++; |
|
2530 } |
|
2531 if (nerrs != 0) { |
|
2532 // if errors, discard code |
|
2533 for (List<JCTree> l = cdef.defs; l.nonEmpty(); l = l.tail) { |
|
2534 if (l.head.hasTag(METHODDEF)) |
|
2535 ((JCMethodDecl) l.head).sym.code = null; |
|
2536 } |
|
2537 } |
|
2538 cdef.defs = List.nil(); // discard trees |
|
2539 return nerrs == 0; |
|
2540 } finally { |
|
2541 // note: this method does NOT support recursion. |
|
2542 attrEnv = null; |
|
2543 this.env = null; |
|
2544 toplevel = null; |
|
2545 endPosTable = null; |
|
2546 nerrs = 0; |
|
2547 } |
|
2548 } |
|
2549 |
|
2550 /* ************************************************************************ |
|
2551 * Auxiliary classes |
|
2552 *************************************************************************/ |
|
2553 |
|
2554 /** An abstract class for finalizer generation. |
|
2555 */ |
|
2556 abstract class GenFinalizer { |
|
2557 /** Generate code to clean up when unwinding. */ |
|
2558 abstract void gen(); |
|
2559 |
|
2560 /** Generate code to clean up at last. */ |
|
2561 abstract void genLast(); |
|
2562 |
|
2563 /** Does this finalizer have some nontrivial cleanup to perform? */ |
|
2564 boolean hasFinalizer() { return true; } |
|
2565 } |
|
2566 |
|
2567 /** code generation contexts, |
|
2568 * to be used as type parameter for environments. |
|
2569 */ |
|
2570 static class GenContext { |
|
2571 |
|
2572 /** A chain for all unresolved jumps that exit the current environment. |
|
2573 */ |
|
2574 Chain exit = null; |
|
2575 |
|
2576 /** A chain for all unresolved jumps that continue in the |
|
2577 * current environment. |
|
2578 */ |
|
2579 Chain cont = null; |
|
2580 |
|
2581 /** A closure that generates the finalizer of the current environment. |
|
2582 * Only set for Synchronized and Try contexts. |
|
2583 */ |
|
2584 GenFinalizer finalize = null; |
|
2585 |
|
2586 /** Is this a switch statement? If so, allocate registers |
|
2587 * even when the variable declaration is unreachable. |
|
2588 */ |
|
2589 boolean isSwitch = false; |
|
2590 |
|
2591 /** A list buffer containing all gaps in the finalizer range, |
|
2592 * where a catch all exception should not apply. |
|
2593 */ |
|
2594 ListBuffer<Integer> gaps = null; |
|
2595 |
|
2596 /** Add given chain to exit chain. |
|
2597 */ |
|
2598 void addExit(Chain c) { |
|
2599 exit = Code.mergeChains(c, exit); |
|
2600 } |
|
2601 |
|
2602 /** Add given chain to cont chain. |
|
2603 */ |
|
2604 void addCont(Chain c) { |
|
2605 cont = Code.mergeChains(c, cont); |
|
2606 } |
|
2607 } |
|
2608 |
|
2609 static class LVTAssignAnalyzer |
|
2610 extends Flow.AbstractAssignAnalyzer<LVTAssignAnalyzer.LVTAssignPendingExit> { |
|
2611 |
|
2612 final LVTBits lvtInits; |
|
2613 final LVTRanges lvtRanges; |
|
2614 |
|
2615 /* This class is anchored to a context dependent tree. The tree can |
|
2616 * vary inside the same instruction for example in the switch instruction |
|
2617 * the same FlowBits instance can be anchored to the whole tree, or |
|
2618 * to a given case. The aim is to always anchor the bits to the tree |
|
2619 * capable of closing a DA range. |
|
2620 */ |
|
2621 static class LVTBits extends Bits { |
|
2622 |
|
2623 enum BitsOpKind { |
|
2624 INIT, |
|
2625 CLEAR, |
|
2626 INCL_BIT, |
|
2627 EXCL_BIT, |
|
2628 ASSIGN, |
|
2629 AND_SET, |
|
2630 OR_SET, |
|
2631 DIFF_SET, |
|
2632 XOR_SET, |
|
2633 INCL_RANGE, |
|
2634 EXCL_RANGE, |
|
2635 } |
|
2636 |
|
2637 JCTree currentTree; |
|
2638 LVTAssignAnalyzer analyzer; |
|
2639 private int[] oldBits = null; |
|
2640 BitsState stateBeforeOp; |
|
2641 |
|
2642 LVTBits() { |
|
2643 super(false); |
|
2644 } |
|
2645 |
|
2646 LVTBits(int[] bits, BitsState initState) { |
|
2647 super(bits, initState); |
|
2648 } |
|
2649 |
|
2650 @Override |
|
2651 public void clear() { |
|
2652 generalOp(null, -1, BitsOpKind.CLEAR); |
|
2653 } |
|
2654 |
|
2655 @Override |
|
2656 protected void internalReset() { |
|
2657 super.internalReset(); |
|
2658 oldBits = null; |
|
2659 } |
|
2660 |
|
2661 @Override |
|
2662 public Bits assign(Bits someBits) { |
|
2663 // bits can be null |
|
2664 oldBits = bits; |
|
2665 stateBeforeOp = currentState; |
|
2666 super.assign(someBits); |
|
2667 changed(); |
|
2668 return this; |
|
2669 } |
|
2670 |
|
2671 @Override |
|
2672 public void excludeFrom(int start) { |
|
2673 generalOp(null, start, BitsOpKind.EXCL_RANGE); |
|
2674 } |
|
2675 |
|
2676 @Override |
|
2677 public void excl(int x) { |
|
2678 Assert.check(x >= 0); |
|
2679 generalOp(null, x, BitsOpKind.EXCL_BIT); |
|
2680 } |
|
2681 |
|
2682 @Override |
|
2683 public Bits andSet(Bits xs) { |
|
2684 return generalOp(xs, -1, BitsOpKind.AND_SET); |
|
2685 } |
|
2686 |
|
2687 @Override |
|
2688 public Bits orSet(Bits xs) { |
|
2689 return generalOp(xs, -1, BitsOpKind.OR_SET); |
|
2690 } |
|
2691 |
|
2692 @Override |
|
2693 public Bits diffSet(Bits xs) { |
|
2694 return generalOp(xs, -1, BitsOpKind.DIFF_SET); |
|
2695 } |
|
2696 |
|
2697 @Override |
|
2698 public Bits xorSet(Bits xs) { |
|
2699 return generalOp(xs, -1, BitsOpKind.XOR_SET); |
|
2700 } |
|
2701 |
|
2702 private Bits generalOp(Bits xs, int i, BitsOpKind opKind) { |
|
2703 Assert.check(currentState != BitsState.UNKNOWN); |
|
2704 oldBits = dupBits(); |
|
2705 stateBeforeOp = currentState; |
|
2706 switch (opKind) { |
|
2707 case AND_SET: |
|
2708 super.andSet(xs); |
|
2709 break; |
|
2710 case OR_SET: |
|
2711 super.orSet(xs); |
|
2712 break; |
|
2713 case XOR_SET: |
|
2714 super.xorSet(xs); |
|
2715 break; |
|
2716 case DIFF_SET: |
|
2717 super.diffSet(xs); |
|
2718 break; |
|
2719 case CLEAR: |
|
2720 super.clear(); |
|
2721 break; |
|
2722 case EXCL_BIT: |
|
2723 super.excl(i); |
|
2724 break; |
|
2725 case EXCL_RANGE: |
|
2726 super.excludeFrom(i); |
|
2727 break; |
|
2728 } |
|
2729 changed(); |
|
2730 return this; |
|
2731 } |
|
2732 |
|
2733 /* The tree we need to anchor the bits instance to. |
|
2734 */ |
|
2735 LVTBits at(JCTree tree) { |
|
2736 this.currentTree = tree; |
|
2737 return this; |
|
2738 } |
|
2739 |
|
2740 /* If the instance should be changed but the tree is not a closing |
|
2741 * tree then a reset is needed or the former tree can mistakingly be |
|
2742 * used. |
|
2743 */ |
|
2744 LVTBits resetTree() { |
|
2745 this.currentTree = null; |
|
2746 return this; |
|
2747 } |
|
2748 |
|
2749 /** This method will be called after any operation that causes a change to |
|
2750 * the bits. Subclasses can thus override it in order to extract information |
|
2751 * from the changes produced to the bits by the given operation. |
|
2752 */ |
|
2753 public void changed() { |
|
2754 if (currentTree != null && |
|
2755 stateBeforeOp != BitsState.UNKNOWN && |
|
2756 trackTree(currentTree)) { |
|
2757 List<VarSymbol> locals = |
|
2758 analyzer.lvtRanges |
|
2759 .getVars(analyzer.currentMethod, currentTree); |
|
2760 locals = locals != null ? |
|
2761 locals : List.<VarSymbol>nil(); |
|
2762 for (JCVariableDecl vardecl : analyzer.vardecls) { |
|
2763 //once the first is null, the rest will be so. |
|
2764 if (vardecl == null) { |
|
2765 break; |
|
2766 } |
|
2767 if (trackVar(vardecl.sym) && bitChanged(vardecl.sym.adr)) { |
|
2768 locals = locals.prepend(vardecl.sym); |
|
2769 } |
|
2770 } |
|
2771 if (!locals.isEmpty()) { |
|
2772 analyzer.lvtRanges.setEntry(analyzer.currentMethod, |
|
2773 currentTree, locals); |
|
2774 } |
|
2775 } |
|
2776 } |
|
2777 |
|
2778 boolean bitChanged(int x) { |
|
2779 boolean isMemberOfBits = isMember(x); |
|
2780 int[] tmp = bits; |
|
2781 bits = oldBits; |
|
2782 boolean isMemberOfOldBits = isMember(x); |
|
2783 bits = tmp; |
|
2784 return (!isMemberOfBits && isMemberOfOldBits); |
|
2785 } |
|
2786 |
|
2787 boolean trackVar(VarSymbol var) { |
|
2788 return (var.owner.kind == MTH && |
|
2789 (var.flags() & (PARAMETER | HASINIT)) == 0 && |
|
2790 analyzer.trackable(var)); |
|
2791 } |
|
2792 |
|
2793 boolean trackTree(JCTree tree) { |
|
2794 switch (tree.getTag()) { |
|
2795 // of course a method closes the alive range of a local variable. |
|
2796 case METHODDEF: |
|
2797 // for while loops we want only the body |
|
2798 case WHILELOOP: |
|
2799 return false; |
|
2800 } |
|
2801 return true; |
|
2802 } |
|
2803 |
|
2804 } |
|
2805 |
|
2806 public class LVTAssignPendingExit extends Flow.AssignAnalyzer.AssignPendingExit { |
|
2807 |
|
2808 LVTAssignPendingExit(JCTree tree, final Bits inits, final Bits uninits) { |
|
2809 super(tree, inits, uninits); |
|
2810 } |
|
2811 |
|
2812 @Override |
|
2813 public void resolveJump(JCTree tree) { |
|
2814 lvtInits.at(tree); |
|
2815 super.resolveJump(tree); |
|
2816 } |
|
2817 } |
|
2818 |
|
2819 private LVTAssignAnalyzer(LVTRanges lvtRanges, Symtab syms, Names names) { |
|
2820 super(new LVTBits(), syms, names, false); |
|
2821 lvtInits = (LVTBits)inits; |
|
2822 this.lvtRanges = lvtRanges; |
|
2823 } |
|
2824 |
|
2825 public static LVTAssignAnalyzer make(LVTRanges lvtRanges, Symtab syms, Names names) { |
|
2826 LVTAssignAnalyzer result = new LVTAssignAnalyzer(lvtRanges, syms, names); |
|
2827 result.lvtInits.analyzer = result; |
|
2828 return result; |
|
2829 } |
|
2830 |
|
2831 @Override |
|
2832 protected void markDead(JCTree tree) { |
|
2833 lvtInits.at(tree).inclRange(returnadr, nextadr); |
|
2834 super.markDead(tree); |
|
2835 } |
|
2836 |
|
2837 @Override |
|
2838 protected void merge(JCTree tree) { |
|
2839 lvtInits.at(tree); |
|
2840 super.merge(tree); |
|
2841 } |
|
2842 |
|
2843 boolean isSyntheticOrMandated(Symbol sym) { |
|
2844 return (sym.flags() & (SYNTHETIC | MANDATED)) != 0; |
|
2845 } |
|
2846 |
|
2847 @Override |
|
2848 protected boolean trackable(VarSymbol sym) { |
|
2849 if (isSyntheticOrMandated(sym)) { |
|
2850 //fast check to avoid tracking synthetic or mandated variables |
|
2851 return false; |
|
2852 } |
|
2853 return super.trackable(sym); |
|
2854 } |
|
2855 |
|
2856 @Override |
|
2857 protected void initParam(JCVariableDecl def) { |
|
2858 if (!isSyntheticOrMandated(def.sym)) { |
|
2859 super.initParam(def); |
|
2860 } |
|
2861 } |
|
2862 |
|
2863 @Override |
|
2864 protected void assignToInits(JCTree tree, Bits bits) { |
|
2865 lvtInits.at(tree); |
|
2866 lvtInits.assign(bits); |
|
2867 } |
|
2868 |
|
2869 @Override |
|
2870 protected void andSetInits(JCTree tree, Bits bits) { |
|
2871 lvtInits.at(tree); |
|
2872 lvtInits.andSet(bits); |
|
2873 } |
|
2874 |
|
2875 @Override |
|
2876 protected void orSetInits(JCTree tree, Bits bits) { |
|
2877 lvtInits.at(tree); |
|
2878 lvtInits.orSet(bits); |
|
2879 } |
|
2880 |
|
2881 @Override |
|
2882 protected void exclVarFromInits(JCTree tree, int adr) { |
|
2883 lvtInits.at(tree); |
|
2884 lvtInits.excl(adr); |
|
2885 } |
|
2886 |
|
2887 @Override |
|
2888 protected LVTAssignPendingExit createNewPendingExit(JCTree tree, Bits inits, Bits uninits) { |
|
2889 return new LVTAssignPendingExit(tree, inits, uninits); |
|
2890 } |
|
2891 |
|
2892 MethodSymbol currentMethod; |
|
2893 |
|
2894 @Override |
|
2895 public void visitMethodDef(JCMethodDecl tree) { |
|
2896 if ((tree.sym.flags() & (SYNTHETIC | GENERATEDCONSTR)) != 0 |
|
2897 && (tree.sym.flags() & LAMBDA_METHOD) == 0) { |
|
2898 return; |
|
2899 } |
|
2900 if (tree.name.equals(names.clinit)) { |
|
2901 return; |
|
2902 } |
|
2903 boolean enumClass = (tree.sym.owner.flags() & ENUM) != 0; |
|
2904 if (enumClass && |
|
2905 (tree.name.equals(names.valueOf) || |
|
2906 tree.name.equals(names.values) || |
|
2907 tree.name.equals(names.init))) { |
|
2908 return; |
|
2909 } |
|
2910 currentMethod = tree.sym; |
|
2911 |
|
2912 super.visitMethodDef(tree); |
|
2913 } |
|
2914 |
|
2915 } |
|
2916 |
|
2917 } |