Thu, 10 Apr 2014 11:24:26 -0700
8038420: Lambda returning post-increment generates wrong code
Reviewed-by: vromero, jlahoda
1 /*
2 * Copyright (c) 2010, 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 package com.sun.tools.javac.comp;
27 import com.sun.tools.javac.tree.*;
28 import com.sun.tools.javac.tree.JCTree.*;
29 import com.sun.tools.javac.tree.JCTree.JCMemberReference.ReferenceKind;
30 import com.sun.tools.javac.tree.TreeMaker;
31 import com.sun.tools.javac.tree.TreeTranslator;
32 import com.sun.tools.javac.code.Attribute;
33 import com.sun.tools.javac.code.Kinds;
34 import com.sun.tools.javac.code.Scope;
35 import com.sun.tools.javac.code.Symbol;
36 import com.sun.tools.javac.code.Symbol.ClassSymbol;
37 import com.sun.tools.javac.code.Symbol.DynamicMethodSymbol;
38 import com.sun.tools.javac.code.Symbol.MethodSymbol;
39 import com.sun.tools.javac.code.Symbol.VarSymbol;
40 import com.sun.tools.javac.code.Symtab;
41 import com.sun.tools.javac.code.Type;
42 import com.sun.tools.javac.code.Type.MethodType;
43 import com.sun.tools.javac.code.Types;
44 import com.sun.tools.javac.comp.LambdaToMethod.LambdaAnalyzerPreprocessor.*;
45 import com.sun.tools.javac.comp.Lower.BasicFreeVarCollector;
46 import com.sun.tools.javac.jvm.*;
47 import com.sun.tools.javac.util.*;
48 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
49 import com.sun.source.tree.MemberReferenceTree.ReferenceMode;
51 import java.util.EnumMap;
52 import java.util.HashMap;
53 import java.util.LinkedHashMap;
54 import java.util.Map;
56 import static com.sun.tools.javac.comp.LambdaToMethod.LambdaSymbolKind.*;
57 import static com.sun.tools.javac.code.Flags.*;
58 import static com.sun.tools.javac.code.Kinds.*;
59 import static com.sun.tools.javac.code.TypeTag.*;
60 import static com.sun.tools.javac.tree.JCTree.Tag.*;
62 /**
63 * This pass desugars lambda expressions into static methods
64 *
65 * <p><b>This is NOT part of any supported API.
66 * If you write code that depends on this, you do so at your own risk.
67 * This code and its internal interfaces are subject to change or
68 * deletion without notice.</b>
69 */
70 public class LambdaToMethod extends TreeTranslator {
72 private Attr attr;
73 private JCDiagnostic.Factory diags;
74 private Log log;
75 private Lower lower;
76 private Names names;
77 private Symtab syms;
78 private Resolve rs;
79 private TreeMaker make;
80 private Types types;
81 private TransTypes transTypes;
82 private Env<AttrContext> attrEnv;
84 /** the analyzer scanner */
85 private LambdaAnalyzerPreprocessor analyzer;
87 /** map from lambda trees to translation contexts */
88 private Map<JCTree, TranslationContext<?>> contextMap;
90 /** current translation context (visitor argument) */
91 private TranslationContext<?> context;
93 /** info about the current class being processed */
94 private KlassInfo kInfo;
96 /** dump statistics about lambda code generation */
97 private boolean dumpLambdaToMethodStats;
99 /** force serializable representation, for stress testing **/
100 private final boolean forceSerializable;
102 /** Flag for alternate metafactories indicating the lambda object is intended to be serializable */
103 public static final int FLAG_SERIALIZABLE = 1 << 0;
105 /** Flag for alternate metafactories indicating the lambda object has multiple targets */
106 public static final int FLAG_MARKERS = 1 << 1;
108 /** Flag for alternate metafactories indicating the lambda object requires multiple bridges */
109 public static final int FLAG_BRIDGES = 1 << 2;
111 // <editor-fold defaultstate="collapsed" desc="Instantiating">
112 protected static final Context.Key<LambdaToMethod> unlambdaKey =
113 new Context.Key<LambdaToMethod>();
115 public static LambdaToMethod instance(Context context) {
116 LambdaToMethod instance = context.get(unlambdaKey);
117 if (instance == null) {
118 instance = new LambdaToMethod(context);
119 }
120 return instance;
121 }
122 private LambdaToMethod(Context context) {
123 context.put(unlambdaKey, this);
124 diags = JCDiagnostic.Factory.instance(context);
125 log = Log.instance(context);
126 lower = Lower.instance(context);
127 names = Names.instance(context);
128 syms = Symtab.instance(context);
129 rs = Resolve.instance(context);
130 make = TreeMaker.instance(context);
131 types = Types.instance(context);
132 transTypes = TransTypes.instance(context);
133 analyzer = new LambdaAnalyzerPreprocessor();
134 Options options = Options.instance(context);
135 dumpLambdaToMethodStats = options.isSet("dumpLambdaToMethodStats");
136 attr = Attr.instance(context);
137 forceSerializable = options.isSet("forceSerializable");
138 }
139 // </editor-fold>
141 private class KlassInfo {
143 /**
144 * list of methods to append
145 */
146 private ListBuffer<JCTree> appendedMethodList;
148 /**
149 * list of deserialization cases
150 */
151 private final Map<String, ListBuffer<JCStatement>> deserializeCases;
153 /**
154 * deserialize method symbol
155 */
156 private final MethodSymbol deserMethodSym;
158 /**
159 * deserialize method parameter symbol
160 */
161 private final VarSymbol deserParamSym;
163 private final JCClassDecl clazz;
165 private KlassInfo(JCClassDecl clazz) {
166 this.clazz = clazz;
167 appendedMethodList = new ListBuffer<>();
168 deserializeCases = new HashMap<String, ListBuffer<JCStatement>>();
169 MethodType type = new MethodType(List.of(syms.serializedLambdaType), syms.objectType,
170 List.<Type>nil(), syms.methodClass);
171 deserMethodSym = makePrivateSyntheticMethod(STATIC, names.deserializeLambda, type, clazz.sym);
172 deserParamSym = new VarSymbol(FINAL, names.fromString("lambda"),
173 syms.serializedLambdaType, deserMethodSym);
174 }
176 private void addMethod(JCTree decl) {
177 appendedMethodList = appendedMethodList.prepend(decl);
178 }
179 }
181 // <editor-fold defaultstate="collapsed" desc="translate methods">
182 @Override
183 public <T extends JCTree> T translate(T tree) {
184 TranslationContext<?> newContext = contextMap.get(tree);
185 return translate(tree, newContext != null ? newContext : context);
186 }
188 <T extends JCTree> T translate(T tree, TranslationContext<?> newContext) {
189 TranslationContext<?> prevContext = context;
190 try {
191 context = newContext;
192 return super.translate(tree);
193 }
194 finally {
195 context = prevContext;
196 }
197 }
199 <T extends JCTree> List<T> translate(List<T> trees, TranslationContext<?> newContext) {
200 ListBuffer<T> buf = new ListBuffer<>();
201 for (T tree : trees) {
202 buf.append(translate(tree, newContext));
203 }
204 return buf.toList();
205 }
207 public JCTree translateTopLevelClass(Env<AttrContext> env, JCTree cdef, TreeMaker make) {
208 this.make = make;
209 this.attrEnv = env;
210 this.context = null;
211 this.contextMap = new HashMap<JCTree, TranslationContext<?>>();
212 return translate(cdef);
213 }
214 // </editor-fold>
216 // <editor-fold defaultstate="collapsed" desc="visitor methods">
217 /**
218 * Visit a class.
219 * Maintain the translatedMethodList across nested classes.
220 * Append the translatedMethodList to the class after it is translated.
221 * @param tree
222 */
223 @Override
224 public void visitClassDef(JCClassDecl tree) {
225 if (tree.sym.owner.kind == PCK) {
226 //analyze class
227 tree = analyzer.analyzeAndPreprocessClass(tree);
228 }
229 KlassInfo prevKlassInfo = kInfo;
230 try {
231 kInfo = new KlassInfo(tree);
232 super.visitClassDef(tree);
233 if (!kInfo.deserializeCases.isEmpty()) {
234 int prevPos = make.pos;
235 try {
236 make.at(tree);
237 kInfo.addMethod(makeDeserializeMethod(tree.sym));
238 } finally {
239 make.at(prevPos);
240 }
241 }
242 //add all translated instance methods here
243 List<JCTree> newMethods = kInfo.appendedMethodList.toList();
244 tree.defs = tree.defs.appendList(newMethods);
245 for (JCTree lambda : newMethods) {
246 tree.sym.members().enter(((JCMethodDecl)lambda).sym);
247 }
248 result = tree;
249 } finally {
250 kInfo = prevKlassInfo;
251 }
252 }
254 /**
255 * Translate a lambda into a method to be inserted into the class.
256 * Then replace the lambda site with an invokedynamic call of to lambda
257 * meta-factory, which will use the lambda method.
258 * @param tree
259 */
260 @Override
261 public void visitLambda(JCLambda tree) {
262 LambdaTranslationContext localContext = (LambdaTranslationContext)context;
263 MethodSymbol sym = (MethodSymbol)localContext.translatedSym;
264 MethodType lambdaType = (MethodType) sym.type;
266 {
267 Symbol owner = localContext.owner;
268 ListBuffer<Attribute.TypeCompound> ownerTypeAnnos = new ListBuffer<Attribute.TypeCompound>();
269 ListBuffer<Attribute.TypeCompound> lambdaTypeAnnos = new ListBuffer<Attribute.TypeCompound>();
271 for (Attribute.TypeCompound tc : owner.getRawTypeAttributes()) {
272 if (tc.position.onLambda == tree) {
273 lambdaTypeAnnos.append(tc);
274 } else {
275 ownerTypeAnnos.append(tc);
276 }
277 }
278 if (lambdaTypeAnnos.nonEmpty()) {
279 owner.setTypeAttributes(ownerTypeAnnos.toList());
280 sym.setTypeAttributes(lambdaTypeAnnos.toList());
281 }
282 }
284 //create the method declaration hoisting the lambda body
285 JCMethodDecl lambdaDecl = make.MethodDef(make.Modifiers(sym.flags_field),
286 sym.name,
287 make.QualIdent(lambdaType.getReturnType().tsym),
288 List.<JCTypeParameter>nil(),
289 localContext.syntheticParams,
290 lambdaType.getThrownTypes() == null ?
291 List.<JCExpression>nil() :
292 make.Types(lambdaType.getThrownTypes()),
293 null,
294 null);
295 lambdaDecl.sym = sym;
296 lambdaDecl.type = lambdaType;
298 //translate lambda body
299 //As the lambda body is translated, all references to lambda locals,
300 //captured variables, enclosing members are adjusted accordingly
301 //to refer to the static method parameters (rather than i.e. acessing to
302 //captured members directly).
303 lambdaDecl.body = translate(makeLambdaBody(tree, lambdaDecl));
305 //Add the method to the list of methods to be added to this class.
306 kInfo.addMethod(lambdaDecl);
308 //now that we have generated a method for the lambda expression,
309 //we can translate the lambda into a method reference pointing to the newly
310 //created method.
311 //
312 //Note that we need to adjust the method handle so that it will match the
313 //signature of the SAM descriptor - this means that the method reference
314 //should be added the following synthetic arguments:
315 //
316 // * the "this" argument if it is an instance method
317 // * enclosing locals captured by the lambda expression
319 ListBuffer<JCExpression> syntheticInits = new ListBuffer<>();
321 if (!sym.isStatic()) {
322 syntheticInits.append(makeThis(
323 sym.owner.enclClass().asType(),
324 localContext.owner.enclClass()));
325 }
327 //add captured locals
328 for (Symbol fv : localContext.getSymbolMap(CAPTURED_VAR).keySet()) {
329 if (fv != localContext.self) {
330 JCTree captured_local = make.Ident(fv).setType(fv.type);
331 syntheticInits.append((JCExpression) captured_local);
332 }
333 }
335 //then, determine the arguments to the indy call
336 List<JCExpression> indy_args = translate(syntheticInits.toList(), localContext.prev);
338 //build a sam instance using an indy call to the meta-factory
339 int refKind = referenceKind(sym);
341 //convert to an invokedynamic call
342 result = makeMetafactoryIndyCall(context, refKind, sym, indy_args);
343 }
345 private JCIdent makeThis(Type type, Symbol owner) {
346 VarSymbol _this = new VarSymbol(PARAMETER | FINAL | SYNTHETIC,
347 names._this,
348 type,
349 owner);
350 return make.Ident(_this);
351 }
353 /**
354 * Translate a method reference into an invokedynamic call to the
355 * meta-factory.
356 * @param tree
357 */
358 @Override
359 public void visitReference(JCMemberReference tree) {
360 ReferenceTranslationContext localContext = (ReferenceTranslationContext)context;
362 //first determine the method symbol to be used to generate the sam instance
363 //this is either the method reference symbol, or the bridged reference symbol
364 Symbol refSym = localContext.needsBridge()
365 ? localContext.bridgeSym
366 : localContext.isSignaturePolymorphic()
367 ? localContext.sigPolySym
368 : tree.sym;
370 //build the bridge method, if needed
371 if (localContext.needsBridge()) {
372 bridgeMemberReference(tree, localContext);
373 }
375 //the qualifying expression is treated as a special captured arg
376 JCExpression init;
377 switch(tree.kind) {
379 case IMPLICIT_INNER: /** Inner :: new */
380 case SUPER: /** super :: instMethod */
381 init = makeThis(
382 localContext.owner.enclClass().asType(),
383 localContext.owner.enclClass());
384 break;
386 case BOUND: /** Expr :: instMethod */
387 init = tree.getQualifierExpression();
388 init = attr.makeNullCheck(init);
389 break;
391 case UNBOUND: /** Type :: instMethod */
392 case STATIC: /** Type :: staticMethod */
393 case TOPLEVEL: /** Top level :: new */
394 case ARRAY_CTOR: /** ArrayType :: new */
395 init = null;
396 break;
398 default:
399 throw new InternalError("Should not have an invalid kind");
400 }
402 List<JCExpression> indy_args = init==null? List.<JCExpression>nil() : translate(List.of(init), localContext.prev);
405 //build a sam instance using an indy call to the meta-factory
406 result = makeMetafactoryIndyCall(localContext, localContext.referenceKind(), refSym, indy_args);
407 }
409 /**
410 * Translate identifiers within a lambda to the mapped identifier
411 * @param tree
412 */
413 @Override
414 public void visitIdent(JCIdent tree) {
415 if (context == null || !analyzer.lambdaIdentSymbolFilter(tree.sym)) {
416 super.visitIdent(tree);
417 } else {
418 int prevPos = make.pos;
419 try {
420 make.at(tree);
422 LambdaTranslationContext lambdaContext = (LambdaTranslationContext) context;
423 JCTree ltree = lambdaContext.translate(tree);
424 if (ltree != null) {
425 result = ltree;
426 } else {
427 //access to untranslated symbols (i.e. compile-time constants,
428 //members defined inside the lambda body, etc.) )
429 super.visitIdent(tree);
430 }
431 } finally {
432 make.at(prevPos);
433 }
434 }
435 }
437 @Override
438 public void visitVarDef(JCVariableDecl tree) {
439 LambdaTranslationContext lambdaContext = (LambdaTranslationContext)context;
440 if (context != null && lambdaContext.getSymbolMap(LOCAL_VAR).containsKey(tree.sym)) {
441 JCExpression init = translate(tree.init);
442 int prevPos = make.pos;
443 try {
444 result = make.at(tree).VarDef((VarSymbol)lambdaContext.getSymbolMap(LOCAL_VAR).get(tree.sym), init);
445 } finally {
446 make.at(prevPos);
447 }
448 } else if (context != null && lambdaContext.getSymbolMap(TYPE_VAR).containsKey(tree.sym)) {
449 JCExpression init = translate(tree.init);
450 VarSymbol xsym = (VarSymbol)lambdaContext.getSymbolMap(TYPE_VAR).get(tree.sym);
451 int prevPos = make.pos;
452 try {
453 result = make.at(tree).VarDef(xsym, init);
454 } finally {
455 make.at(prevPos);
456 }
457 // Replace the entered symbol for this variable
458 Scope sc = tree.sym.owner.members();
459 if (sc != null) {
460 sc.remove(tree.sym);
461 sc.enter(xsym);
462 }
463 } else {
464 super.visitVarDef(tree);
465 }
466 }
468 // </editor-fold>
470 // <editor-fold defaultstate="collapsed" desc="Translation helper methods">
472 private JCBlock makeLambdaBody(JCLambda tree, JCMethodDecl lambdaMethodDecl) {
473 return tree.getBodyKind() == JCLambda.BodyKind.EXPRESSION ?
474 makeLambdaExpressionBody((JCExpression)tree.body, lambdaMethodDecl) :
475 makeLambdaStatementBody((JCBlock)tree.body, lambdaMethodDecl, tree.canCompleteNormally);
476 }
478 private JCBlock makeLambdaExpressionBody(JCExpression expr, JCMethodDecl lambdaMethodDecl) {
479 Type restype = lambdaMethodDecl.type.getReturnType();
480 boolean isLambda_void = expr.type.hasTag(VOID);
481 boolean isTarget_void = restype.hasTag(VOID);
482 boolean isTarget_Void = types.isSameType(restype, types.boxedClass(syms.voidType).type);
483 int prevPos = make.pos;
484 try {
485 if (isTarget_void) {
486 //target is void:
487 // BODY;
488 JCStatement stat = make.at(expr).Exec(expr);
489 return make.Block(0, List.<JCStatement>of(stat));
490 } else if (isLambda_void && isTarget_Void) {
491 //void to Void conversion:
492 // BODY; return null;
493 ListBuffer<JCStatement> stats = new ListBuffer<>();
494 stats.append(make.at(expr).Exec(expr));
495 stats.append(make.Return(make.Literal(BOT, null).setType(syms.botType)));
496 return make.Block(0, stats.toList());
497 } else {
498 //non-void to non-void conversion:
499 // return (TYPE)BODY;
500 JCExpression retExpr = transTypes.coerce(attrEnv, expr, restype);
501 return make.at(retExpr).Block(0, List.<JCStatement>of(make.Return(retExpr)));
502 }
503 } finally {
504 make.at(prevPos);
505 }
506 }
508 private JCBlock makeLambdaStatementBody(JCBlock block, final JCMethodDecl lambdaMethodDecl, boolean completeNormally) {
509 final Type restype = lambdaMethodDecl.type.getReturnType();
510 final boolean isTarget_void = restype.hasTag(VOID);
511 boolean isTarget_Void = types.isSameType(restype, types.boxedClass(syms.voidType).type);
513 class LambdaBodyTranslator extends TreeTranslator {
515 @Override
516 public void visitClassDef(JCClassDecl tree) {
517 //do NOT recurse on any inner classes
518 result = tree;
519 }
521 @Override
522 public void visitLambda(JCLambda tree) {
523 //do NOT recurse on any nested lambdas
524 result = tree;
525 }
527 @Override
528 public void visitReturn(JCReturn tree) {
529 boolean isLambda_void = tree.expr == null;
530 if (isTarget_void && !isLambda_void) {
531 //Void to void conversion:
532 // { TYPE $loc = RET-EXPR; return; }
533 VarSymbol loc = makeSyntheticVar(0, names.fromString("$loc"), tree.expr.type, lambdaMethodDecl.sym);
534 JCVariableDecl varDef = make.VarDef(loc, tree.expr);
535 result = make.Block(0, List.<JCStatement>of(varDef, make.Return(null)));
536 } else if (!isTarget_void || !isLambda_void) {
537 //non-void to non-void conversion:
538 // return (TYPE)RET-EXPR;
539 tree.expr = transTypes.coerce(attrEnv, tree.expr, restype);
540 result = tree;
541 } else {
542 result = tree;
543 }
545 }
546 }
548 JCBlock trans_block = new LambdaBodyTranslator().translate(block);
549 if (completeNormally && isTarget_Void) {
550 //there's no return statement and the lambda (possibly inferred)
551 //return type is java.lang.Void; emit a synthetic return statement
552 trans_block.stats = trans_block.stats.append(make.Return(make.Literal(BOT, null).setType(syms.botType)));
553 }
554 return trans_block;
555 }
557 private JCMethodDecl makeDeserializeMethod(Symbol kSym) {
558 ListBuffer<JCCase> cases = new ListBuffer<>();
559 ListBuffer<JCBreak> breaks = new ListBuffer<>();
560 for (Map.Entry<String, ListBuffer<JCStatement>> entry : kInfo.deserializeCases.entrySet()) {
561 JCBreak br = make.Break(null);
562 breaks.add(br);
563 List<JCStatement> stmts = entry.getValue().append(br).toList();
564 cases.add(make.Case(make.Literal(entry.getKey()), stmts));
565 }
566 JCSwitch sw = make.Switch(deserGetter("getImplMethodName", syms.stringType), cases.toList());
567 for (JCBreak br : breaks) {
568 br.target = sw;
569 }
570 JCBlock body = make.Block(0L, List.<JCStatement>of(
571 sw,
572 make.Throw(makeNewClass(
573 syms.illegalArgumentExceptionType,
574 List.<JCExpression>of(make.Literal("Invalid lambda deserialization"))))));
575 JCMethodDecl deser = make.MethodDef(make.Modifiers(kInfo.deserMethodSym.flags()),
576 names.deserializeLambda,
577 make.QualIdent(kInfo.deserMethodSym.getReturnType().tsym),
578 List.<JCTypeParameter>nil(),
579 List.of(make.VarDef(kInfo.deserParamSym, null)),
580 List.<JCExpression>nil(),
581 body,
582 null);
583 deser.sym = kInfo.deserMethodSym;
584 deser.type = kInfo.deserMethodSym.type;
585 //System.err.printf("DESER: '%s'\n", deser);
586 return deser;
587 }
589 /** Make an attributed class instance creation expression.
590 * @param ctype The class type.
591 * @param args The constructor arguments.
592 * @param cons The constructor symbol
593 */
594 JCNewClass makeNewClass(Type ctype, List<JCExpression> args, Symbol cons) {
595 JCNewClass tree = make.NewClass(null,
596 null, make.QualIdent(ctype.tsym), args, null);
597 tree.constructor = cons;
598 tree.type = ctype;
599 return tree;
600 }
602 /** Make an attributed class instance creation expression.
603 * @param ctype The class type.
604 * @param args The constructor arguments.
605 */
606 JCNewClass makeNewClass(Type ctype, List<JCExpression> args) {
607 return makeNewClass(ctype, args,
608 rs.resolveConstructor(null, attrEnv, ctype, TreeInfo.types(args), List.<Type>nil()));
609 }
611 private void addDeserializationCase(int implMethodKind, Symbol refSym, Type targetType, MethodSymbol samSym,
612 DiagnosticPosition pos, List<Object> staticArgs, MethodType indyType) {
613 String functionalInterfaceClass = classSig(targetType);
614 String functionalInterfaceMethodName = samSym.getSimpleName().toString();
615 String functionalInterfaceMethodSignature = typeSig(types.erasure(samSym.type));
616 String implClass = classSig(types.erasure(refSym.owner.type));
617 String implMethodName = refSym.getQualifiedName().toString();
618 String implMethodSignature = typeSig(types.erasure(refSym.type));
620 JCExpression kindTest = eqTest(syms.intType, deserGetter("getImplMethodKind", syms.intType), make.Literal(implMethodKind));
621 ListBuffer<JCExpression> serArgs = new ListBuffer<>();
622 int i = 0;
623 for (Type t : indyType.getParameterTypes()) {
624 List<JCExpression> indexAsArg = new ListBuffer<JCExpression>().append(make.Literal(i)).toList();
625 List<Type> argTypes = new ListBuffer<Type>().append(syms.intType).toList();
626 serArgs.add(make.TypeCast(types.erasure(t), deserGetter("getCapturedArg", syms.objectType, argTypes, indexAsArg)));
627 ++i;
628 }
629 JCStatement stmt = make.If(
630 deserTest(deserTest(deserTest(deserTest(deserTest(
631 kindTest,
632 "getFunctionalInterfaceClass", functionalInterfaceClass),
633 "getFunctionalInterfaceMethodName", functionalInterfaceMethodName),
634 "getFunctionalInterfaceMethodSignature", functionalInterfaceMethodSignature),
635 "getImplClass", implClass),
636 "getImplMethodSignature", implMethodSignature),
637 make.Return(makeIndyCall(
638 pos,
639 syms.lambdaMetafactory,
640 names.altMetafactory,
641 staticArgs, indyType, serArgs.toList(), samSym.name)),
642 null);
643 ListBuffer<JCStatement> stmts = kInfo.deserializeCases.get(implMethodName);
644 if (stmts == null) {
645 stmts = new ListBuffer<>();
646 kInfo.deserializeCases.put(implMethodName, stmts);
647 }
648 /****
649 System.err.printf("+++++++++++++++++\n");
650 System.err.printf("*functionalInterfaceClass: '%s'\n", functionalInterfaceClass);
651 System.err.printf("*functionalInterfaceMethodName: '%s'\n", functionalInterfaceMethodName);
652 System.err.printf("*functionalInterfaceMethodSignature: '%s'\n", functionalInterfaceMethodSignature);
653 System.err.printf("*implMethodKind: %d\n", implMethodKind);
654 System.err.printf("*implClass: '%s'\n", implClass);
655 System.err.printf("*implMethodName: '%s'\n", implMethodName);
656 System.err.printf("*implMethodSignature: '%s'\n", implMethodSignature);
657 ****/
658 stmts.append(stmt);
659 }
661 private JCExpression eqTest(Type argType, JCExpression arg1, JCExpression arg2) {
662 JCBinary testExpr = make.Binary(JCTree.Tag.EQ, arg1, arg2);
663 testExpr.operator = rs.resolveBinaryOperator(null, JCTree.Tag.EQ, attrEnv, argType, argType);
664 testExpr.setType(syms.booleanType);
665 return testExpr;
666 }
668 private JCExpression deserTest(JCExpression prev, String func, String lit) {
669 MethodType eqmt = new MethodType(List.of(syms.objectType), syms.booleanType, List.<Type>nil(), syms.methodClass);
670 Symbol eqsym = rs.resolveQualifiedMethod(null, attrEnv, syms.objectType, names.equals, List.of(syms.objectType), List.<Type>nil());
671 JCMethodInvocation eqtest = make.Apply(
672 List.<JCExpression>nil(),
673 make.Select(deserGetter(func, syms.stringType), eqsym).setType(eqmt),
674 List.<JCExpression>of(make.Literal(lit)));
675 eqtest.setType(syms.booleanType);
676 JCBinary compound = make.Binary(JCTree.Tag.AND, prev, eqtest);
677 compound.operator = rs.resolveBinaryOperator(null, JCTree.Tag.AND, attrEnv, syms.booleanType, syms.booleanType);
678 compound.setType(syms.booleanType);
679 return compound;
680 }
682 private JCExpression deserGetter(String func, Type type) {
683 return deserGetter(func, type, List.<Type>nil(), List.<JCExpression>nil());
684 }
686 private JCExpression deserGetter(String func, Type type, List<Type> argTypes, List<JCExpression> args) {
687 MethodType getmt = new MethodType(argTypes, type, List.<Type>nil(), syms.methodClass);
688 Symbol getsym = rs.resolveQualifiedMethod(null, attrEnv, syms.serializedLambdaType, names.fromString(func), argTypes, List.<Type>nil());
689 return make.Apply(
690 List.<JCExpression>nil(),
691 make.Select(make.Ident(kInfo.deserParamSym).setType(syms.serializedLambdaType), getsym).setType(getmt),
692 args).setType(type);
693 }
695 /**
696 * Create new synthetic method with given flags, name, type, owner
697 */
698 private MethodSymbol makePrivateSyntheticMethod(long flags, Name name, Type type, Symbol owner) {
699 return new MethodSymbol(flags | SYNTHETIC | PRIVATE, name, type, owner);
700 }
702 /**
703 * Create new synthetic variable with given flags, name, type, owner
704 */
705 private VarSymbol makeSyntheticVar(long flags, String name, Type type, Symbol owner) {
706 return makeSyntheticVar(flags, names.fromString(name), type, owner);
707 }
709 /**
710 * Create new synthetic variable with given flags, name, type, owner
711 */
712 private VarSymbol makeSyntheticVar(long flags, Name name, Type type, Symbol owner) {
713 return new VarSymbol(flags | SYNTHETIC, name, type, owner);
714 }
716 /**
717 * Set varargsElement field on a given tree (must be either a new class tree
718 * or a method call tree)
719 */
720 private void setVarargsIfNeeded(JCTree tree, Type varargsElement) {
721 if (varargsElement != null) {
722 switch (tree.getTag()) {
723 case APPLY: ((JCMethodInvocation)tree).varargsElement = varargsElement; break;
724 case NEWCLASS: ((JCNewClass)tree).varargsElement = varargsElement; break;
725 default: throw new AssertionError();
726 }
727 }
728 }
730 /**
731 * Convert method/constructor arguments by inserting appropriate cast
732 * as required by type-erasure - this is needed when bridging a lambda/method
733 * reference, as the bridged signature might require downcast to be compatible
734 * with the generated signature.
735 */
736 private List<JCExpression> convertArgs(Symbol meth, List<JCExpression> args, Type varargsElement) {
737 Assert.check(meth.kind == Kinds.MTH);
738 List<Type> formals = types.erasure(meth.type).getParameterTypes();
739 if (varargsElement != null) {
740 Assert.check((meth.flags() & VARARGS) != 0);
741 }
742 return transTypes.translateArgs(args, formals, varargsElement, attrEnv);
743 }
745 // </editor-fold>
747 /**
748 * Generate an adapter method "bridge" for a method reference which cannot
749 * be used directly.
750 */
751 private class MemberReferenceBridger {
753 private final JCMemberReference tree;
754 private final ReferenceTranslationContext localContext;
755 private final ListBuffer<JCExpression> args = new ListBuffer<>();
756 private final ListBuffer<JCVariableDecl> params = new ListBuffer<>();
758 MemberReferenceBridger(JCMemberReference tree, ReferenceTranslationContext localContext) {
759 this.tree = tree;
760 this.localContext = localContext;
761 }
763 /**
764 * Generate the bridge
765 */
766 JCMethodDecl bridge() {
767 int prevPos = make.pos;
768 try {
769 make.at(tree);
770 Type samDesc = localContext.bridgedRefSig();
771 List<Type> samPTypes = samDesc.getParameterTypes();
773 //an extra argument is prepended to the signature of the bridge in case
774 //the member reference is an instance method reference (in which case
775 //the receiver expression is passed to the bridge itself).
776 Type recType = null;
777 switch (tree.kind) {
778 case IMPLICIT_INNER:
779 recType = tree.sym.owner.type.getEnclosingType();
780 break;
781 case BOUND:
782 recType = tree.getQualifierExpression().type;
783 break;
784 case UNBOUND:
785 recType = samPTypes.head;
786 samPTypes = samPTypes.tail;
787 break;
788 }
790 //generate the parameter list for the bridged member reference - the
791 //bridge signature will match the signature of the target sam descriptor
793 VarSymbol rcvr = (recType == null)
794 ? null
795 : addParameter("rec$", recType, false);
797 List<Type> refPTypes = tree.sym.type.getParameterTypes();
798 int refSize = refPTypes.size();
799 int samSize = samPTypes.size();
800 // Last parameter to copy from referenced method
801 int last = localContext.needsVarArgsConversion() ? refSize - 1 : refSize;
803 List<Type> l = refPTypes;
804 // Use parameter types of the referenced method, excluding final var args
805 for (int i = 0; l.nonEmpty() && i < last; ++i) {
806 addParameter("x$" + i, l.head, true);
807 l = l.tail;
808 }
809 // Flatten out the var args
810 for (int i = last; i < samSize; ++i) {
811 addParameter("xva$" + i, tree.varargsElement, true);
812 }
814 //generate the bridge method declaration
815 JCMethodDecl bridgeDecl = make.MethodDef(make.Modifiers(localContext.bridgeSym.flags()),
816 localContext.bridgeSym.name,
817 make.QualIdent(samDesc.getReturnType().tsym),
818 List.<JCTypeParameter>nil(),
819 params.toList(),
820 tree.sym.type.getThrownTypes() == null
821 ? List.<JCExpression>nil()
822 : make.Types(tree.sym.type.getThrownTypes()),
823 null,
824 null);
825 bridgeDecl.sym = (MethodSymbol) localContext.bridgeSym;
826 bridgeDecl.type = localContext.bridgeSym.type =
827 types.createMethodTypeWithParameters(samDesc, TreeInfo.types(params.toList()));
829 //bridge method body generation - this can be either a method call or a
830 //new instance creation expression, depending on the member reference kind
831 JCExpression bridgeExpr = (tree.getMode() == ReferenceMode.INVOKE)
832 ? bridgeExpressionInvoke(makeReceiver(rcvr))
833 : bridgeExpressionNew();
835 //the body is either a return expression containing a method call,
836 //or the method call itself, depending on whether the return type of
837 //the bridge is non-void/void.
838 bridgeDecl.body = makeLambdaExpressionBody(bridgeExpr, bridgeDecl);
840 return bridgeDecl;
841 } finally {
842 make.at(prevPos);
843 }
844 }
845 //where
846 private JCExpression makeReceiver(VarSymbol rcvr) {
847 if (rcvr == null) return null;
848 JCExpression rcvrExpr = make.Ident(rcvr);
849 Type rcvrType = tree.sym.enclClass().type;
850 if (!rcvr.type.tsym.isSubClass(rcvrType.tsym, types)) {
851 rcvrExpr = make.TypeCast(make.Type(rcvrType), rcvrExpr).setType(rcvrType);
852 }
853 return rcvrExpr;
854 }
856 /**
857 * determine the receiver of the bridged method call - the receiver can
858 * be either the synthetic receiver parameter or a type qualifier; the
859 * original qualifier expression is never used here, as it might refer
860 * to symbols not available in the static context of the bridge
861 */
862 private JCExpression bridgeExpressionInvoke(JCExpression rcvr) {
863 JCExpression qualifier =
864 tree.sym.isStatic() ?
865 make.Type(tree.sym.owner.type) :
866 (rcvr != null) ?
867 rcvr :
868 tree.getQualifierExpression();
870 //create the qualifier expression
871 JCFieldAccess select = make.Select(qualifier, tree.sym.name);
872 select.sym = tree.sym;
873 select.type = tree.sym.erasure(types);
875 //create the method call expression
876 JCExpression apply = make.Apply(List.<JCExpression>nil(), select,
877 convertArgs(tree.sym, args.toList(), tree.varargsElement)).
878 setType(tree.sym.erasure(types).getReturnType());
880 apply = transTypes.coerce(apply, localContext.generatedRefSig().getReturnType());
881 setVarargsIfNeeded(apply, tree.varargsElement);
882 return apply;
883 }
885 /**
886 * the enclosing expression is either 'null' (no enclosing type) or set
887 * to the first bridge synthetic parameter
888 */
889 private JCExpression bridgeExpressionNew() {
890 if (tree.kind == ReferenceKind.ARRAY_CTOR) {
891 //create the array creation expression
892 JCNewArray newArr = make.NewArray(
893 make.Type(types.elemtype(tree.getQualifierExpression().type)),
894 List.of(make.Ident(params.first())),
895 null);
896 newArr.type = tree.getQualifierExpression().type;
897 return newArr;
898 } else {
899 JCExpression encl = null;
900 switch (tree.kind) {
901 case UNBOUND:
902 case IMPLICIT_INNER:
903 encl = make.Ident(params.first());
904 }
906 //create the instance creation expression
907 JCNewClass newClass = make.NewClass(encl,
908 List.<JCExpression>nil(),
909 make.Type(tree.getQualifierExpression().type),
910 convertArgs(tree.sym, args.toList(), tree.varargsElement),
911 null);
912 newClass.constructor = tree.sym;
913 newClass.constructorType = tree.sym.erasure(types);
914 newClass.type = tree.getQualifierExpression().type;
915 setVarargsIfNeeded(newClass, tree.varargsElement);
916 return newClass;
917 }
918 }
920 private VarSymbol addParameter(String name, Type p, boolean genArg) {
921 VarSymbol vsym = new VarSymbol(0, names.fromString(name), p, localContext.bridgeSym);
922 params.append(make.VarDef(vsym, null));
923 if (genArg) {
924 args.append(make.Ident(vsym));
925 }
926 return vsym;
927 }
928 }
930 /**
931 * Bridges a member reference - this is needed when:
932 * * Var args in the referenced method need to be flattened away
933 * * super is used
934 */
935 private void bridgeMemberReference(JCMemberReference tree, ReferenceTranslationContext localContext) {
936 kInfo.addMethod(new MemberReferenceBridger(tree, localContext).bridge());
937 }
939 private MethodType typeToMethodType(Type mt) {
940 Type type = types.erasure(mt);
941 return new MethodType(type.getParameterTypes(),
942 type.getReturnType(),
943 type.getThrownTypes(),
944 syms.methodClass);
945 }
947 /**
948 * Generate an indy method call to the meta factory
949 */
950 private JCExpression makeMetafactoryIndyCall(TranslationContext<?> context,
951 int refKind, Symbol refSym, List<JCExpression> indy_args) {
952 JCFunctionalExpression tree = context.tree;
953 //determine the static bsm args
954 MethodSymbol samSym = (MethodSymbol) types.findDescriptorSymbol(tree.type.tsym);
955 List<Object> staticArgs = List.<Object>of(
956 typeToMethodType(samSym.type),
957 new Pool.MethodHandle(refKind, refSym, types),
958 typeToMethodType(tree.getDescriptorType(types)));
960 //computed indy arg types
961 ListBuffer<Type> indy_args_types = new ListBuffer<>();
962 for (JCExpression arg : indy_args) {
963 indy_args_types.append(arg.type);
964 }
966 //finally, compute the type of the indy call
967 MethodType indyType = new MethodType(indy_args_types.toList(),
968 tree.type,
969 List.<Type>nil(),
970 syms.methodClass);
972 Name metafactoryName = context.needsAltMetafactory() ?
973 names.altMetafactory : names.metafactory;
975 if (context.needsAltMetafactory()) {
976 ListBuffer<Object> markers = new ListBuffer<>();
977 for (Type t : tree.targets.tail) {
978 if (t.tsym != syms.serializableType.tsym) {
979 markers.append(t.tsym);
980 }
981 }
982 int flags = context.isSerializable() ? FLAG_SERIALIZABLE : 0;
983 boolean hasMarkers = markers.nonEmpty();
984 boolean hasBridges = context.bridges.nonEmpty();
985 if (hasMarkers) {
986 flags |= FLAG_MARKERS;
987 }
988 if (hasBridges) {
989 flags |= FLAG_BRIDGES;
990 }
991 staticArgs = staticArgs.append(flags);
992 if (hasMarkers) {
993 staticArgs = staticArgs.append(markers.length());
994 staticArgs = staticArgs.appendList(markers.toList());
995 }
996 if (hasBridges) {
997 staticArgs = staticArgs.append(context.bridges.length() - 1);
998 for (Symbol s : context.bridges) {
999 Type s_erasure = s.erasure(types);
1000 if (!types.isSameType(s_erasure, samSym.erasure(types))) {
1001 staticArgs = staticArgs.append(s.erasure(types));
1002 }
1003 }
1004 }
1005 if (context.isSerializable()) {
1006 int prevPos = make.pos;
1007 try {
1008 make.at(kInfo.clazz);
1009 addDeserializationCase(refKind, refSym, tree.type, samSym,
1010 tree, staticArgs, indyType);
1011 } finally {
1012 make.at(prevPos);
1013 }
1014 }
1015 }
1017 return makeIndyCall(tree, syms.lambdaMetafactory, metafactoryName, staticArgs, indyType, indy_args, samSym.name);
1018 }
1020 /**
1021 * Generate an indy method call with given name, type and static bootstrap
1022 * arguments types
1023 */
1024 private JCExpression makeIndyCall(DiagnosticPosition pos, Type site, Name bsmName,
1025 List<Object> staticArgs, MethodType indyType, List<JCExpression> indyArgs,
1026 Name methName) {
1027 int prevPos = make.pos;
1028 try {
1029 make.at(pos);
1030 List<Type> bsm_staticArgs = List.of(syms.methodHandleLookupType,
1031 syms.stringType,
1032 syms.methodTypeType).appendList(bsmStaticArgToTypes(staticArgs));
1034 Symbol bsm = rs.resolveInternalMethod(pos, attrEnv, site,
1035 bsmName, bsm_staticArgs, List.<Type>nil());
1037 DynamicMethodSymbol dynSym =
1038 new DynamicMethodSymbol(methName,
1039 syms.noSymbol,
1040 bsm.isStatic() ?
1041 ClassFile.REF_invokeStatic :
1042 ClassFile.REF_invokeVirtual,
1043 (MethodSymbol)bsm,
1044 indyType,
1045 staticArgs.toArray());
1047 JCFieldAccess qualifier = make.Select(make.QualIdent(site.tsym), bsmName);
1048 qualifier.sym = dynSym;
1049 qualifier.type = indyType.getReturnType();
1051 JCMethodInvocation proxyCall = make.Apply(List.<JCExpression>nil(), qualifier, indyArgs);
1052 proxyCall.type = indyType.getReturnType();
1053 return proxyCall;
1054 } finally {
1055 make.at(prevPos);
1056 }
1057 }
1058 //where
1059 private List<Type> bsmStaticArgToTypes(List<Object> args) {
1060 ListBuffer<Type> argtypes = new ListBuffer<>();
1061 for (Object arg : args) {
1062 argtypes.append(bsmStaticArgToType(arg));
1063 }
1064 return argtypes.toList();
1065 }
1067 private Type bsmStaticArgToType(Object arg) {
1068 Assert.checkNonNull(arg);
1069 if (arg instanceof ClassSymbol) {
1070 return syms.classType;
1071 } else if (arg instanceof Integer) {
1072 return syms.intType;
1073 } else if (arg instanceof Long) {
1074 return syms.longType;
1075 } else if (arg instanceof Float) {
1076 return syms.floatType;
1077 } else if (arg instanceof Double) {
1078 return syms.doubleType;
1079 } else if (arg instanceof String) {
1080 return syms.stringType;
1081 } else if (arg instanceof Pool.MethodHandle) {
1082 return syms.methodHandleType;
1083 } else if (arg instanceof MethodType) {
1084 return syms.methodTypeType;
1085 } else {
1086 Assert.error("bad static arg " + arg.getClass());
1087 return null;
1088 }
1089 }
1091 /**
1092 * Get the opcode associated with this method reference
1093 */
1094 private int referenceKind(Symbol refSym) {
1095 if (refSym.isConstructor()) {
1096 return ClassFile.REF_newInvokeSpecial;
1097 } else {
1098 if (refSym.isStatic()) {
1099 return ClassFile.REF_invokeStatic;
1100 } else if ((refSym.flags() & PRIVATE) != 0) {
1101 return ClassFile.REF_invokeSpecial;
1102 } else if (refSym.enclClass().isInterface()) {
1103 return ClassFile.REF_invokeInterface;
1104 } else {
1105 return ClassFile.REF_invokeVirtual;
1106 }
1107 }
1108 }
1110 // <editor-fold defaultstate="collapsed" desc="Lambda/reference analyzer">
1111 /**
1112 * This visitor collects information about translation of a lambda expression.
1113 * More specifically, it keeps track of the enclosing contexts and captured locals
1114 * accessed by the lambda being translated (as well as other useful info).
1115 * It also translates away problems for LambdaToMethod.
1116 */
1117 class LambdaAnalyzerPreprocessor extends TreeTranslator {
1119 /** the frame stack - used to reconstruct translation info about enclosing scopes */
1120 private List<Frame> frameStack;
1122 /**
1123 * keep the count of lambda expression (used to generate unambiguous
1124 * names)
1125 */
1126 private int lambdaCount = 0;
1128 /**
1129 * keep the count of lambda expression defined in given context (used to
1130 * generate unambiguous names for serializable lambdas)
1131 */
1132 private class SyntheticMethodNameCounter {
1133 private Map<String, Integer> map = new HashMap<>();
1134 int getIndex(StringBuilder buf) {
1135 String temp = buf.toString();
1136 Integer count = map.get(temp);
1137 if (count == null) {
1138 count = 0;
1139 }
1140 ++count;
1141 map.put(temp, count);
1142 return count;
1143 }
1144 }
1145 private SyntheticMethodNameCounter syntheticMethodNameCounts =
1146 new SyntheticMethodNameCounter();
1148 private Map<Symbol, JCClassDecl> localClassDefs;
1150 /**
1151 * maps for fake clinit symbols to be used as owners of lambda occurring in
1152 * a static var init context
1153 */
1154 private Map<ClassSymbol, Symbol> clinits =
1155 new HashMap<ClassSymbol, Symbol>();
1157 private JCClassDecl analyzeAndPreprocessClass(JCClassDecl tree) {
1158 frameStack = List.nil();
1159 localClassDefs = new HashMap<Symbol, JCClassDecl>();
1160 return translate(tree);
1161 }
1163 @Override
1164 public void visitBlock(JCBlock tree) {
1165 List<Frame> prevStack = frameStack;
1166 try {
1167 if (frameStack.nonEmpty() && frameStack.head.tree.hasTag(CLASSDEF)) {
1168 frameStack = frameStack.prepend(new Frame(tree));
1169 }
1170 super.visitBlock(tree);
1171 }
1172 finally {
1173 frameStack = prevStack;
1174 }
1175 }
1177 @Override
1178 public void visitClassDef(JCClassDecl tree) {
1179 List<Frame> prevStack = frameStack;
1180 SyntheticMethodNameCounter prevSyntheticMethodNameCounts =
1181 syntheticMethodNameCounts;
1182 Map<ClassSymbol, Symbol> prevClinits = clinits;
1183 DiagnosticSource prevSource = log.currentSource();
1184 try {
1185 log.useSource(tree.sym.sourcefile);
1186 syntheticMethodNameCounts = new SyntheticMethodNameCounter();
1187 prevClinits = new HashMap<ClassSymbol, Symbol>();
1188 if (tree.sym.owner.kind == MTH) {
1189 localClassDefs.put(tree.sym, tree);
1190 }
1191 if (directlyEnclosingLambda() != null) {
1192 tree.sym.owner = owner();
1193 if (tree.sym.hasOuterInstance()) {
1194 //if a class is defined within a lambda, the lambda must capture
1195 //its enclosing instance (if any)
1196 TranslationContext<?> localContext = context();
1197 while (localContext != null) {
1198 if (localContext.tree.getTag() == LAMBDA) {
1199 ((LambdaTranslationContext)localContext)
1200 .addSymbol(tree.sym.type.getEnclosingType().tsym, CAPTURED_THIS);
1201 }
1202 localContext = localContext.prev;
1203 }
1204 }
1205 }
1206 frameStack = frameStack.prepend(new Frame(tree));
1207 super.visitClassDef(tree);
1208 }
1209 finally {
1210 log.useSource(prevSource.getFile());
1211 frameStack = prevStack;
1212 syntheticMethodNameCounts = prevSyntheticMethodNameCounts;
1213 clinits = prevClinits;
1214 }
1215 }
1217 @Override
1218 public void visitIdent(JCIdent tree) {
1219 if (context() != null && lambdaIdentSymbolFilter(tree.sym)) {
1220 if (tree.sym.kind == VAR &&
1221 tree.sym.owner.kind == MTH &&
1222 tree.type.constValue() == null) {
1223 TranslationContext<?> localContext = context();
1224 while (localContext != null) {
1225 if (localContext.tree.getTag() == LAMBDA) {
1226 JCTree block = capturedDecl(localContext.depth, tree.sym);
1227 if (block == null) break;
1228 ((LambdaTranslationContext)localContext)
1229 .addSymbol(tree.sym, CAPTURED_VAR);
1230 }
1231 localContext = localContext.prev;
1232 }
1233 } else if (tree.sym.owner.kind == TYP) {
1234 TranslationContext<?> localContext = context();
1235 while (localContext != null) {
1236 if (localContext.tree.hasTag(LAMBDA)) {
1237 JCTree block = capturedDecl(localContext.depth, tree.sym);
1238 if (block == null) break;
1239 switch (block.getTag()) {
1240 case CLASSDEF:
1241 JCClassDecl cdecl = (JCClassDecl)block;
1242 ((LambdaTranslationContext)localContext)
1243 .addSymbol(cdecl.sym, CAPTURED_THIS);
1244 break;
1245 default:
1246 Assert.error("bad block kind");
1247 }
1248 }
1249 localContext = localContext.prev;
1250 }
1251 }
1252 }
1253 super.visitIdent(tree);
1254 }
1256 @Override
1257 public void visitLambda(JCLambda tree) {
1258 List<Frame> prevStack = frameStack;
1259 try {
1260 LambdaTranslationContext context = (LambdaTranslationContext)makeLambdaContext(tree);
1261 frameStack = frameStack.prepend(new Frame(tree));
1262 for (JCVariableDecl param : tree.params) {
1263 context.addSymbol(param.sym, PARAM);
1264 frameStack.head.addLocal(param.sym);
1265 }
1266 contextMap.put(tree, context);
1267 super.visitLambda(tree);
1268 context.complete();
1269 }
1270 finally {
1271 frameStack = prevStack;
1272 }
1273 }
1275 @Override
1276 public void visitMethodDef(JCMethodDecl tree) {
1277 List<Frame> prevStack = frameStack;
1278 try {
1279 frameStack = frameStack.prepend(new Frame(tree));
1280 super.visitMethodDef(tree);
1281 }
1282 finally {
1283 frameStack = prevStack;
1284 }
1285 }
1287 @Override
1288 public void visitNewClass(JCNewClass tree) {
1289 if (lambdaNewClassFilter(context(), tree)) {
1290 TranslationContext<?> localContext = context();
1291 while (localContext != null) {
1292 if (localContext.tree.getTag() == LAMBDA) {
1293 ((LambdaTranslationContext)localContext)
1294 .addSymbol(tree.type.getEnclosingType().tsym, CAPTURED_THIS);
1295 }
1296 localContext = localContext.prev;
1297 }
1298 }
1299 if (context() != null && tree.type.tsym.owner.kind == MTH) {
1300 LambdaTranslationContext lambdaContext = (LambdaTranslationContext)context();
1301 captureLocalClassDefs(tree.type.tsym, lambdaContext);
1302 }
1303 super.visitNewClass(tree);
1304 }
1305 //where
1306 void captureLocalClassDefs(Symbol csym, final LambdaTranslationContext lambdaContext) {
1307 JCClassDecl localCDef = localClassDefs.get(csym);
1308 if (localCDef != null && localCDef.pos < lambdaContext.tree.pos) {
1309 BasicFreeVarCollector fvc = lower.new BasicFreeVarCollector() {
1310 @Override
1311 void addFreeVars(ClassSymbol c) {
1312 captureLocalClassDefs(c, lambdaContext);
1313 }
1314 @Override
1315 void visitSymbol(Symbol sym) {
1316 if (sym.kind == VAR &&
1317 sym.owner.kind == MTH &&
1318 ((VarSymbol)sym).getConstValue() == null) {
1319 TranslationContext<?> localContext = context();
1320 while (localContext != null) {
1321 if (localContext.tree.getTag() == LAMBDA) {
1322 JCTree block = capturedDecl(localContext.depth, sym);
1323 if (block == null) break;
1324 ((LambdaTranslationContext)localContext).addSymbol(sym, CAPTURED_VAR);
1325 }
1326 localContext = localContext.prev;
1327 }
1328 }
1329 }
1330 };
1331 fvc.scan(localCDef);
1332 }
1333 }
1335 /**
1336 * Method references to local class constructors, may, if the local
1337 * class references local variables, have implicit constructor
1338 * parameters added in Lower; As a result, the invokedynamic bootstrap
1339 * information added in the LambdaToMethod pass will have the wrong
1340 * signature. Hooks between Lower and LambdaToMethod have been added to
1341 * handle normal "new" in this case. This visitor converts potentially
1342 * effected method references into a lambda containing a normal "new" of
1343 * the class.
1344 *
1345 * @param tree
1346 */
1347 @Override
1348 public void visitReference(JCMemberReference tree) {
1349 if (tree.getMode() == ReferenceMode.NEW
1350 && tree.kind != ReferenceKind.ARRAY_CTOR
1351 && tree.sym.owner.isLocal()) {
1352 MethodSymbol consSym = (MethodSymbol) tree.sym;
1353 List<Type> ptypes = ((MethodType) consSym.type).getParameterTypes();
1354 Type classType = consSym.owner.type;
1356 // Build lambda parameters
1357 // partially cloned from TreeMaker.Params until 8014021 is fixed
1358 Symbol owner = owner();
1359 ListBuffer<JCVariableDecl> paramBuff = new ListBuffer<JCVariableDecl>();
1360 int i = 0;
1361 for (List<Type> l = ptypes; l.nonEmpty(); l = l.tail) {
1362 JCVariableDecl param = make.Param(make.paramName(i++), l.head, owner);
1363 param.sym.pos = tree.pos;
1364 paramBuff.append(param);
1365 }
1366 List<JCVariableDecl> params = paramBuff.toList();
1368 // Make new-class call
1369 JCNewClass nc = makeNewClass(classType, make.Idents(params));
1370 nc.pos = tree.pos;
1372 // Make lambda holding the new-class call
1373 JCLambda slam = make.Lambda(params, nc);
1374 slam.targets = tree.targets;
1375 slam.type = tree.type;
1376 slam.pos = tree.pos;
1378 // Now it is a lambda, process as such
1379 visitLambda(slam);
1380 } else {
1381 super.visitReference(tree);
1382 contextMap.put(tree, makeReferenceContext(tree));
1383 }
1384 }
1386 @Override
1387 public void visitSelect(JCFieldAccess tree) {
1388 if (context() != null && tree.sym.kind == VAR &&
1389 (tree.sym.name == names._this ||
1390 tree.sym.name == names._super)) {
1391 // A select of this or super means, if we are in a lambda,
1392 // we much have an instance context
1393 TranslationContext<?> localContext = context();
1394 while (localContext != null) {
1395 if (localContext.tree.hasTag(LAMBDA)) {
1396 JCClassDecl clazz = (JCClassDecl)capturedDecl(localContext.depth, tree.sym);
1397 if (clazz == null) break;
1398 ((LambdaTranslationContext)localContext).addSymbol(clazz.sym, CAPTURED_THIS);
1399 }
1400 localContext = localContext.prev;
1401 }
1402 }
1403 super.visitSelect(tree);
1404 }
1406 @Override
1407 public void visitVarDef(JCVariableDecl tree) {
1408 TranslationContext<?> context = context();
1409 LambdaTranslationContext ltc = (context != null && context instanceof LambdaTranslationContext)?
1410 (LambdaTranslationContext)context :
1411 null;
1412 if (ltc != null) {
1413 if (frameStack.head.tree.hasTag(LAMBDA)) {
1414 ltc.addSymbol(tree.sym, LOCAL_VAR);
1415 }
1416 // Check for type variables (including as type arguments).
1417 // If they occur within class nested in a lambda, mark for erasure
1418 Type type = tree.sym.asType();
1419 if (inClassWithinLambda() && !types.isSameType(types.erasure(type), type)) {
1420 ltc.addSymbol(tree.sym, TYPE_VAR);
1421 }
1422 }
1424 List<Frame> prevStack = frameStack;
1425 try {
1426 if (tree.sym.owner.kind == MTH) {
1427 frameStack.head.addLocal(tree.sym);
1428 }
1429 frameStack = frameStack.prepend(new Frame(tree));
1430 super.visitVarDef(tree);
1431 }
1432 finally {
1433 frameStack = prevStack;
1434 }
1435 }
1437 /**
1438 * Return a valid owner given the current declaration stack
1439 * (required to skip synthetic lambda symbols)
1440 */
1441 private Symbol owner() {
1442 return owner(false);
1443 }
1445 @SuppressWarnings("fallthrough")
1446 private Symbol owner(boolean skipLambda) {
1447 List<Frame> frameStack2 = frameStack;
1448 while (frameStack2.nonEmpty()) {
1449 switch (frameStack2.head.tree.getTag()) {
1450 case VARDEF:
1451 if (((JCVariableDecl)frameStack2.head.tree).sym.isLocal()) {
1452 frameStack2 = frameStack2.tail;
1453 break;
1454 }
1455 JCClassDecl cdecl = (JCClassDecl)frameStack2.tail.head.tree;
1456 return initSym(cdecl.sym,
1457 ((JCVariableDecl)frameStack2.head.tree).sym.flags() & STATIC);
1458 case BLOCK:
1459 JCClassDecl cdecl2 = (JCClassDecl)frameStack2.tail.head.tree;
1460 return initSym(cdecl2.sym,
1461 ((JCBlock)frameStack2.head.tree).flags & STATIC);
1462 case CLASSDEF:
1463 return ((JCClassDecl)frameStack2.head.tree).sym;
1464 case METHODDEF:
1465 return ((JCMethodDecl)frameStack2.head.tree).sym;
1466 case LAMBDA:
1467 if (!skipLambda)
1468 return ((LambdaTranslationContext)contextMap
1469 .get(frameStack2.head.tree)).translatedSym;
1470 default:
1471 frameStack2 = frameStack2.tail;
1472 }
1473 }
1474 Assert.error();
1475 return null;
1476 }
1478 private Symbol initSym(ClassSymbol csym, long flags) {
1479 boolean isStatic = (flags & STATIC) != 0;
1480 if (isStatic) {
1481 /* static clinits are generated in Gen, so we need to use a fake
1482 * one. Attr creates a fake clinit method while attributing
1483 * lambda expressions used as initializers of static fields, so
1484 * let's use that one.
1485 */
1486 MethodSymbol clinit = attr.removeClinit(csym);
1487 if (clinit != null) {
1488 clinits.put(csym, clinit);
1489 return clinit;
1490 }
1492 /* if no clinit is found at Attr, then let's try at clinits.
1493 */
1494 clinit = (MethodSymbol)clinits.get(csym);
1495 if (clinit == null) {
1496 /* no luck, let's create a new one
1497 */
1498 clinit = makePrivateSyntheticMethod(STATIC,
1499 names.clinit,
1500 new MethodType(List.<Type>nil(), syms.voidType,
1501 List.<Type>nil(), syms.methodClass),
1502 csym);
1503 clinits.put(csym, clinit);
1504 }
1505 return clinit;
1506 } else {
1507 //get the first constructor and treat it as the instance init sym
1508 for (Symbol s : csym.members_field.getElementsByName(names.init)) {
1509 return s;
1510 }
1511 }
1512 Assert.error("init not found");
1513 return null;
1514 }
1516 private JCTree directlyEnclosingLambda() {
1517 if (frameStack.isEmpty()) {
1518 return null;
1519 }
1520 List<Frame> frameStack2 = frameStack;
1521 while (frameStack2.nonEmpty()) {
1522 switch (frameStack2.head.tree.getTag()) {
1523 case CLASSDEF:
1524 case METHODDEF:
1525 return null;
1526 case LAMBDA:
1527 return frameStack2.head.tree;
1528 default:
1529 frameStack2 = frameStack2.tail;
1530 }
1531 }
1532 Assert.error();
1533 return null;
1534 }
1536 private boolean inClassWithinLambda() {
1537 if (frameStack.isEmpty()) {
1538 return false;
1539 }
1540 List<Frame> frameStack2 = frameStack;
1541 boolean classFound = false;
1542 while (frameStack2.nonEmpty()) {
1543 switch (frameStack2.head.tree.getTag()) {
1544 case LAMBDA:
1545 return classFound;
1546 case CLASSDEF:
1547 classFound = true;
1548 frameStack2 = frameStack2.tail;
1549 break;
1550 default:
1551 frameStack2 = frameStack2.tail;
1552 }
1553 }
1554 // No lambda
1555 return false;
1556 }
1558 /**
1559 * Return the declaration corresponding to a symbol in the enclosing
1560 * scope; the depth parameter is used to filter out symbols defined
1561 * in nested scopes (which do not need to undergo capture).
1562 */
1563 private JCTree capturedDecl(int depth, Symbol sym) {
1564 int currentDepth = frameStack.size() - 1;
1565 for (Frame block : frameStack) {
1566 switch (block.tree.getTag()) {
1567 case CLASSDEF:
1568 ClassSymbol clazz = ((JCClassDecl)block.tree).sym;
1569 if (sym.isMemberOf(clazz, types)) {
1570 return currentDepth > depth ? null : block.tree;
1571 }
1572 break;
1573 case VARDEF:
1574 if (((JCVariableDecl)block.tree).sym == sym &&
1575 sym.owner.kind == MTH) { //only locals are captured
1576 return currentDepth > depth ? null : block.tree;
1577 }
1578 break;
1579 case BLOCK:
1580 case METHODDEF:
1581 case LAMBDA:
1582 if (block.locals != null && block.locals.contains(sym)) {
1583 return currentDepth > depth ? null : block.tree;
1584 }
1585 break;
1586 default:
1587 Assert.error("bad decl kind " + block.tree.getTag());
1588 }
1589 currentDepth--;
1590 }
1591 return null;
1592 }
1594 private TranslationContext<?> context() {
1595 for (Frame frame : frameStack) {
1596 TranslationContext<?> context = contextMap.get(frame.tree);
1597 if (context != null) {
1598 return context;
1599 }
1600 }
1601 return null;
1602 }
1604 /**
1605 * This is used to filter out those identifiers that needs to be adjusted
1606 * when translating away lambda expressions
1607 */
1608 private boolean lambdaIdentSymbolFilter(Symbol sym) {
1609 return (sym.kind == VAR || sym.kind == MTH)
1610 && !sym.isStatic()
1611 && sym.name != names.init;
1612 }
1614 /**
1615 * This is used to filter out those new class expressions that need to
1616 * be qualified with an enclosing tree
1617 */
1618 private boolean lambdaNewClassFilter(TranslationContext<?> context, JCNewClass tree) {
1619 if (context != null
1620 && tree.encl == null
1621 && tree.def == null
1622 && !tree.type.getEnclosingType().hasTag(NONE)) {
1623 Type encl = tree.type.getEnclosingType();
1624 Type current = context.owner.enclClass().type;
1625 while (!current.hasTag(NONE)) {
1626 if (current.tsym.isSubClass(encl.tsym, types)) {
1627 return true;
1628 }
1629 current = current.getEnclosingType();
1630 }
1631 return false;
1632 } else {
1633 return false;
1634 }
1635 }
1637 private TranslationContext<JCLambda> makeLambdaContext(JCLambda tree) {
1638 return new LambdaTranslationContext(tree);
1639 }
1641 private TranslationContext<JCMemberReference> makeReferenceContext(JCMemberReference tree) {
1642 return new ReferenceTranslationContext(tree);
1643 }
1645 private class Frame {
1646 final JCTree tree;
1647 List<Symbol> locals;
1649 public Frame(JCTree tree) {
1650 this.tree = tree;
1651 }
1653 void addLocal(Symbol sym) {
1654 if (locals == null) {
1655 locals = List.nil();
1656 }
1657 locals = locals.prepend(sym);
1658 }
1659 }
1661 /**
1662 * This class is used to store important information regarding translation of
1663 * lambda expression/method references (see subclasses).
1664 */
1665 private abstract class TranslationContext<T extends JCFunctionalExpression> {
1667 /** the underlying (untranslated) tree */
1668 final T tree;
1670 /** points to the adjusted enclosing scope in which this lambda/mref expression occurs */
1671 final Symbol owner;
1673 /** the depth of this lambda expression in the frame stack */
1674 final int depth;
1676 /** the enclosing translation context (set for nested lambdas/mref) */
1677 final TranslationContext<?> prev;
1679 /** list of methods to be bridged by the meta-factory */
1680 final List<Symbol> bridges;
1682 TranslationContext(T tree) {
1683 this.tree = tree;
1684 this.owner = owner();
1685 this.depth = frameStack.size() - 1;
1686 this.prev = context();
1687 ClassSymbol csym =
1688 types.makeFunctionalInterfaceClass(attrEnv, names.empty, tree.targets, ABSTRACT | INTERFACE);
1689 this.bridges = types.functionalInterfaceBridges(csym);
1690 }
1692 /** does this functional expression need to be created using alternate metafactory? */
1693 boolean needsAltMetafactory() {
1694 return tree.targets.length() > 1 ||
1695 isSerializable() ||
1696 bridges.length() > 1;
1697 }
1699 /** does this functional expression require serialization support? */
1700 boolean isSerializable() {
1701 if (forceSerializable) {
1702 return true;
1703 }
1704 for (Type target : tree.targets) {
1705 if (types.asSuper(target, syms.serializableType.tsym) != null) {
1706 return true;
1707 }
1708 }
1709 return false;
1710 }
1712 /**
1713 * @return Name of the enclosing method to be folded into synthetic
1714 * method name
1715 */
1716 String enclosingMethodName() {
1717 return syntheticMethodNameComponent(owner.name);
1718 }
1720 /**
1721 * @return Method name in a form that can be folded into a
1722 * component of a synthetic method name
1723 */
1724 String syntheticMethodNameComponent(Name name) {
1725 if (name == null) {
1726 return "null";
1727 }
1728 String methodName = name.toString();
1729 if (methodName.equals("<clinit>")) {
1730 methodName = "static";
1731 } else if (methodName.equals("<init>")) {
1732 methodName = "new";
1733 }
1734 return methodName;
1735 }
1736 }
1738 /**
1739 * This class retains all the useful information about a lambda expression;
1740 * the contents of this class are filled by the LambdaAnalyzer visitor,
1741 * and the used by the main translation routines in order to adjust references
1742 * to captured locals/members, etc.
1743 */
1744 private class LambdaTranslationContext extends TranslationContext<JCLambda> {
1746 /** variable in the enclosing context to which this lambda is assigned */
1747 final Symbol self;
1749 /** variable in the enclosing context to which this lambda is assigned */
1750 final Symbol assignedTo;
1752 Map<LambdaSymbolKind, Map<Symbol, Symbol>> translatedSymbols;
1754 /** the synthetic symbol for the method hoisting the translated lambda */
1755 Symbol translatedSym;
1757 List<JCVariableDecl> syntheticParams;
1759 LambdaTranslationContext(JCLambda tree) {
1760 super(tree);
1761 Frame frame = frameStack.head;
1762 switch (frame.tree.getTag()) {
1763 case VARDEF:
1764 assignedTo = self = ((JCVariableDecl) frame.tree).sym;
1765 break;
1766 case ASSIGN:
1767 self = null;
1768 assignedTo = TreeInfo.symbol(((JCAssign) frame.tree).getVariable());
1769 break;
1770 default:
1771 assignedTo = self = null;
1772 break;
1773 }
1775 // This symbol will be filled-in in complete
1776 this.translatedSym = makePrivateSyntheticMethod(0, null, null, owner.enclClass());
1778 if (dumpLambdaToMethodStats) {
1779 log.note(tree, "lambda.stat", needsAltMetafactory(), translatedSym);
1780 }
1781 translatedSymbols = new EnumMap<>(LambdaSymbolKind.class);
1783 translatedSymbols.put(PARAM, new LinkedHashMap<Symbol, Symbol>());
1784 translatedSymbols.put(LOCAL_VAR, new LinkedHashMap<Symbol, Symbol>());
1785 translatedSymbols.put(CAPTURED_VAR, new LinkedHashMap<Symbol, Symbol>());
1786 translatedSymbols.put(CAPTURED_THIS, new LinkedHashMap<Symbol, Symbol>());
1787 translatedSymbols.put(TYPE_VAR, new LinkedHashMap<Symbol, Symbol>());
1788 }
1790 /**
1791 * For a serializable lambda, generate a disambiguating string
1792 * which maximizes stability across deserialization.
1793 *
1794 * @return String to differentiate synthetic lambda method names
1795 */
1796 private String serializedLambdaDisambiguation() {
1797 StringBuilder buf = new StringBuilder();
1798 // Append the enclosing method signature to differentiate
1799 // overloaded enclosing methods. For lambdas enclosed in
1800 // lambdas, the generated lambda method will not have type yet,
1801 // but the enclosing method's name will have been generated
1802 // with this same method, so it will be unique and never be
1803 // overloaded.
1804 Assert.check(
1805 owner.type != null ||
1806 directlyEnclosingLambda() != null);
1807 if (owner.type != null) {
1808 buf.append(typeSig(owner.type));
1809 buf.append(":");
1810 }
1812 // Add target type info
1813 buf.append(types.findDescriptorSymbol(tree.type.tsym).owner.flatName());
1814 buf.append(" ");
1816 // Add variable assigned to
1817 if (assignedTo != null) {
1818 buf.append(assignedTo.flatName());
1819 buf.append("=");
1820 }
1821 //add captured locals info: type, name, order
1822 for (Symbol fv : getSymbolMap(CAPTURED_VAR).keySet()) {
1823 if (fv != self) {
1824 buf.append(typeSig(fv.type));
1825 buf.append(" ");
1826 buf.append(fv.flatName());
1827 buf.append(",");
1828 }
1829 }
1831 return buf.toString();
1832 }
1834 /**
1835 * For a non-serializable lambda, generate a simple method.
1836 *
1837 * @return Name to use for the synthetic lambda method name
1838 */
1839 private Name lambdaName() {
1840 return names.lambda.append(names.fromString(enclosingMethodName() + "$" + lambdaCount++));
1841 }
1843 /**
1844 * For a serializable lambda, generate a method name which maximizes
1845 * name stability across deserialization.
1846 *
1847 * @return Name to use for the synthetic lambda method name
1848 */
1849 private Name serializedLambdaName() {
1850 StringBuilder buf = new StringBuilder();
1851 buf.append(names.lambda);
1852 // Append the name of the method enclosing the lambda.
1853 buf.append(enclosingMethodName());
1854 buf.append('$');
1855 // Append a hash of the disambiguating string : enclosing method
1856 // signature, etc.
1857 String disam = serializedLambdaDisambiguation();
1858 buf.append(Integer.toHexString(disam.hashCode()));
1859 buf.append('$');
1860 // The above appended name components may not be unique, append
1861 // a count based on the above name components.
1862 buf.append(syntheticMethodNameCounts.getIndex(buf));
1863 String result = buf.toString();
1864 //System.err.printf("serializedLambdaName: %s -- %s\n", result, disam);
1865 return names.fromString(result);
1866 }
1868 /**
1869 * Translate a symbol of a given kind into something suitable for the
1870 * synthetic lambda body
1871 */
1872 Symbol translate(Name name, final Symbol sym, LambdaSymbolKind skind) {
1873 Symbol ret;
1874 switch (skind) {
1875 case CAPTURED_THIS:
1876 ret = sym; // self represented
1877 break;
1878 case TYPE_VAR:
1879 // Just erase the type var
1880 ret = new VarSymbol(sym.flags(), name,
1881 types.erasure(sym.type), sym.owner);
1883 /* this information should also be kept for LVT generation at Gen
1884 * a Symbol with pos < startPos won't be tracked.
1885 */
1886 ((VarSymbol)ret).pos = ((VarSymbol)sym).pos;
1887 break;
1888 case CAPTURED_VAR:
1889 ret = new VarSymbol(SYNTHETIC | FINAL | PARAMETER, name, types.erasure(sym.type), translatedSym) {
1890 @Override
1891 public Symbol baseSymbol() {
1892 //keep mapping with original captured symbol
1893 return sym;
1894 }
1895 };
1896 break;
1897 case LOCAL_VAR:
1898 ret = new VarSymbol(sym.flags() & FINAL, name, types.erasure(sym.type), translatedSym);
1899 ((VarSymbol) ret).pos = ((VarSymbol) sym).pos;
1900 break;
1901 case PARAM:
1902 ret = new VarSymbol((sym.flags() & FINAL) | PARAMETER, name, types.erasure(sym.type), translatedSym);
1903 ((VarSymbol) ret).pos = ((VarSymbol) sym).pos;
1904 break;
1905 default:
1906 ret = makeSyntheticVar(FINAL, name, types.erasure(sym.type), translatedSym);
1907 ((VarSymbol) ret).pos = ((VarSymbol) sym).pos;
1908 }
1909 if (ret != sym) {
1910 ret.setDeclarationAttributes(sym.getRawAttributes());
1911 ret.setTypeAttributes(sym.getRawTypeAttributes());
1912 }
1913 return ret;
1914 }
1916 void addSymbol(Symbol sym, LambdaSymbolKind skind) {
1917 Map<Symbol, Symbol> transMap = getSymbolMap(skind);
1918 Name preferredName;
1919 switch (skind) {
1920 case CAPTURED_THIS:
1921 preferredName = names.fromString("encl$" + transMap.size());
1922 break;
1923 case CAPTURED_VAR:
1924 preferredName = names.fromString("cap$" + transMap.size());
1925 break;
1926 case LOCAL_VAR:
1927 preferredName = sym.name;
1928 break;
1929 case PARAM:
1930 preferredName = sym.name;
1931 break;
1932 case TYPE_VAR:
1933 preferredName = sym.name;
1934 break;
1935 default: throw new AssertionError();
1936 }
1937 if (!transMap.containsKey(sym)) {
1938 transMap.put(sym, translate(preferredName, sym, skind));
1939 }
1940 }
1942 Map<Symbol, Symbol> getSymbolMap(LambdaSymbolKind skind) {
1943 Map<Symbol, Symbol> m = translatedSymbols.get(skind);
1944 Assert.checkNonNull(m);
1945 return m;
1946 }
1948 JCTree translate(JCIdent lambdaIdent) {
1949 for (Map<Symbol, Symbol> m : translatedSymbols.values()) {
1950 if (m.containsKey(lambdaIdent.sym)) {
1951 Symbol tSym = m.get(lambdaIdent.sym);
1952 JCTree t = make.Ident(tSym).setType(lambdaIdent.type);
1953 tSym.setTypeAttributes(lambdaIdent.sym.getRawTypeAttributes());
1954 return t;
1955 }
1956 }
1957 return null;
1958 }
1960 /**
1961 * The translatedSym is not complete/accurate until the analysis is
1962 * finished. Once the analysis is finished, the translatedSym is
1963 * "completed" -- updated with type information, access modifiers,
1964 * and full parameter list.
1965 */
1966 void complete() {
1967 if (syntheticParams != null) {
1968 return;
1969 }
1970 boolean inInterface = translatedSym.owner.isInterface();
1971 boolean thisReferenced = !getSymbolMap(CAPTURED_THIS).isEmpty();
1973 // If instance access isn't needed, make it static.
1974 // Interface instance methods must be default methods.
1975 // Lambda methods are private synthetic.
1976 translatedSym.flags_field = SYNTHETIC | LAMBDA_METHOD |
1977 PRIVATE |
1978 (thisReferenced? (inInterface? DEFAULT : 0) : STATIC);
1980 //compute synthetic params
1981 ListBuffer<JCVariableDecl> params = new ListBuffer<>();
1983 // The signature of the method is augmented with the following
1984 // synthetic parameters:
1985 //
1986 // 1) reference to enclosing contexts captured by the lambda expression
1987 // 2) enclosing locals captured by the lambda expression
1988 for (Symbol thisSym : getSymbolMap(CAPTURED_VAR).values()) {
1989 params.append(make.VarDef((VarSymbol) thisSym, null));
1990 }
1991 for (Symbol thisSym : getSymbolMap(PARAM).values()) {
1992 params.append(make.VarDef((VarSymbol) thisSym, null));
1993 }
1994 syntheticParams = params.toList();
1996 // Compute and set the lambda name
1997 translatedSym.name = isSerializable()
1998 ? serializedLambdaName()
1999 : lambdaName();
2001 //prepend synthetic args to translated lambda method signature
2002 translatedSym.type = types.createMethodTypeWithParameters(
2003 generatedLambdaSig(),
2004 TreeInfo.types(syntheticParams));
2005 }
2007 Type generatedLambdaSig() {
2008 return types.erasure(tree.getDescriptorType(types));
2009 }
2010 }
2012 /**
2013 * This class retains all the useful information about a method reference;
2014 * the contents of this class are filled by the LambdaAnalyzer visitor,
2015 * and the used by the main translation routines in order to adjust method
2016 * references (i.e. in case a bridge is needed)
2017 */
2018 private class ReferenceTranslationContext extends TranslationContext<JCMemberReference> {
2020 final boolean isSuper;
2021 final Symbol bridgeSym;
2022 final Symbol sigPolySym;
2024 ReferenceTranslationContext(JCMemberReference tree) {
2025 super(tree);
2026 this.isSuper = tree.hasKind(ReferenceKind.SUPER);
2027 this.bridgeSym = needsBridge()
2028 ? makePrivateSyntheticMethod(isSuper ? 0 : STATIC,
2029 referenceBridgeName(), null,
2030 owner.enclClass())
2031 : null;
2032 this.sigPolySym = isSignaturePolymorphic()
2033 ? makePrivateSyntheticMethod(tree.sym.flags(),
2034 tree.sym.name,
2035 bridgedRefSig(),
2036 tree.sym.enclClass())
2037 : null;
2038 if (dumpLambdaToMethodStats) {
2039 String key = bridgeSym == null ?
2040 "mref.stat" : "mref.stat.1";
2041 log.note(tree, key, needsAltMetafactory(), bridgeSym);
2042 }
2043 }
2045 /**
2046 * Get the opcode associated with this method reference
2047 */
2048 int referenceKind() {
2049 return LambdaToMethod.this.referenceKind(needsBridge()
2050 ? bridgeSym
2051 : tree.sym);
2052 }
2054 boolean needsVarArgsConversion() {
2055 return tree.varargsElement != null;
2056 }
2058 /**
2059 * Generate a disambiguating string to increase stability (important
2060 * if serialized)
2061 *
2062 * @return String to differentiate synthetic lambda method names
2063 */
2064 private String referenceBridgeDisambiguation() {
2065 StringBuilder buf = new StringBuilder();
2066 // Append the enclosing method signature to differentiate
2067 // overloaded enclosing methods.
2068 if (owner.type != null) {
2069 buf.append(typeSig(owner.type));
2070 buf.append(":");
2071 }
2073 // Append qualifier type
2074 buf.append(classSig(tree.sym.owner.type));
2076 // Note static/instance
2077 buf.append(tree.sym.isStatic()? " S " : " I ");
2079 // Append referenced signature
2080 buf.append(typeSig(tree.sym.erasure(types)));
2082 return buf.toString();
2083 }
2085 /**
2086 * Construct a unique stable name for the method reference bridge
2087 *
2088 * @return Name to use for the synthetic method name
2089 */
2090 private Name referenceBridgeName() {
2091 StringBuilder buf = new StringBuilder();
2092 // Append lambda ID, this is semantically significant
2093 buf.append(names.lambda);
2094 // Note that it is a method reference bridge
2095 buf.append("MR$");
2096 // Append the enclosing method name
2097 buf.append(enclosingMethodName());
2098 buf.append('$');
2099 // Append the referenced method name
2100 buf.append(syntheticMethodNameComponent(tree.sym.name));
2101 buf.append('$');
2102 // Append a hash of the disambiguating string : enclosing method
2103 // signature, etc.
2104 String disam = referenceBridgeDisambiguation();
2105 buf.append(Integer.toHexString(disam.hashCode()));
2106 buf.append('$');
2107 // The above appended name components may not be unique, append
2108 // a count based on the above name components.
2109 buf.append(syntheticMethodNameCounts.getIndex(buf));
2110 String result = buf.toString();
2111 return names.fromString(result);
2112 }
2114 /**
2115 * @return Is this an array operation like clone()
2116 */
2117 boolean isArrayOp() {
2118 return tree.sym.owner == syms.arrayClass;
2119 }
2121 boolean receiverAccessible() {
2122 //hack needed to workaround 292 bug (7087658)
2123 //when 292 issue is fixed we should remove this and change the backend
2124 //code to always generate a method handle to an accessible method
2125 return tree.ownerAccessible;
2126 }
2128 /**
2129 * The VM does not support access across nested classes (8010319).
2130 * Were that ever to change, this should be removed.
2131 */
2132 boolean isPrivateInOtherClass() {
2133 return (tree.sym.flags() & PRIVATE) != 0 &&
2134 !types.isSameType(
2135 types.erasure(tree.sym.enclClass().asType()),
2136 types.erasure(owner.enclClass().asType()));
2137 }
2139 /**
2140 * Signature polymorphic methods need special handling.
2141 * e.g. MethodHandle.invoke() MethodHandle.invokeExact()
2142 */
2143 final boolean isSignaturePolymorphic() {
2144 return tree.sym.kind == MTH &&
2145 types.isSignaturePolymorphic((MethodSymbol)tree.sym);
2146 }
2148 /**
2149 * Does this reference needs a bridge (i.e. var args need to be
2150 * expanded or "super" is used)
2151 */
2152 final boolean needsBridge() {
2153 return isSuper || needsVarArgsConversion() || isArrayOp() ||
2154 isPrivateInOtherClass() ||
2155 !receiverAccessible();
2156 }
2158 Type generatedRefSig() {
2159 return types.erasure(tree.sym.type);
2160 }
2162 Type bridgedRefSig() {
2163 return types.erasure(types.findDescriptorSymbol(tree.targets.head.tsym).type);
2164 }
2165 }
2166 }
2167 // </editor-fold>
2169 /*
2170 * These keys provide mappings for various translated lambda symbols
2171 * and the prevailing order must be maintained.
2172 */
2173 enum LambdaSymbolKind {
2174 PARAM, // original to translated lambda parameters
2175 LOCAL_VAR, // original to translated lambda locals
2176 CAPTURED_VAR, // variables in enclosing scope to translated synthetic parameters
2177 CAPTURED_THIS, // class symbols to translated synthetic parameters (for captured member access)
2178 TYPE_VAR; // original to translated lambda type variables
2179 }
2181 /**
2182 * ****************************************************************
2183 * Signature Generation
2184 * ****************************************************************
2185 */
2187 private String typeSig(Type type) {
2188 L2MSignatureGenerator sg = new L2MSignatureGenerator();
2189 sg.assembleSig(type);
2190 return sg.toString();
2191 }
2193 private String classSig(Type type) {
2194 L2MSignatureGenerator sg = new L2MSignatureGenerator();
2195 sg.assembleClassSig(type);
2196 return sg.toString();
2197 }
2199 /**
2200 * Signature Generation
2201 */
2202 private class L2MSignatureGenerator extends Types.SignatureGenerator {
2204 /**
2205 * An output buffer for type signatures.
2206 */
2207 StringBuilder sb = new StringBuilder();
2209 L2MSignatureGenerator() {
2210 super(types);
2211 }
2213 @Override
2214 protected void append(char ch) {
2215 sb.append(ch);
2216 }
2218 @Override
2219 protected void append(byte[] ba) {
2220 sb.append(new String(ba));
2221 }
2223 @Override
2224 protected void append(Name name) {
2225 sb.append(name.toString());
2226 }
2228 @Override
2229 public String toString() {
2230 return sb.toString();
2231 }
2232 }
2233 }