|
1 /* |
|
2 * Copyright (c) 2010, 2012, 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 package com.sun.tools.javac.comp; |
|
26 |
|
27 import com.sun.tools.javac.tree.*; |
|
28 import com.sun.tools.javac.tree.JCTree; |
|
29 import com.sun.tools.javac.tree.JCTree.*; |
|
30 import com.sun.tools.javac.tree.JCTree.JCMemberReference.ReferenceKind; |
|
31 import com.sun.tools.javac.tree.TreeMaker; |
|
32 import com.sun.tools.javac.tree.TreeScanner; |
|
33 import com.sun.tools.javac.tree.TreeTranslator; |
|
34 import com.sun.tools.javac.code.Flags; |
|
35 import com.sun.tools.javac.code.Kinds; |
|
36 import com.sun.tools.javac.code.Symbol; |
|
37 import com.sun.tools.javac.code.Symbol.ClassSymbol; |
|
38 import com.sun.tools.javac.code.Symbol.DynamicMethodSymbol; |
|
39 import com.sun.tools.javac.code.Symbol.MethodSymbol; |
|
40 import com.sun.tools.javac.code.Symbol.VarSymbol; |
|
41 import com.sun.tools.javac.code.Symtab; |
|
42 import com.sun.tools.javac.code.Type; |
|
43 import com.sun.tools.javac.code.Type.ClassType; |
|
44 import com.sun.tools.javac.code.Type.MethodType; |
|
45 import com.sun.tools.javac.code.Types; |
|
46 import com.sun.tools.javac.comp.LambdaToMethod.LambdaAnalyzer.*; |
|
47 import com.sun.tools.javac.jvm.*; |
|
48 import com.sun.tools.javac.util.*; |
|
49 import com.sun.tools.javac.util.List; |
|
50 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; |
|
51 import com.sun.source.tree.MemberReferenceTree.ReferenceMode; |
|
52 |
|
53 import java.util.HashMap; |
|
54 import java.util.LinkedHashMap; |
|
55 import java.util.Map; |
|
56 |
|
57 import static com.sun.tools.javac.comp.LambdaToMethod.LambdaSymbolKind.*; |
|
58 import static com.sun.tools.javac.code.Flags.*; |
|
59 import static com.sun.tools.javac.code.Kinds.*; |
|
60 import static com.sun.tools.javac.code.TypeTag.BOT; |
|
61 import static com.sun.tools.javac.code.TypeTag.NONE; |
|
62 import static com.sun.tools.javac.code.TypeTag.VOID; |
|
63 import static com.sun.tools.javac.tree.JCTree.Tag.*; |
|
64 |
|
65 /** |
|
66 * This pass desugars lambda expressions into static methods |
|
67 * |
|
68 * <p><b>This is NOT part of any supported API. |
|
69 * If you write code that depends on this, you do so at your own risk. |
|
70 * This code and its internal interfaces are subject to change or |
|
71 * deletion without notice.</b> |
|
72 */ |
|
73 public class LambdaToMethod extends TreeTranslator { |
|
74 |
|
75 private Names names; |
|
76 private Symtab syms; |
|
77 private Resolve rs; |
|
78 private TreeMaker make; |
|
79 private Types types; |
|
80 private TransTypes transTypes; |
|
81 private Env<AttrContext> attrEnv; |
|
82 |
|
83 /** the analyzer scanner */ |
|
84 private LambdaAnalyzer analyzer; |
|
85 |
|
86 /** map from lambda trees to translation contexts */ |
|
87 private Map<JCTree, TranslationContext<?>> contextMap; |
|
88 |
|
89 /** current translation context (visitor argument) */ |
|
90 private TranslationContext<?> context; |
|
91 |
|
92 /** list of translated methods |
|
93 **/ |
|
94 private ListBuffer<JCTree> translatedMethodList; |
|
95 |
|
96 // <editor-fold defaultstate="collapsed" desc="Instantiating"> |
|
97 private static final Context.Key<LambdaToMethod> unlambdaKey = |
|
98 new Context.Key<LambdaToMethod>(); |
|
99 |
|
100 public static LambdaToMethod instance(Context context) { |
|
101 LambdaToMethod instance = context.get(unlambdaKey); |
|
102 if (instance == null) { |
|
103 instance = new LambdaToMethod(context); |
|
104 } |
|
105 return instance; |
|
106 } |
|
107 |
|
108 private LambdaToMethod(Context context) { |
|
109 names = Names.instance(context); |
|
110 syms = Symtab.instance(context); |
|
111 rs = Resolve.instance(context); |
|
112 make = TreeMaker.instance(context); |
|
113 types = Types.instance(context); |
|
114 transTypes = TransTypes.instance(context); |
|
115 this.analyzer = makeAnalyzer(); |
|
116 } |
|
117 |
|
118 private LambdaAnalyzer makeAnalyzer() { |
|
119 return new LambdaAnalyzer(); |
|
120 } |
|
121 // </editor-fold> |
|
122 |
|
123 // <editor-fold defaultstate="collapsed" desc="translate methods"> |
|
124 @Override |
|
125 public <T extends JCTree> T translate(T tree) { |
|
126 TranslationContext<?> newContext = contextMap.get(tree); |
|
127 return translate(tree, newContext != null ? newContext : context); |
|
128 } |
|
129 |
|
130 public <T extends JCTree> T translate(T tree, TranslationContext<?> newContext) { |
|
131 TranslationContext<?> prevContext = context; |
|
132 try { |
|
133 context = newContext; |
|
134 return super.translate(tree); |
|
135 } |
|
136 finally { |
|
137 context = prevContext; |
|
138 } |
|
139 } |
|
140 |
|
141 public <T extends JCTree> List<T> translate(List<T> trees, TranslationContext<?> newContext) { |
|
142 ListBuffer<T> buf = ListBuffer.lb(); |
|
143 for (T tree : trees) { |
|
144 buf.append(translate(tree, newContext)); |
|
145 } |
|
146 return buf.toList(); |
|
147 } |
|
148 |
|
149 public JCTree translateTopLevelClass(Env<AttrContext> env, JCTree cdef, TreeMaker make) { |
|
150 this.make = make; |
|
151 this.attrEnv = env; |
|
152 this.context = null; |
|
153 this.contextMap = new HashMap<JCTree, TranslationContext<?>>(); |
|
154 return translate(cdef); |
|
155 } |
|
156 // </editor-fold> |
|
157 |
|
158 // <editor-fold defaultstate="collapsed" desc="visitor methods"> |
|
159 /** |
|
160 * Visit a class. |
|
161 * Maintain the translatedMethodList across nested classes. |
|
162 * Append the translatedMethodList to the class after it is translated. |
|
163 * @param tree |
|
164 */ |
|
165 @Override |
|
166 public void visitClassDef(JCClassDecl tree) { |
|
167 if (tree.sym.owner.kind == PCK) { |
|
168 //analyze class |
|
169 analyzer.analyzeClass(tree); |
|
170 } |
|
171 ListBuffer<JCTree> prevTranslated = translatedMethodList; |
|
172 try { |
|
173 translatedMethodList = ListBuffer.lb(); |
|
174 super.visitClassDef(tree); |
|
175 //add all translated instance methods here |
|
176 tree.defs = tree.defs.appendList(translatedMethodList.toList()); |
|
177 for (JCTree lambda : translatedMethodList) { |
|
178 tree.sym.members().enter(((JCMethodDecl)lambda).sym); |
|
179 } |
|
180 result = tree; |
|
181 } finally { |
|
182 translatedMethodList = prevTranslated; |
|
183 } |
|
184 } |
|
185 |
|
186 /** |
|
187 * Translate a lambda into a method to be inserted into the class. |
|
188 * Then replace the lambda site with an invokedynamic call of to lambda |
|
189 * meta-factory, which will use the lambda method. |
|
190 * @param tree |
|
191 */ |
|
192 @Override |
|
193 public void visitLambda(JCLambda tree) { |
|
194 LambdaTranslationContext localContext = (LambdaTranslationContext)context; |
|
195 MethodSymbol sym = (MethodSymbol)localContext.translatedSym; |
|
196 MethodType lambdaType = (MethodType) sym.type; |
|
197 |
|
198 //create the method declaration hoisting the lambda body |
|
199 JCMethodDecl lambdaDecl = make.MethodDef(make.Modifiers(sym.flags_field), |
|
200 sym.name, |
|
201 make.QualIdent(lambdaType.getReturnType().tsym), |
|
202 List.<JCTypeParameter>nil(), |
|
203 localContext.syntheticParams, |
|
204 lambdaType.getThrownTypes() == null ? |
|
205 List.<JCExpression>nil() : |
|
206 make.Types(lambdaType.getThrownTypes()), |
|
207 null, |
|
208 null); |
|
209 lambdaDecl.sym = sym; |
|
210 lambdaDecl.type = lambdaType; |
|
211 |
|
212 //translate lambda body |
|
213 //As the lambda body is translated, all references to lambda locals, |
|
214 //captured variables, enclosing members are adjusted accordingly |
|
215 //to refer to the static method parameters (rather than i.e. acessing to |
|
216 //captured members directly). |
|
217 lambdaDecl.body = translate(makeLambdaBody(tree, lambdaDecl)); |
|
218 |
|
219 //Add the method to the list of methods to be added to this class. |
|
220 translatedMethodList = translatedMethodList.prepend(lambdaDecl); |
|
221 |
|
222 //now that we have generated a method for the lambda expression, |
|
223 //we can translate the lambda into a method reference pointing to the newly |
|
224 //created method. |
|
225 // |
|
226 //Note that we need to adjust the method handle so that it will match the |
|
227 //signature of the SAM descriptor - this means that the method reference |
|
228 //should be added the following synthetic arguments: |
|
229 // |
|
230 // * the "this" argument if it is an instance method |
|
231 // * enclosing locals captured by the lambda expression |
|
232 |
|
233 ListBuffer<JCExpression> syntheticInits = ListBuffer.lb(); |
|
234 |
|
235 if (!sym.isStatic()) { |
|
236 syntheticInits.append(makeThis( |
|
237 sym.owner.asType(), |
|
238 localContext.owner.enclClass())); |
|
239 } |
|
240 |
|
241 //add captured locals |
|
242 for (Symbol fv : localContext.getSymbolMap(CAPTURED_VAR).keySet()) { |
|
243 if (fv != localContext.self) { |
|
244 JCTree captured_local = make.Ident(fv).setType(fv.type); |
|
245 syntheticInits.append((JCExpression) captured_local); |
|
246 } |
|
247 } |
|
248 |
|
249 //then, determine the arguments to the indy call |
|
250 List<JCExpression> indy_args = translate(syntheticInits.toList(), localContext.prev); |
|
251 |
|
252 //build a sam instance using an indy call to the meta-factory |
|
253 int refKind = referenceKind(sym); |
|
254 |
|
255 //convert to an invokedynamic call |
|
256 result = makeMetaFactoryIndyCall(tree, tree.targetType, refKind, sym, indy_args); |
|
257 } |
|
258 |
|
259 private JCIdent makeThis(Type type, Symbol owner) { |
|
260 VarSymbol _this = new VarSymbol(PARAMETER | FINAL | SYNTHETIC, |
|
261 names._this, |
|
262 type, |
|
263 owner); |
|
264 return make.Ident(_this); |
|
265 } |
|
266 |
|
267 /** |
|
268 * Translate a method reference into an invokedynamic call to the |
|
269 * meta-factory. |
|
270 * @param tree |
|
271 */ |
|
272 @Override |
|
273 public void visitReference(JCMemberReference tree) { |
|
274 ReferenceTranslationContext localContext = (ReferenceTranslationContext)context; |
|
275 |
|
276 //first determine the method symbol to be used to generate the sam instance |
|
277 //this is either the method reference symbol, or the bridged reference symbol |
|
278 Symbol refSym = localContext.needsBridge() ? |
|
279 localContext.bridgeSym : |
|
280 tree.sym; |
|
281 |
|
282 //build the bridge method, if needed |
|
283 if (localContext.needsBridge()) { |
|
284 bridgeMemberReference(tree, localContext); |
|
285 } |
|
286 |
|
287 //the qualifying expression is treated as a special captured arg |
|
288 JCExpression init; |
|
289 switch(tree.kind) { |
|
290 |
|
291 case IMPLICIT_INNER: /** Inner # new */ |
|
292 case SUPER: /** super # instMethod */ |
|
293 init = makeThis( |
|
294 localContext.owner.owner.asType(), |
|
295 localContext.owner); |
|
296 break; |
|
297 |
|
298 case BOUND: /** Expr # instMethod */ |
|
299 init = tree.getQualifierExpression(); |
|
300 break; |
|
301 |
|
302 case STATIC_EVAL: /** Expr # staticMethod */ |
|
303 case UNBOUND: /** Type # instMethod */ |
|
304 case STATIC: /** Type # staticMethod */ |
|
305 case TOPLEVEL: /** Top level # new */ |
|
306 init = null; |
|
307 break; |
|
308 |
|
309 default: |
|
310 throw new InternalError("Should not have an invalid kind"); |
|
311 } |
|
312 |
|
313 List<JCExpression> indy_args = init==null? List.<JCExpression>nil() : translate(List.of(init), localContext.prev); |
|
314 |
|
315 |
|
316 //build a sam instance using an indy call to the meta-factory |
|
317 result = makeMetaFactoryIndyCall(tree, tree.targetType, localContext.referenceKind(), refSym, indy_args); |
|
318 |
|
319 //if we had a static reference with non-static qualifier, add a let |
|
320 //expression to force the evaluation of the qualifier expr |
|
321 if (tree.hasKind(ReferenceKind.STATIC_EVAL)) { |
|
322 VarSymbol rec = new VarSymbol(0, names.fromString("rec$"), tree.getQualifierExpression().type, localContext.owner); |
|
323 JCVariableDecl recDef = make.VarDef(rec, tree.getQualifierExpression()); |
|
324 result = make.LetExpr(recDef, result).setType(tree.type); |
|
325 } |
|
326 } |
|
327 |
|
328 /** |
|
329 * Translate identifiers within a lambda to the mapped identifier |
|
330 * @param tree |
|
331 */ |
|
332 @Override |
|
333 public void visitIdent(JCIdent tree) { |
|
334 if (context == null || !analyzer.lambdaIdentSymbolFilter(tree.sym)) { |
|
335 super.visitIdent(tree); |
|
336 } else { |
|
337 LambdaTranslationContext lambdaContext = (LambdaTranslationContext) context; |
|
338 if (lambdaContext.getSymbolMap(PARAM).containsKey(tree.sym)) { |
|
339 Symbol translatedSym = lambdaContext.getSymbolMap(PARAM).get(tree.sym); |
|
340 result = make.Ident(translatedSym).setType(tree.type); |
|
341 } else if (lambdaContext.getSymbolMap(LOCAL_VAR).containsKey(tree.sym)) { |
|
342 Symbol translatedSym = lambdaContext.getSymbolMap(LOCAL_VAR).get(tree.sym); |
|
343 result = make.Ident(translatedSym).setType(tree.type); |
|
344 } else if (lambdaContext.getSymbolMap(CAPTURED_VAR).containsKey(tree.sym)) { |
|
345 Symbol translatedSym = lambdaContext.getSymbolMap(CAPTURED_VAR).get(tree.sym); |
|
346 result = make.Ident(translatedSym).setType(tree.type); |
|
347 } else { |
|
348 if (tree.sym.owner.kind == Kinds.TYP) { |
|
349 for (Map.Entry<Symbol, Symbol> encl_entry : lambdaContext.getSymbolMap(CAPTURED_THIS).entrySet()) { |
|
350 if (tree.sym.isMemberOf((ClassSymbol) encl_entry.getKey(), types)) { |
|
351 JCExpression enclRef = make.Ident(encl_entry.getValue()); |
|
352 result = tree.sym.name == names._this |
|
353 ? enclRef.setType(tree.type) |
|
354 : make.Select(enclRef, tree.sym).setType(tree.type); |
|
355 result = tree; |
|
356 return; |
|
357 } |
|
358 } |
|
359 } |
|
360 //access to untranslated symbols (i.e. compile-time constants, |
|
361 //members defined inside the lambda body, etc.) ) |
|
362 super.visitIdent(tree); |
|
363 } |
|
364 } |
|
365 } |
|
366 |
|
367 @Override |
|
368 public void visitVarDef(JCVariableDecl tree) { |
|
369 LambdaTranslationContext lambdaContext = (LambdaTranslationContext)context; |
|
370 if (context != null && lambdaContext.getSymbolMap(LOCAL_VAR).containsKey(tree.sym)) { |
|
371 JCExpression init = translate(tree.init); |
|
372 result = make.VarDef((VarSymbol)lambdaContext.getSymbolMap(LOCAL_VAR).get(tree.sym), init); |
|
373 } else { |
|
374 super.visitVarDef(tree); |
|
375 } |
|
376 } |
|
377 |
|
378 // </editor-fold> |
|
379 |
|
380 // <editor-fold defaultstate="collapsed" desc="Translation helper methods"> |
|
381 |
|
382 private JCBlock makeLambdaBody(JCLambda tree, JCMethodDecl lambdaMethodDecl) { |
|
383 return tree.getBodyKind() == JCLambda.BodyKind.EXPRESSION ? |
|
384 makeLambdaExpressionBody((JCExpression)tree.body, lambdaMethodDecl) : |
|
385 makeLambdaStatementBody((JCBlock)tree.body, lambdaMethodDecl, tree.canCompleteNormally); |
|
386 } |
|
387 |
|
388 private JCBlock makeLambdaExpressionBody(JCExpression expr, JCMethodDecl lambdaMethodDecl) { |
|
389 Type restype = lambdaMethodDecl.type.getReturnType(); |
|
390 boolean isLambda_void = expr.type.hasTag(VOID); |
|
391 boolean isTarget_void = restype.hasTag(VOID); |
|
392 boolean isTarget_Void = types.isSameType(restype, types.boxedClass(syms.voidType).type); |
|
393 if (isTarget_void) { |
|
394 //target is void: |
|
395 // BODY; |
|
396 JCStatement stat = make.Exec(expr); |
|
397 return make.Block(0, List.<JCStatement>of(stat)); |
|
398 } else if (isLambda_void && isTarget_Void) { |
|
399 //void to Void conversion: |
|
400 // BODY; return null; |
|
401 ListBuffer<JCStatement> stats = ListBuffer.lb(); |
|
402 stats.append(make.Exec(expr)); |
|
403 stats.append(make.Return(make.Literal(BOT, null).setType(syms.botType))); |
|
404 return make.Block(0, stats.toList()); |
|
405 } else { |
|
406 //non-void to non-void conversion: |
|
407 // return (TYPE)BODY; |
|
408 JCExpression retExpr = transTypes.coerce(attrEnv, expr, restype); |
|
409 return make.Block(0, List.<JCStatement>of(make.Return(retExpr))); |
|
410 } |
|
411 } |
|
412 |
|
413 private JCBlock makeLambdaStatementBody(JCBlock block, final JCMethodDecl lambdaMethodDecl, boolean completeNormally) { |
|
414 final Type restype = lambdaMethodDecl.type.getReturnType(); |
|
415 final boolean isTarget_void = restype.hasTag(VOID); |
|
416 boolean isTarget_Void = types.isSameType(restype, types.boxedClass(syms.voidType).type); |
|
417 |
|
418 class LambdaBodyTranslator extends TreeTranslator { |
|
419 |
|
420 @Override |
|
421 public void visitClassDef(JCClassDecl tree) { |
|
422 //do NOT recurse on any inner classes |
|
423 result = tree; |
|
424 } |
|
425 |
|
426 @Override |
|
427 public void visitLambda(JCLambda tree) { |
|
428 //do NOT recurse on any nested lambdas |
|
429 result = tree; |
|
430 } |
|
431 |
|
432 @Override |
|
433 public void visitReturn(JCReturn tree) { |
|
434 boolean isLambda_void = tree.expr == null; |
|
435 if (isTarget_void && !isLambda_void) { |
|
436 //Void to void conversion: |
|
437 // { TYPE $loc = RET-EXPR; return; } |
|
438 VarSymbol loc = makeSyntheticVar(0, names.fromString("$loc"), tree.expr.type, lambdaMethodDecl.sym); |
|
439 JCVariableDecl varDef = make.VarDef(loc, tree.expr); |
|
440 result = make.Block(0, List.<JCStatement>of(varDef, make.Return(null))); |
|
441 } else if (!isTarget_void || !isLambda_void) { |
|
442 //non-void to non-void conversion: |
|
443 // return (TYPE)RET-EXPR; |
|
444 tree.expr = transTypes.coerce(attrEnv, tree.expr, restype); |
|
445 result = tree; |
|
446 } else { |
|
447 result = tree; |
|
448 } |
|
449 |
|
450 } |
|
451 } |
|
452 |
|
453 JCBlock trans_block = new LambdaBodyTranslator().translate(block); |
|
454 if (completeNormally && isTarget_Void) { |
|
455 //there's no return statement and the lambda (possibly inferred) |
|
456 //return type is java.lang.Void; emit a synthetic return statement |
|
457 trans_block.stats = trans_block.stats.append(make.Return(make.Literal(BOT, null).setType(syms.botType))); |
|
458 } |
|
459 return trans_block; |
|
460 } |
|
461 |
|
462 /** |
|
463 * Create new synthetic method with given flags, name, type, owner |
|
464 */ |
|
465 private MethodSymbol makeSyntheticMethod(long flags, Name name, Type type, Symbol owner) { |
|
466 return new MethodSymbol(flags | SYNTHETIC, name, type, owner); |
|
467 } |
|
468 |
|
469 /** |
|
470 * Create new synthetic variable with given flags, name, type, owner |
|
471 */ |
|
472 private VarSymbol makeSyntheticVar(long flags, String name, Type type, Symbol owner) { |
|
473 return makeSyntheticVar(flags, names.fromString(name), type, owner); |
|
474 } |
|
475 |
|
476 /** |
|
477 * Create new synthetic variable with given flags, name, type, owner |
|
478 */ |
|
479 private VarSymbol makeSyntheticVar(long flags, Name name, Type type, Symbol owner) { |
|
480 return new VarSymbol(flags | SYNTHETIC, name, type, owner); |
|
481 } |
|
482 |
|
483 /** |
|
484 * Set varargsElement field on a given tree (must be either a new class tree |
|
485 * or a method call tree) |
|
486 */ |
|
487 private void setVarargsIfNeeded(JCTree tree, Type varargsElement) { |
|
488 if (varargsElement != null) { |
|
489 switch (tree.getTag()) { |
|
490 case APPLY: ((JCMethodInvocation)tree).varargsElement = varargsElement; break; |
|
491 case NEWCLASS: ((JCNewClass)tree).varargsElement = varargsElement; break; |
|
492 default: throw new AssertionError(); |
|
493 } |
|
494 } |
|
495 } |
|
496 |
|
497 /** |
|
498 * Convert method/constructor arguments by inserting appropriate cast |
|
499 * as required by type-erasure - this is needed when bridging a lambda/method |
|
500 * reference, as the bridged signature might require downcast to be compatible |
|
501 * with the generated signature. |
|
502 */ |
|
503 private List<JCExpression> convertArgs(Symbol meth, List<JCExpression> args, Type varargsElement) { |
|
504 Assert.check(meth.kind == Kinds.MTH); |
|
505 List<Type> formals = types.erasure(meth.type).getParameterTypes(); |
|
506 if (varargsElement != null) { |
|
507 Assert.check((meth.flags() & VARARGS) != 0); |
|
508 } |
|
509 return transTypes.translateArgs(args, formals, varargsElement, attrEnv); |
|
510 } |
|
511 |
|
512 // </editor-fold> |
|
513 |
|
514 private MethodSymbol makeSamDescriptor(Type targetType) { |
|
515 return (MethodSymbol)types.findDescriptorSymbol(targetType.tsym); |
|
516 } |
|
517 |
|
518 private Type makeFunctionalDescriptorType(Type targetType, MethodSymbol samDescriptor, boolean erased) { |
|
519 Type descType = types.memberType(targetType, samDescriptor); |
|
520 return erased ? types.erasure(descType) : descType; |
|
521 } |
|
522 |
|
523 private Type makeFunctionalDescriptorType(Type targetType, boolean erased) { |
|
524 return makeFunctionalDescriptorType(targetType, makeSamDescriptor(targetType), erased); |
|
525 } |
|
526 |
|
527 /** |
|
528 * Generate an adapter method "bridge" for a method reference which cannot |
|
529 * be used directly. |
|
530 */ |
|
531 private class MemberReferenceBridger { |
|
532 |
|
533 private final JCMemberReference tree; |
|
534 private final ReferenceTranslationContext localContext; |
|
535 private final ListBuffer<JCExpression> args = ListBuffer.lb(); |
|
536 private final ListBuffer<JCVariableDecl> params = ListBuffer.lb(); |
|
537 |
|
538 MemberReferenceBridger(JCMemberReference tree, ReferenceTranslationContext localContext) { |
|
539 this.tree = tree; |
|
540 this.localContext = localContext; |
|
541 } |
|
542 |
|
543 /** |
|
544 * Generate the bridge |
|
545 */ |
|
546 JCMethodDecl bridge() { |
|
547 int prevPos = make.pos; |
|
548 try { |
|
549 make.at(tree); |
|
550 Type samDesc = localContext.bridgedRefSig(); |
|
551 List<Type> samPTypes = samDesc.getParameterTypes(); |
|
552 |
|
553 //an extra argument is prepended to the signature of the bridge in case |
|
554 //the member reference is an instance method reference (in which case |
|
555 //the receiver expression is passed to the bridge itself). |
|
556 Type recType = null; |
|
557 switch (tree.kind) { |
|
558 case IMPLICIT_INNER: |
|
559 recType = tree.sym.owner.type.getEnclosingType(); |
|
560 break; |
|
561 case BOUND: |
|
562 recType = tree.getQualifierExpression().type; |
|
563 break; |
|
564 case UNBOUND: |
|
565 recType = samPTypes.head; |
|
566 samPTypes = samPTypes.tail; |
|
567 break; |
|
568 } |
|
569 |
|
570 //generate the parameter list for the bridged member reference - the |
|
571 //bridge signature will match the signature of the target sam descriptor |
|
572 |
|
573 VarSymbol rcvr = (recType == null) |
|
574 ? null |
|
575 : addParameter("rec$", recType, false); |
|
576 |
|
577 List<Type> refPTypes = tree.sym.type.getParameterTypes(); |
|
578 int refSize = refPTypes.size(); |
|
579 int samSize = samPTypes.size(); |
|
580 int last = localContext.needsVarArgsConversion() ? refSize - 1 : refSize; // Last parameter to copy from referenced method |
|
581 |
|
582 List<Type> l = refPTypes; |
|
583 // Use parameter types of the referenced method, excluding final var args |
|
584 for (int i = 0; l.nonEmpty() && i < last; ++i) { |
|
585 addParameter("x$" + i, l.head, true); |
|
586 l = l.tail; |
|
587 } |
|
588 // Flatten out the var args |
|
589 for (int i = last; i < samSize; ++i) { |
|
590 addParameter("xva$" + i, tree.varargsElement, true); |
|
591 } |
|
592 |
|
593 //generate the bridge method declaration |
|
594 JCMethodDecl bridgeDecl = make.MethodDef(make.Modifiers(localContext.bridgeSym.flags()), |
|
595 localContext.bridgeSym.name, |
|
596 make.QualIdent(samDesc.getReturnType().tsym), |
|
597 List.<JCTypeParameter>nil(), |
|
598 params.toList(), |
|
599 tree.sym.type.getThrownTypes() == null |
|
600 ? List.<JCExpression>nil() |
|
601 : make.Types(tree.sym.type.getThrownTypes()), |
|
602 null, |
|
603 null); |
|
604 bridgeDecl.sym = (MethodSymbol) localContext.bridgeSym; |
|
605 bridgeDecl.type = localContext.bridgeSym.type = types.createMethodTypeWithParameters(samDesc, TreeInfo.types(params.toList())); |
|
606 |
|
607 //bridge method body generation - this can be either a method call or a |
|
608 //new instance creation expression, depending on the member reference kind |
|
609 JCExpression bridgeExpr = (tree.getMode() == ReferenceMode.INVOKE) |
|
610 ? bridgeExpressionInvoke(rcvr) |
|
611 : bridgeExpressionNew(); |
|
612 |
|
613 //the body is either a return expression containing a method call, |
|
614 //or the method call itself, depending on whether the return type of |
|
615 //the bridge is non-void/void. |
|
616 bridgeDecl.body = makeLambdaExpressionBody(bridgeExpr, bridgeDecl); |
|
617 |
|
618 return bridgeDecl; |
|
619 } finally { |
|
620 make.at(prevPos); |
|
621 } |
|
622 } |
|
623 |
|
624 /** |
|
625 * determine the receiver of the bridged method call - the receiver can |
|
626 * be either the synthetic receiver parameter or a type qualifier; the |
|
627 * original qualifier expression is never used here, as it might refer |
|
628 * to symbols not available in the static context of the bridge |
|
629 */ |
|
630 private JCExpression bridgeExpressionInvoke(VarSymbol rcvr) { |
|
631 JCExpression qualifier = |
|
632 tree.sym.isStatic() ? |
|
633 make.Type(tree.sym.owner.type) : |
|
634 (rcvr != null) ? |
|
635 make.Ident(rcvr) : |
|
636 tree.getQualifierExpression(); |
|
637 |
|
638 //create the qualifier expression |
|
639 JCFieldAccess select = make.Select(qualifier, tree.sym.name); |
|
640 select.sym = tree.sym; |
|
641 select.type = tree.sym.erasure(types); |
|
642 |
|
643 //create the method call expression |
|
644 JCExpression apply = make.Apply(List.<JCExpression>nil(), select, |
|
645 convertArgs(tree.sym, args.toList(), tree.varargsElement)).setType(tree.sym.erasure(types).getReturnType()); |
|
646 |
|
647 apply = transTypes.coerce(apply, localContext.generatedRefSig().getReturnType()); |
|
648 setVarargsIfNeeded(apply, tree.varargsElement); |
|
649 return apply; |
|
650 } |
|
651 |
|
652 /** |
|
653 * the enclosing expression is either 'null' (no enclosing type) or set |
|
654 * to the first bridge synthetic parameter |
|
655 */ |
|
656 private JCExpression bridgeExpressionNew() { |
|
657 JCExpression encl = null; |
|
658 switch (tree.kind) { |
|
659 case UNBOUND: |
|
660 case IMPLICIT_INNER: |
|
661 encl = make.Ident(params.first()); |
|
662 } |
|
663 |
|
664 //create the instance creation expression |
|
665 JCNewClass newClass = make.NewClass(encl, |
|
666 List.<JCExpression>nil(), |
|
667 make.Type(tree.getQualifierExpression().type), |
|
668 convertArgs(tree.sym, args.toList(), tree.varargsElement), |
|
669 null); |
|
670 newClass.constructor = tree.sym; |
|
671 newClass.constructorType = tree.sym.erasure(types); |
|
672 newClass.type = tree.getQualifierExpression().type; |
|
673 setVarargsIfNeeded(newClass, tree.varargsElement); |
|
674 return newClass; |
|
675 } |
|
676 |
|
677 private VarSymbol addParameter(String name, Type p, boolean genArg) { |
|
678 VarSymbol vsym = new VarSymbol(0, names.fromString(name), p, localContext.bridgeSym); |
|
679 params.append(make.VarDef(vsym, null)); |
|
680 if (genArg) { |
|
681 args.append(make.Ident(vsym)); |
|
682 } |
|
683 return vsym; |
|
684 } |
|
685 } |
|
686 |
|
687 /** |
|
688 * Bridges a member reference - this is needed when: |
|
689 * * Var args in the referenced method need to be flattened away |
|
690 * * super is used |
|
691 */ |
|
692 private void bridgeMemberReference(JCMemberReference tree, ReferenceTranslationContext localContext) { |
|
693 JCMethodDecl bridgeDecl = (new MemberReferenceBridger(tree, localContext).bridge()); |
|
694 translatedMethodList = translatedMethodList.prepend(bridgeDecl); |
|
695 } |
|
696 |
|
697 /** |
|
698 * Generate an indy method call to the meta factory |
|
699 */ |
|
700 private JCExpression makeMetaFactoryIndyCall(JCExpression tree, Type targetType, int refKind, Symbol refSym, List<JCExpression> indy_args) { |
|
701 //determine the static bsm args |
|
702 Type mtype = makeFunctionalDescriptorType(targetType, true); |
|
703 List<Object> staticArgs = List.<Object>of( |
|
704 new Pool.MethodHandle(ClassFile.REF_invokeInterface, types.findDescriptorSymbol(targetType.tsym)), |
|
705 new Pool.MethodHandle(refKind, refSym), |
|
706 new MethodType(mtype.getParameterTypes(), |
|
707 mtype.getReturnType(), |
|
708 mtype.getThrownTypes(), |
|
709 syms.methodClass)); |
|
710 |
|
711 //computed indy arg types |
|
712 ListBuffer<Type> indy_args_types = ListBuffer.lb(); |
|
713 for (JCExpression arg : indy_args) { |
|
714 indy_args_types.append(arg.type); |
|
715 } |
|
716 |
|
717 //finally, compute the type of the indy call |
|
718 MethodType indyType = new MethodType(indy_args_types.toList(), |
|
719 tree.type, |
|
720 List.<Type>nil(), |
|
721 syms.methodClass); |
|
722 |
|
723 return makeIndyCall(tree, syms.lambdaMetafactory, names.metaFactory, staticArgs, indyType, indy_args); |
|
724 } |
|
725 |
|
726 /** |
|
727 * Generate an indy method call with given name, type and static bootstrap |
|
728 * arguments types |
|
729 */ |
|
730 private JCExpression makeIndyCall(DiagnosticPosition pos, Type site, Name bsmName, List<Object> staticArgs, MethodType indyType, List<JCExpression> indyArgs) { |
|
731 int prevPos = make.pos; |
|
732 try { |
|
733 make.at(pos); |
|
734 List<Type> bsm_staticArgs = List.of(syms.methodHandleLookupType, |
|
735 syms.stringType, |
|
736 syms.methodTypeType).appendList(bsmStaticArgToTypes(staticArgs)); |
|
737 |
|
738 Symbol bsm = rs.resolveInternalMethod(pos, attrEnv, site, |
|
739 bsmName, bsm_staticArgs, List.<Type>nil()); |
|
740 |
|
741 DynamicMethodSymbol dynSym = |
|
742 new DynamicMethodSymbol(names.lambda, |
|
743 syms.noSymbol, |
|
744 bsm.isStatic() ? ClassFile.REF_invokeStatic : ClassFile.REF_invokeVirtual, |
|
745 (MethodSymbol)bsm, |
|
746 indyType, |
|
747 staticArgs.toArray()); |
|
748 |
|
749 JCFieldAccess qualifier = make.Select(make.QualIdent(site.tsym), bsmName); |
|
750 qualifier.sym = dynSym; |
|
751 qualifier.type = indyType.getReturnType(); |
|
752 |
|
753 JCMethodInvocation proxyCall = make.Apply(List.<JCExpression>nil(), qualifier, indyArgs); |
|
754 proxyCall.type = indyType.getReturnType(); |
|
755 return proxyCall; |
|
756 } finally { |
|
757 make.at(prevPos); |
|
758 } |
|
759 } |
|
760 //where |
|
761 private List<Type> bsmStaticArgToTypes(List<Object> args) { |
|
762 ListBuffer<Type> argtypes = ListBuffer.lb(); |
|
763 for (Object arg : args) { |
|
764 argtypes.append(bsmStaticArgToType(arg)); |
|
765 } |
|
766 return argtypes.toList(); |
|
767 } |
|
768 |
|
769 private Type bsmStaticArgToType(Object arg) { |
|
770 Assert.checkNonNull(arg); |
|
771 if (arg instanceof ClassSymbol) { |
|
772 return syms.classType; |
|
773 } else if (arg instanceof Integer) { |
|
774 return syms.intType; |
|
775 } else if (arg instanceof Long) { |
|
776 return syms.longType; |
|
777 } else if (arg instanceof Float) { |
|
778 return syms.floatType; |
|
779 } else if (arg instanceof Double) { |
|
780 return syms.doubleType; |
|
781 } else if (arg instanceof String) { |
|
782 return syms.stringType; |
|
783 } else if (arg instanceof Pool.MethodHandle) { |
|
784 return syms.methodHandleType; |
|
785 } else if (arg instanceof MethodType) { |
|
786 return syms.methodTypeType; |
|
787 } else { |
|
788 Assert.error("bad static arg " + arg.getClass()); |
|
789 return null; |
|
790 } |
|
791 } |
|
792 |
|
793 /** |
|
794 * Get the opcode associated with this method reference |
|
795 */ |
|
796 private int referenceKind(Symbol refSym) { |
|
797 if (refSym.isConstructor()) { |
|
798 return ClassFile.REF_newInvokeSpecial; |
|
799 } else { |
|
800 if (refSym.isStatic()) { |
|
801 return ClassFile.REF_invokeStatic; |
|
802 } else if (refSym.enclClass().isInterface()) { |
|
803 return ClassFile.REF_invokeInterface; |
|
804 } else { |
|
805 return ClassFile.REF_invokeVirtual; |
|
806 } |
|
807 } |
|
808 } |
|
809 // </editor-fold> |
|
810 |
|
811 // <editor-fold defaultstate="collapsed" desc="Lambda/reference analyzer">\ |
|
812 /** |
|
813 * This visitor collects information about translation of a lambda expression. |
|
814 * More specifically, it keeps track of the enclosing contexts and captured locals |
|
815 * accessed by the lambda being translated (as well as other useful info). |
|
816 */ |
|
817 class LambdaAnalyzer extends TreeScanner { |
|
818 |
|
819 /** the frame stack - used to reconstruct translation info about enclosing scopes */ |
|
820 private List<Frame> frameStack; |
|
821 |
|
822 /** |
|
823 * keep the count of lambda expression (used to generate unambiguous |
|
824 * names) |
|
825 */ |
|
826 private int lambdaCount = 0; |
|
827 |
|
828 private void analyzeClass(JCClassDecl tree) { |
|
829 frameStack = List.nil(); |
|
830 scan(tree); |
|
831 } |
|
832 |
|
833 @Override |
|
834 public void visitBlock(JCBlock tree) { |
|
835 List<Frame> prevStack = frameStack; |
|
836 try { |
|
837 if (frameStack.nonEmpty() && frameStack.head.tree.hasTag(CLASSDEF)) { |
|
838 frameStack = frameStack.prepend(new Frame(tree)); |
|
839 } |
|
840 super.visitBlock(tree); |
|
841 } |
|
842 finally { |
|
843 frameStack = prevStack; |
|
844 } |
|
845 } |
|
846 |
|
847 @Override |
|
848 public void visitClassDef(JCClassDecl tree) { |
|
849 List<Frame> prevStack = frameStack; |
|
850 try { |
|
851 if (frameStack.nonEmpty() && enclosingLambda() != null) { |
|
852 tree.sym.owner = owner(); |
|
853 LambdaTranslationContext lambdaContext = (LambdaTranslationContext)contextMap.get(enclosingLambda()); |
|
854 Type encl = lambdaContext.enclosingType(); |
|
855 if (encl.hasTag(NONE)) { |
|
856 //if the translated lambda body occurs in a static context, |
|
857 //any class declaration within it must be made static |
|
858 tree.sym.flags_field |= STATIC; |
|
859 ((ClassType)tree.sym.type).setEnclosingType(Type.noType); |
|
860 } else { |
|
861 //if the translated lambda body is in an instance context |
|
862 //the enclosing type of any class declaration within it |
|
863 //must be updated to point to the new enclosing type (if any) |
|
864 ((ClassType)tree.sym.type).setEnclosingType(encl); |
|
865 } |
|
866 } |
|
867 frameStack = frameStack.prepend(new Frame(tree)); |
|
868 super.visitClassDef(tree); |
|
869 } |
|
870 finally { |
|
871 frameStack = prevStack; |
|
872 } |
|
873 if (frameStack.nonEmpty() && enclosingLambda() != null) { |
|
874 // Any class defined within a lambda is an implicit 'this' reference |
|
875 // because its constructor will reference the enclosing class |
|
876 ((LambdaTranslationContext) context()).addSymbol(tree.sym.type.getEnclosingType().tsym, CAPTURED_THIS); |
|
877 } |
|
878 } |
|
879 |
|
880 @Override |
|
881 public void visitIdent(JCIdent tree) { |
|
882 if (context() == null || !lambdaIdentSymbolFilter(tree.sym)) { |
|
883 super.visitIdent(tree); |
|
884 } else { |
|
885 if (tree.sym.kind == VAR && |
|
886 tree.sym.owner.kind == MTH && |
|
887 tree.type.constValue() == null) { |
|
888 TranslationContext<?> localContext = context(); |
|
889 while (localContext != null) { |
|
890 if (localContext.tree.getTag() == LAMBDA) { |
|
891 JCTree block = capturedDecl(localContext.depth, tree.sym); |
|
892 if (block == null) break; |
|
893 ((LambdaTranslationContext)localContext).addSymbol(tree.sym, CAPTURED_VAR); |
|
894 } |
|
895 localContext = localContext.prev; |
|
896 } |
|
897 } else if (tree.sym.owner.kind == TYP) { |
|
898 TranslationContext<?> localContext = context(); |
|
899 while (localContext != null) { |
|
900 if (localContext.tree.hasTag(LAMBDA)) { |
|
901 JCTree block = capturedDecl(localContext.depth, tree.sym); |
|
902 if (block == null) break; |
|
903 switch (block.getTag()) { |
|
904 case CLASSDEF: |
|
905 JCClassDecl cdecl = (JCClassDecl)block; |
|
906 ((LambdaTranslationContext)localContext).addSymbol(cdecl.sym, CAPTURED_THIS); |
|
907 break; |
|
908 default: |
|
909 Assert.error("bad block kind"); |
|
910 } |
|
911 } |
|
912 localContext = localContext.prev; |
|
913 } |
|
914 } |
|
915 } |
|
916 } |
|
917 |
|
918 @Override |
|
919 public void visitLambda(JCLambda tree) { |
|
920 List<Frame> prevStack = frameStack; |
|
921 try { |
|
922 LambdaTranslationContext context = (LambdaTranslationContext)makeLambdaContext(tree); |
|
923 frameStack = frameStack.prepend(new Frame(tree)); |
|
924 for (JCVariableDecl param : tree.params) { |
|
925 context.addSymbol(param.sym, PARAM); |
|
926 frameStack.head.addLocal(param.sym); |
|
927 } |
|
928 contextMap.put(tree, context); |
|
929 scan(tree.body); |
|
930 context.complete(); |
|
931 } |
|
932 finally { |
|
933 frameStack = prevStack; |
|
934 } |
|
935 } |
|
936 |
|
937 @Override |
|
938 public void visitMethodDef(JCMethodDecl tree) { |
|
939 List<Frame> prevStack = frameStack; |
|
940 try { |
|
941 frameStack = frameStack.prepend(new Frame(tree)); |
|
942 super.visitMethodDef(tree); |
|
943 } |
|
944 finally { |
|
945 frameStack = prevStack; |
|
946 } |
|
947 } |
|
948 |
|
949 @Override |
|
950 public void visitNewClass(JCNewClass tree) { |
|
951 if (lambdaNewClassFilter(context(), tree)) { |
|
952 ((LambdaTranslationContext) context()).addSymbol(tree.type.getEnclosingType().tsym, CAPTURED_THIS); |
|
953 } |
|
954 super.visitNewClass(tree); |
|
955 } |
|
956 |
|
957 @Override |
|
958 public void visitReference(JCMemberReference tree) { |
|
959 scan(tree.getQualifierExpression()); |
|
960 contextMap.put(tree, makeReferenceContext(tree)); |
|
961 } |
|
962 |
|
963 @Override |
|
964 public void visitSelect(JCFieldAccess tree) { |
|
965 if (context() != null && lambdaSelectSymbolFilter(tree.sym)) { |
|
966 TranslationContext<?> localContext = context(); |
|
967 while (localContext != null) { |
|
968 if (localContext.tree.hasTag(LAMBDA)) { |
|
969 JCClassDecl clazz = (JCClassDecl)capturedDecl(localContext.depth, tree.sym); |
|
970 if (clazz == null) break; |
|
971 ((LambdaTranslationContext)localContext).addSymbol(clazz.sym, CAPTURED_THIS); |
|
972 } |
|
973 localContext = localContext.prev; |
|
974 } |
|
975 scan(tree.selected); |
|
976 } else { |
|
977 super.visitSelect(tree); |
|
978 } |
|
979 } |
|
980 |
|
981 @Override |
|
982 public void visitVarDef(JCVariableDecl tree) { |
|
983 if (frameStack.head.tree.hasTag(LAMBDA)) { |
|
984 ((LambdaTranslationContext)context()).addSymbol(tree.sym, LOCAL_VAR); |
|
985 } |
|
986 List<Frame> prevStack = frameStack; |
|
987 try { |
|
988 if (tree.sym.owner.kind == MTH) { |
|
989 frameStack.head.addLocal(tree.sym); |
|
990 } |
|
991 frameStack = frameStack.prepend(new Frame(tree)); |
|
992 super.visitVarDef(tree); |
|
993 } |
|
994 finally { |
|
995 frameStack = prevStack; |
|
996 } |
|
997 } |
|
998 |
|
999 private Name lambdaName() { |
|
1000 return names.lambda.append(names.fromString("$" + lambdaCount++)); |
|
1001 } |
|
1002 |
|
1003 /** |
|
1004 * Return a valid owner given the current declaration stack |
|
1005 * (required to skip synthetic lambda symbols) |
|
1006 */ |
|
1007 private Symbol owner() { |
|
1008 List<Frame> frameStack2 = frameStack; |
|
1009 while (frameStack2.nonEmpty()) { |
|
1010 switch (frameStack2.head.tree.getTag()) { |
|
1011 case VARDEF: |
|
1012 if (((JCVariableDecl)frameStack2.head.tree).sym.isLocal()) { |
|
1013 frameStack2 = frameStack2.tail; |
|
1014 break; |
|
1015 } |
|
1016 JCClassDecl cdecl = (JCClassDecl)frameStack2.tail.head.tree; |
|
1017 return makeSyntheticMethod(((JCVariableDecl)frameStack2.head.tree).sym.flags() & STATIC, names.empty, null, cdecl.sym); |
|
1018 case BLOCK: |
|
1019 JCClassDecl cdecl2 = (JCClassDecl)frameStack2.tail.head.tree; |
|
1020 return makeSyntheticMethod(((JCBlock)frameStack2.head.tree).flags & STATIC | Flags.BLOCK, names.empty, null, cdecl2.sym); |
|
1021 case CLASSDEF: |
|
1022 return ((JCClassDecl)frameStack2.head.tree).sym; |
|
1023 case METHODDEF: |
|
1024 return ((JCMethodDecl)frameStack2.head.tree).sym; |
|
1025 case LAMBDA: |
|
1026 return ((LambdaTranslationContext)contextMap.get(frameStack2.head.tree)).translatedSym; |
|
1027 default: |
|
1028 frameStack2 = frameStack2.tail; |
|
1029 } |
|
1030 } |
|
1031 Assert.error(); |
|
1032 return null; |
|
1033 } |
|
1034 |
|
1035 private JCTree enclosingLambda() { |
|
1036 List<Frame> frameStack2 = frameStack; |
|
1037 while (frameStack2.nonEmpty()) { |
|
1038 switch (frameStack2.head.tree.getTag()) { |
|
1039 case CLASSDEF: |
|
1040 case METHODDEF: |
|
1041 return null; |
|
1042 case LAMBDA: |
|
1043 return frameStack2.head.tree; |
|
1044 default: |
|
1045 frameStack2 = frameStack2.tail; |
|
1046 } |
|
1047 } |
|
1048 Assert.error(); |
|
1049 return null; |
|
1050 } |
|
1051 |
|
1052 /** |
|
1053 * Return the declaration corresponding to a symbol in the enclosing |
|
1054 * scope; the depth parameter is used to filter out symbols defined |
|
1055 * in nested scopes (which do not need to undergo capture). |
|
1056 */ |
|
1057 private JCTree capturedDecl(int depth, Symbol sym) { |
|
1058 int currentDepth = frameStack.size() - 1; |
|
1059 for (Frame block : frameStack) { |
|
1060 switch (block.tree.getTag()) { |
|
1061 case CLASSDEF: |
|
1062 ClassSymbol clazz = ((JCClassDecl)block.tree).sym; |
|
1063 if (sym.isMemberOf(clazz, types)) { |
|
1064 return currentDepth > depth ? null : block.tree; |
|
1065 } |
|
1066 break; |
|
1067 case VARDEF: |
|
1068 if (((JCVariableDecl)block.tree).sym == sym && |
|
1069 sym.owner.kind == MTH) { //only locals are captured |
|
1070 return currentDepth > depth ? null : block.tree; |
|
1071 } |
|
1072 break; |
|
1073 case BLOCK: |
|
1074 case METHODDEF: |
|
1075 case LAMBDA: |
|
1076 if (block.locals != null && block.locals.contains(sym)) { |
|
1077 return currentDepth > depth ? null : block.tree; |
|
1078 } |
|
1079 break; |
|
1080 default: |
|
1081 Assert.error("bad decl kind " + block.tree.getTag()); |
|
1082 } |
|
1083 currentDepth--; |
|
1084 } |
|
1085 return null; |
|
1086 } |
|
1087 |
|
1088 private TranslationContext<?> context() { |
|
1089 for (Frame frame : frameStack) { |
|
1090 TranslationContext<?> context = contextMap.get(frame.tree); |
|
1091 if (context != null) { |
|
1092 return context; |
|
1093 } |
|
1094 } |
|
1095 return null; |
|
1096 } |
|
1097 |
|
1098 /** |
|
1099 * This is used to filter out those identifiers that needs to be adjusted |
|
1100 * when translating away lambda expressions |
|
1101 */ |
|
1102 private boolean lambdaIdentSymbolFilter(Symbol sym) { |
|
1103 return (sym.kind == VAR || sym.kind == MTH) |
|
1104 && !sym.isStatic() |
|
1105 && sym.name != names.init; |
|
1106 } |
|
1107 |
|
1108 private boolean lambdaSelectSymbolFilter(Symbol sym) { |
|
1109 return (sym.kind == VAR || sym.kind == MTH) && |
|
1110 !sym.isStatic() && |
|
1111 (sym.name == names._this || |
|
1112 sym.name == names._super); |
|
1113 } |
|
1114 |
|
1115 /** |
|
1116 * This is used to filter out those new class expressions that need to |
|
1117 * be qualified with an enclosing tree |
|
1118 */ |
|
1119 private boolean lambdaNewClassFilter(TranslationContext<?> context, JCNewClass tree) { |
|
1120 if (context != null |
|
1121 && tree.encl == null |
|
1122 && tree.def == null |
|
1123 && tree.type.getEnclosingType().hasTag(NONE)) { |
|
1124 Type encl = tree.type.getEnclosingType(); |
|
1125 Type current = context.owner.enclClass().type; |
|
1126 while (current.hasTag(NONE)) { |
|
1127 if (current.tsym.isSubClass(encl.tsym, types)) { |
|
1128 return true; |
|
1129 } |
|
1130 current = current.getEnclosingType(); |
|
1131 } |
|
1132 return false; |
|
1133 } else { |
|
1134 return false; |
|
1135 } |
|
1136 } |
|
1137 |
|
1138 private TranslationContext<JCLambda> makeLambdaContext(JCLambda tree) { |
|
1139 return new LambdaTranslationContext(tree); |
|
1140 } |
|
1141 |
|
1142 private TranslationContext<JCMemberReference> makeReferenceContext(JCMemberReference tree) { |
|
1143 return new ReferenceTranslationContext(tree); |
|
1144 } |
|
1145 |
|
1146 private class Frame { |
|
1147 final JCTree tree; |
|
1148 List<Symbol> locals; |
|
1149 |
|
1150 public Frame(JCTree tree) { |
|
1151 this.tree = tree; |
|
1152 } |
|
1153 |
|
1154 void addLocal(Symbol sym) { |
|
1155 if (locals == null) { |
|
1156 locals = List.nil(); |
|
1157 } |
|
1158 locals = locals.prepend(sym); |
|
1159 } |
|
1160 } |
|
1161 |
|
1162 /** |
|
1163 * This class is used to store important information regarding translation of |
|
1164 * lambda expression/method references (see subclasses). |
|
1165 */ |
|
1166 private abstract class TranslationContext<T extends JCTree> { |
|
1167 |
|
1168 /** the underlying (untranslated) tree */ |
|
1169 T tree; |
|
1170 |
|
1171 /** points to the adjusted enclosing scope in which this lambda/mref expression occurs */ |
|
1172 Symbol owner; |
|
1173 |
|
1174 /** the depth of this lambda expression in the frame stack */ |
|
1175 int depth; |
|
1176 |
|
1177 /** the enclosing translation context (set for nested lambdas/mref) */ |
|
1178 TranslationContext<?> prev; |
|
1179 |
|
1180 TranslationContext(T tree) { |
|
1181 this.tree = tree; |
|
1182 this.owner = owner(); |
|
1183 this.depth = frameStack.size() - 1; |
|
1184 this.prev = context(); |
|
1185 } |
|
1186 } |
|
1187 |
|
1188 /** |
|
1189 * This class retains all the useful information about a lambda expression; |
|
1190 * the contents of this class are filled by the LambdaAnalyzer visitor, |
|
1191 * and the used by the main translation routines in order to adjust references |
|
1192 * to captured locals/members, etc. |
|
1193 */ |
|
1194 private class LambdaTranslationContext extends TranslationContext<JCLambda> { |
|
1195 |
|
1196 /** variable in the enclosing context to which this lambda is assigned */ |
|
1197 Symbol self; |
|
1198 |
|
1199 /** map from original to translated lambda parameters */ |
|
1200 Map<Symbol, Symbol> lambdaParams = new LinkedHashMap<Symbol, Symbol>(); |
|
1201 |
|
1202 /** map from original to translated lambda locals */ |
|
1203 Map<Symbol, Symbol> lambdaLocals = new LinkedHashMap<Symbol, Symbol>(); |
|
1204 |
|
1205 /** map from variables in enclosing scope to translated synthetic parameters */ |
|
1206 Map<Symbol, Symbol> capturedLocals = new LinkedHashMap<Symbol, Symbol>(); |
|
1207 |
|
1208 /** map from class symbols to translated synthetic parameters (for captured member access) */ |
|
1209 Map<Symbol, Symbol> capturedThis = new LinkedHashMap<Symbol, Symbol>(); |
|
1210 |
|
1211 /** the synthetic symbol for the method hoisting the translated lambda */ |
|
1212 Symbol translatedSym; |
|
1213 |
|
1214 List<JCVariableDecl> syntheticParams; |
|
1215 |
|
1216 LambdaTranslationContext(JCLambda tree) { |
|
1217 super(tree); |
|
1218 Frame frame = frameStack.head; |
|
1219 if (frame.tree.hasTag(VARDEF)) { |
|
1220 self = ((JCVariableDecl)frame.tree).sym; |
|
1221 } |
|
1222 this.translatedSym = makeSyntheticMethod(0, lambdaName(), null, owner.enclClass()); |
|
1223 } |
|
1224 |
|
1225 /** |
|
1226 * Translate a symbol of a given kind into something suitable for the |
|
1227 * synthetic lambda body |
|
1228 */ |
|
1229 Symbol translate(String name, Symbol sym, LambdaSymbolKind skind) { |
|
1230 if (skind == CAPTURED_THIS) { |
|
1231 return sym; // self represented |
|
1232 } else { |
|
1233 return makeSyntheticVar(FINAL, name, types.erasure(sym.type), translatedSym); |
|
1234 } |
|
1235 } |
|
1236 |
|
1237 void addSymbol(Symbol sym, LambdaSymbolKind skind) { |
|
1238 Map<Symbol, Symbol> transMap = null; |
|
1239 String preferredName; |
|
1240 switch (skind) { |
|
1241 case CAPTURED_THIS: |
|
1242 transMap = capturedThis; |
|
1243 preferredName = "encl$" + capturedThis.size(); |
|
1244 break; |
|
1245 case CAPTURED_VAR: |
|
1246 transMap = capturedLocals; |
|
1247 preferredName = "cap$" + capturedLocals.size(); |
|
1248 break; |
|
1249 case LOCAL_VAR: |
|
1250 transMap = lambdaLocals; |
|
1251 preferredName = sym.name.toString(); |
|
1252 break; |
|
1253 case PARAM: |
|
1254 transMap = lambdaParams; |
|
1255 preferredName = sym.name.toString(); |
|
1256 break; |
|
1257 default: throw new AssertionError(); |
|
1258 } |
|
1259 if (!transMap.containsKey(sym)) { |
|
1260 transMap.put(sym, translate(preferredName, sym, skind)); |
|
1261 } |
|
1262 } |
|
1263 |
|
1264 Map<Symbol, Symbol> getSymbolMap(LambdaSymbolKind... skinds) { |
|
1265 LinkedHashMap<Symbol, Symbol> translationMap = new LinkedHashMap<Symbol, Symbol>(); |
|
1266 for (LambdaSymbolKind skind : skinds) { |
|
1267 switch (skind) { |
|
1268 case CAPTURED_THIS: |
|
1269 translationMap.putAll(capturedThis); |
|
1270 break; |
|
1271 case CAPTURED_VAR: |
|
1272 translationMap.putAll(capturedLocals); |
|
1273 break; |
|
1274 case LOCAL_VAR: |
|
1275 translationMap.putAll(lambdaLocals); |
|
1276 break; |
|
1277 case PARAM: |
|
1278 translationMap.putAll(lambdaParams); |
|
1279 break; |
|
1280 default: throw new AssertionError(); |
|
1281 } |
|
1282 } |
|
1283 return translationMap; |
|
1284 } |
|
1285 |
|
1286 /** |
|
1287 * The translatedSym is not complete/accurate until the analysis is |
|
1288 * finished. Once the analysis is finished, the translatedSym is |
|
1289 * "completed" -- updated with type information, access modifiers, |
|
1290 * and full parameter list. |
|
1291 */ |
|
1292 void complete() { |
|
1293 if (syntheticParams != null) { |
|
1294 return; |
|
1295 } |
|
1296 boolean inInterface = translatedSym.owner.isInterface(); |
|
1297 boolean thisReferenced = !getSymbolMap(CAPTURED_THIS).isEmpty(); |
|
1298 boolean needInstance = thisReferenced || inInterface; |
|
1299 |
|
1300 // If instance access isn't needed, make it static |
|
1301 // Interface methods much be public default methods, otherwise make it private |
|
1302 translatedSym.flags_field = SYNTHETIC | (needInstance? 0 : STATIC) | (inInterface? PUBLIC | DEFAULT : PRIVATE); |
|
1303 |
|
1304 //compute synthetic params |
|
1305 ListBuffer<JCVariableDecl> params = ListBuffer.lb(); |
|
1306 |
|
1307 // The signature of the method is augmented with the following |
|
1308 // synthetic parameters: |
|
1309 // |
|
1310 // 1) reference to enclosing contexts captured by the lambda expression |
|
1311 // 2) enclosing locals captured by the lambda expression |
|
1312 for (Symbol thisSym : getSymbolMap(CAPTURED_VAR, PARAM).values()) { |
|
1313 params.append(make.VarDef((VarSymbol) thisSym, null)); |
|
1314 } |
|
1315 |
|
1316 syntheticParams = params.toList(); |
|
1317 |
|
1318 //prepend synthetic args to translated lambda method signature |
|
1319 translatedSym.type = (MethodType) types.createMethodTypeWithParameters( |
|
1320 (MethodType) generatedLambdaSig(), |
|
1321 TreeInfo.types(syntheticParams)); |
|
1322 } |
|
1323 |
|
1324 Type enclosingType() { |
|
1325 //local inner classes defined inside a lambda are always non-static |
|
1326 return owner.enclClass().type; |
|
1327 } |
|
1328 |
|
1329 Type generatedLambdaSig() { |
|
1330 return types.erasure(types.findDescriptorType(tree.targetType)); |
|
1331 } |
|
1332 } |
|
1333 |
|
1334 /** |
|
1335 * This class retains all the useful information about a method reference; |
|
1336 * the contents of this class are filled by the LambdaAnalyzer visitor, |
|
1337 * and the used by the main translation routines in order to adjust method |
|
1338 * references (i.e. in case a bridge is needed) |
|
1339 */ |
|
1340 private class ReferenceTranslationContext extends TranslationContext<JCMemberReference> { |
|
1341 |
|
1342 final boolean isSuper; |
|
1343 final Symbol bridgeSym; |
|
1344 |
|
1345 ReferenceTranslationContext(JCMemberReference tree) { |
|
1346 super(tree); |
|
1347 this.isSuper = tree.hasKind(ReferenceKind.SUPER); |
|
1348 this.bridgeSym = needsBridge() |
|
1349 ? makeSyntheticMethod(isSuper ? 0 : STATIC, |
|
1350 lambdaName().append(names.fromString("$bridge")), null, |
|
1351 owner.enclClass()) |
|
1352 : null; |
|
1353 } |
|
1354 |
|
1355 /** |
|
1356 * Get the opcode associated with this method reference |
|
1357 */ |
|
1358 int referenceKind() { |
|
1359 return LambdaToMethod.this.referenceKind(needsBridge() ? bridgeSym : tree.sym); |
|
1360 } |
|
1361 |
|
1362 boolean needsVarArgsConversion() { |
|
1363 return tree.varargsElement != null; |
|
1364 } |
|
1365 |
|
1366 /** |
|
1367 * @return Is this an array operation like clone() |
|
1368 */ |
|
1369 boolean isArrayOp() { |
|
1370 return tree.sym.owner == syms.arrayClass; |
|
1371 } |
|
1372 |
|
1373 /** |
|
1374 * Does this reference needs a bridge (i.e. var args need to be |
|
1375 * expanded or "super" is used) |
|
1376 */ |
|
1377 final boolean needsBridge() { |
|
1378 return isSuper || needsVarArgsConversion() || isArrayOp(); |
|
1379 } |
|
1380 |
|
1381 Type generatedRefSig() { |
|
1382 return types.erasure(tree.sym.type); |
|
1383 } |
|
1384 |
|
1385 Type bridgedRefSig() { |
|
1386 return types.erasure(types.findDescriptorSymbol(tree.targetType.tsym).type); |
|
1387 } |
|
1388 } |
|
1389 } |
|
1390 // </editor-fold> |
|
1391 |
|
1392 enum LambdaSymbolKind { |
|
1393 CAPTURED_VAR, |
|
1394 CAPTURED_THIS, |
|
1395 LOCAL_VAR, |
|
1396 PARAM; |
|
1397 } |
|
1398 } |